Skip to content

Commit

Permalink
feat(v-resize): add v-resize directive (vexip-ui#457)
Browse files Browse the repository at this point in the history
* types(v-loading): improve type definition for binding.value

* fix(v-loading): auto create component on updated if not mounted

* feat: create directive

* docs: update
  • Loading branch information
qmhc authored Jan 12, 2024
1 parent 9dfafb6 commit 7a0571e
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 18 deletions.
4 changes: 2 additions & 2 deletions components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { install as installDirectives } from '@/directives'
import { installDirectives } from '@/directives'

import { Affix } from './affix'
import { Alert } from './alert'
Expand Down Expand Up @@ -385,4 +385,4 @@ export * from './viewer'
export * from './virtual-list'
export * from './wheel'

export * from '@/directives/loading'
export * from '@/directives'
50 changes: 39 additions & 11 deletions dev-server/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { resolve } from 'node:path'
import { existsSync, readdirSync, statSync } from 'node:fs'
import { existsSync, statSync } from 'node:fs'
import { readdir, stat } from 'node:fs/promises'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
Expand All @@ -18,15 +19,7 @@ const demos = process.env.DEMOS
const port = parseInt(process.env.PORT || '') || 8008

const componentsDir = resolve(__dirname, '../components')
const components = readdirSync(componentsDir).filter(f => {
const path = resolve(componentsDir, f)

if (!statSync(path).isDirectory()) {
return false
}

return existsSync(`${path}/index.ts`)
})
const directivesDir = resolve(__dirname, '../directives')

const typography = [
'Title',
Expand All @@ -44,7 +37,29 @@ const typography = [
'Strong'
]

export default defineConfig((): UserConfig => {
export default defineConfig(async (): Promise<UserConfig> => {
const components: string[] = []
const directives: string[] = []

const results = await Promise.all([readdir(componentsDir), readdir(directivesDir)])

await Promise.all([
...results[0].map(async f => {
const path = resolve(componentsDir, f)

if ((await stat(path)).isDirectory() && existsSync(`${path}/index.ts`)) {
components.push(f)
}
}),
...results[1].map(async f => {
const path = resolve(directivesDir, f)

if ((await stat(path)).isDirectory() && existsSync(`${path}/index.ts`)) {
directives.push(f)
}
})
])

return {
publicDir: '../docs/public',
define: {
Expand Down Expand Up @@ -96,6 +111,19 @@ export default defineConfig((): UserConfig => {
}
}
}
},
{
type: 'directive',
resolve: name => {
const kebabName = toKebabCase(name)

if (directives.includes(kebabName)) {
return {
name: `v${name}`,
from: `@/directives/${kebabName}/index.ts`
}
}
}
}
],
exclude: ['../components/**']
Expand Down
7 changes: 6 additions & 1 deletion directives/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { vLoading } from './loading'
import { vResize } from './resize'

import type { App } from 'vue'

export function install(app: App) {
export function installDirectives(app: App) {
app.directive('loading', vLoading)
app.directive('resize', vResize)
}

export * from './loading'
export * from './resize'
96 changes: 96 additions & 0 deletions directives/resize/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { useResize } from '@vexip-ui/hooks'
import { noop, throttle } from '@vexip-ui/utils'

import type { DirectiveBinding, ObjectDirective } from 'vue'
import type { ResizeHandler } from '@vexip-ui/hooks'

const { observeResize, unobserveResize } = useResize()

export interface VResizeOptions {
handler: ResizeHandler,
throttle?: boolean | number,
disabled?: boolean
}

interface ResizeRecord {
useThrottle: boolean | number,
observed: boolean
}

function createObserver(
el: HTMLElement & { __resize?: ResizeRecord },
binding: DirectiveBinding<ResizeHandler | VResizeOptions>
) {
const options: VResizeOptions =
typeof binding.value === 'function' ? { handler: binding.value } : { ...binding.value }
const useThrottle = options.throttle || binding.modifiers.throttle

el.__resize = {
useThrottle,
observed: false
}

if (options.disabled) {
unobserveResize(el)
el.__resize.observed = false
return
}

const throttleResize = useThrottle
? throttle(options.handler, typeof useThrottle === 'boolean' ? 16 : useThrottle)
: options.handler

observeResize(el, throttleResize)
el.__resize.observed = true
}

export const vResize: ObjectDirective<
HTMLElement & { __resize?: ResizeRecord },
ResizeHandler | VResizeOptions
> = {
mounted(el, binding) {
createObserver(el, binding)
},
updated(el, binding) {
if (!el.__resize) {
createObserver(el, binding)
return
}

const options: VResizeOptions =
typeof binding.value === 'function' ? { handler: binding.value } : { ...binding.value }
const useThrottle = options.throttle || binding.modifiers.throttle

const getHandler = () =>
useThrottle
? throttle(options.handler, typeof useThrottle === 'boolean' ? 16 : useThrottle)
: options.handler

if (options.disabled) {
if (el.__resize.observed) {
unobserveResize(el)
el.__resize.observed = false
}
} else if (!el.__resize.observed) {
observeResize(el, getHandler())
el.__resize.observed = true
} else {
const prevOptions: VResizeOptions =
typeof binding.oldValue === 'function'
? { handler: binding.oldValue }
: { ...(binding.oldValue || { handler: noop }) }

if (useThrottle !== el.__resize.useThrottle || options.handler !== prevOptions.handler) {
unobserveResize(el)
observeResize(el, getHandler())
}
}
},
beforeUnmount(el) {
if (el.__resize?.observed) {
unobserveResize(el)
}

delete el.__resize
}
}
51 changes: 51 additions & 0 deletions docs/demos/resize-observer/directive/demo.en-US.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div v-resize="handleResize" class="resizable-pane">
<span>
Width: {{ width }}px {{ width === 1000 ? '(Max)' : width === 200 ? '(Min)' : '' }}
</span>
<span>
Height: {{ height }}px {{ height === 600 ? '(Max)' : height === 80 ? '(Min)' : '' }}
</span>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const width = ref(0)
const height = ref(0)
function handleResize(entry: ResizeObserverEntry) {
console.info('toggle resize')
const box = entry.borderBoxSize?.[0]
if (box) {
width.value = box.inlineSize
height.value = box.blockSize
} else {
width.value = entry.contentRect.width
height.value = entry.contentRect.height
}
}
</script>

<style scoped>
.resizable-pane {
display: inline-flex;
flex-direction: column;
width: 300px;
min-width: 200px;
max-width: 1000px;
height: 200px;
min-height: 80px;
max-height: 600px;
padding: 10px;
overflow: auto;
color: #fff;
resize: both;
background-color: var(--vxp-color-primary-base);
border: var(--vxp-border-base);
border-radius: var(--vxp-border-radius-large);
}
</style>
51 changes: 51 additions & 0 deletions docs/demos/resize-observer/directive/demo.zh-CN.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div v-resize="handleResize" class="resizable-pane">
<span>
Width: {{ width }}px {{ width === 1000 ? '(Max)' : width === 200 ? '(Min)' : '' }}
</span>
<span>
Height: {{ height }}px {{ height === 600 ? '(Max)' : height === 80 ? '(Min)' : '' }}
</span>
</div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const width = ref(0)
const height = ref(0)
function handleResize(entry: ResizeObserverEntry) {
console.info('toggle resize')
const box = entry.borderBoxSize?.[0]
if (box) {
width.value = box.inlineSize
height.value = box.blockSize
} else {
width.value = entry.contentRect.width
height.value = entry.contentRect.height
}
}
</script>

<style scoped>
.resizable-pane {
display: inline-flex;
flex-direction: column;
width: 300px;
min-width: 200px;
max-width: 1000px;
height: 200px;
min-height: 80px;
max-height: 600px;
padding: 10px;
overflow: auto;
color: #fff;
resize: both;
background-color: var(--vxp-color-primary-base);
border: var(--vxp-border-base);
border-radius: var(--vxp-border-radius-large);
}
</style>
26 changes: 26 additions & 0 deletions docs/en-US/component/resize-observer.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,38 @@ Try to resize this element.

:::

:::demo resize-observer/directive

### Use Directive

Use the `v-resize` directive to quickly add resize callback to element.

:::

## API

### Preset Types

```ts
type ResizeHandler = (entry: ResizeObserverEntry) => any

interface VResizeOptions {
handler: ResizeHandler,
throttle?: boolean | number,
disabled?: boolean
}
```

### ResizeObserver Props

| Name | Type | Description | Default | Since |
| --------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
| on-resize | `(entry: ResizeObserverEntry) => any` | The callback when resized | `null` | - |
| throttle | `boolean \| number` | Set whether to enable the throttling of the callback, the number of milliseconds of throttling can be customized when passing in a number | `false` | - |
| disabled | `boolean` | Set whether to disable | `false` | `2.2.8` |

### ResizeObserver Directives

| Name | Description | Parameters | Since |
| -------- | -------------------------------------------------------------------------------- | -------------------------------------------- | ------- |
| v-resize | Used to quickly add resize callback to element, support the `.throttle` modifier | `(binding: ResizeHandler \| VResizeOptions)` | `2.3.0` |
2 changes: 1 addition & 1 deletion docs/en-US/component/spin.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ When you want to obtain different delay durations for display and disappearance,

:::demo spin/directive

### Directive
### Use Directive

Use the `v-loading` directive to quickly add a loading state to an element.

Expand Down
26 changes: 26 additions & 0 deletions docs/zh-CN/component/resize-observer.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,38 @@

:::

:::demo resize-observer/directive

### 使用指令

使用 `v-resize` 指令可以快速为元素添加缩放回调。

:::

## API

### 预设类型

```ts
type ResizeHandler = (entry: ResizeObserverEntry) => any

interface VResizeOptions {
handler: ResizeHandler,
throttle?: boolean | number,
disabled?: boolean
}
```

### ResizeObserver 属性

| 名称 | 类型 | 说明 | 默认值 | 始于 |
| --------- | ------------------------------------- | ------------------------------------------------------ | ------- | ------- |
| on-resize | `(entry: ResizeObserverEntry) => any` | 大小变化后的回调 | `null` | - |
| throttle | `boolean \| number` | 设置是否开启回调的节流,传入数字时可以定制节流的毫秒数 | `false` | - |
| disabled | `boolean` | 设置是否禁用 | `false` | `2.2.8` |

### ResizeObserver 指令

| 名称 | 说明 | 参数 | 始于 |
| -------- | --------------------------------------------------- | -------------------------------------------- | ------- |
| v-resize | 用于为元素快速添加缩放回调,支持 `.throttle` 修饰符 | `(binding: ResizeHandler \| VResizeOptions)` | `2.3.0` |
2 changes: 1 addition & 1 deletion docs/zh-CN/component/spin.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

:::demo spin/directive

### 指令加载中
### 使用指令

使用 `v-loading` 指令可以快速为元素添加加载中状态。

Expand Down
Loading

0 comments on commit 7a0571e

Please sign in to comment.