@indielayer/ui 1.13.2 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/pages/component/accordion/index.vue +1 -1
- package/docs/pages/component/button/index.vue +1 -1
- package/docs/pages/component/checkbox/index.vue +1 -1
- package/docs/pages/component/container/index.vue +1 -1
- package/docs/pages/component/drawer/index.vue +1 -1
- package/docs/pages/component/form/index.vue +1 -1
- package/docs/pages/component/formGroup/index.vue +1 -1
- package/docs/pages/component/icon/index.vue +1 -1
- package/docs/pages/component/notifications/index.vue +1 -1
- package/docs/pages/component/pagination/index.vue +1 -1
- package/docs/pages/component/popover/index.vue +1 -1
- package/docs/pages/component/progress/index.vue +1 -1
- package/docs/pages/component/scroll/index.vue +1 -1
- package/docs/pages/component/skeleton/index.vue +1 -1
- package/docs/pages/component/slider/index.vue +1 -1
- package/docs/pages/component/spacer/index.vue +1 -1
- package/docs/pages/component/spinner/index.vue +1 -1
- package/docs/pages/component/table/index.vue +7 -0
- package/docs/pages/component/table/selectable.vue +67 -0
- package/docs/pages/component/table/usage.vue +1 -4
- package/docs/pages/component/table/virtual.vue +3 -0
- package/docs/pages/component/tag/index.vue +1 -1
- package/docs/pages/component/textarea/index.vue +1 -1
- package/docs/pages/component/toggle/index.vue +1 -1
- package/docs/pages/component/upload/index.vue +1 -1
- package/docs/search/components.json +1 -1
- package/lib/components/button/theme/Button.base.theme.js +21 -21
- package/lib/components/radio/theme/Radio.base.theme.js +24 -24
- package/lib/components/select/Select.vue.js +121 -112
- package/lib/components/table/Table.vue.d.ts +62 -8
- package/lib/components/table/Table.vue.js +273 -219
- package/lib/components/table/TableHeader.vue.js +21 -21
- package/lib/components/table/TableRow.vue.d.ts +4 -0
- package/lib/components/table/TableRow.vue.js +3 -2
- package/lib/components/table/theme/TableRow.base.theme.js +3 -3
- package/lib/composables/useFocusTrap.d.ts +9 -4
- package/lib/composables/useFocusTrap.js +42 -27
- package/lib/index.js +1 -1
- package/lib/index.umd.js +4 -4
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/components/button/theme/Button.base.theme.ts +1 -1
- package/src/components/radio/theme/Radio.base.theme.ts +1 -1
- package/src/components/select/Select.vue +20 -5
- package/src/components/table/Table.vue +112 -15
- package/src/components/table/TableHeader.vue +1 -1
- package/src/components/table/TableRow.vue +1 -0
- package/src/components/table/theme/TableRow.base.theme.ts +2 -2
- package/src/composables/useFocusTrap.ts +73 -42
- package/src/version.ts +1 -1
package/lib/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "1.
|
|
1
|
+
declare const _default: "1.14.0";
|
|
2
2
|
export default _default;
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import type { ButtonTheme } from '../Button.vue'
|
|
|
3
3
|
const theme: ButtonTheme = {
|
|
4
4
|
classes: {
|
|
5
5
|
wrapper({ props, slots, data }) {
|
|
6
|
-
const classes = ['relative transition duration-150 focus:outline-
|
|
6
|
+
const classes = ['relative transition duration-150 focus-visible:outline-secondary-300 outline-transparent outline outline-1 outline-offset-2 inline-flex items-center justify-center font-medium whitespace-nowrap overflow-hidden align-middle active:!shadow-none border appearance-none shrink-0']
|
|
7
7
|
|
|
8
8
|
// radius
|
|
9
9
|
if (!data.isButtonGroup) classes.push(props.rounded ? 'rounded-full' : 'rounded-md')
|
|
@@ -11,7 +11,7 @@ const theme: RadioTheme = {
|
|
|
11
11
|
},
|
|
12
12
|
|
|
13
13
|
circle: ({ props }) => {
|
|
14
|
-
const classes = ['rounded-full flex justify-center items-center shrink-0 border outline-offset-2 outline-slate-300 dark:outline-slate-500 group-focus:outline-1 group-focus:outline']
|
|
14
|
+
const classes = ['rounded-full flex justify-center items-center shrink-0 border outline-offset-2 outline-slate-300 dark:outline-slate-500 group-focus-visible:outline-1 group-focus-visible:outline']
|
|
15
15
|
|
|
16
16
|
if (props.size === 'lg') classes.push('h-5 w-5')
|
|
17
17
|
else if (props.size === 'xl') classes.push('h-6 w-6')
|
|
@@ -216,20 +216,35 @@ function findSelectableIndex(start: number | undefined, direction = 'down') {
|
|
|
216
216
|
start = direction === 'down' ? -1 : 1
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
const totalOptions = internalOptions.value.length
|
|
220
|
+
let checked = 0
|
|
221
|
+
|
|
219
222
|
if (direction === 'down') {
|
|
220
223
|
let next = start + 1
|
|
221
224
|
|
|
222
|
-
if (next >
|
|
225
|
+
if (next > totalOptions - 1) next = 0
|
|
223
226
|
while (internalOptions.value[next].disabled) {
|
|
224
|
-
if (++next >
|
|
227
|
+
if (++next > totalOptions - 1) next = 0
|
|
228
|
+
if (++checked >= totalOptions) {
|
|
229
|
+
// All options are disabled, break to avoid infinite loop
|
|
230
|
+
selectedIndex.value = undefined
|
|
231
|
+
|
|
232
|
+
return
|
|
233
|
+
}
|
|
225
234
|
}
|
|
226
235
|
selectedIndex.value = next
|
|
227
236
|
} else {
|
|
228
237
|
let next = start - 1
|
|
229
238
|
|
|
230
|
-
if (next < 0) next =
|
|
239
|
+
if (next < 0) next = totalOptions - 1
|
|
231
240
|
while (internalOptions.value[next].disabled) {
|
|
232
|
-
if (--next < 0) next =
|
|
241
|
+
if (--next < 0) next = totalOptions - 1
|
|
242
|
+
if (++checked >= totalOptions) {
|
|
243
|
+
// All options are disabled, break to avoid infinite loop
|
|
244
|
+
selectedIndex.value = undefined
|
|
245
|
+
|
|
246
|
+
return
|
|
247
|
+
}
|
|
233
248
|
}
|
|
234
249
|
selectedIndex.value = next
|
|
235
250
|
}
|
|
@@ -286,7 +301,7 @@ function isEmpty(value: string | number | []) {
|
|
|
286
301
|
function handleRemove(e: Event, value: string) {
|
|
287
302
|
e.stopPropagation()
|
|
288
303
|
|
|
289
|
-
if (isDisabled.value) return
|
|
304
|
+
if (isDisabled.value || !Array.isArray(selected.value)) return
|
|
290
305
|
|
|
291
306
|
// find value in selected and remove it
|
|
292
307
|
const index = selected.value.indexOf(value)
|
|
@@ -44,6 +44,12 @@ const tableProps = {
|
|
|
44
44
|
default: 5,
|
|
45
45
|
},
|
|
46
46
|
keyProp: String,
|
|
47
|
+
selectable: Boolean,
|
|
48
|
+
singleSelect: Boolean,
|
|
49
|
+
autoClearSelected: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: true,
|
|
52
|
+
},
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
export type TableHeader = {
|
|
@@ -92,8 +98,7 @@ const props = defineProps({
|
|
|
92
98
|
},
|
|
93
99
|
})
|
|
94
100
|
|
|
95
|
-
const selected = defineModel<number | string>('selected')
|
|
96
|
-
const hasSelected = computed(() => typeof selected.value !== 'undefined')
|
|
101
|
+
const selected = defineModel<(number | string) | (number | string)[]>('selected')
|
|
97
102
|
|
|
98
103
|
type internalT = T & {
|
|
99
104
|
__expanded?: boolean;
|
|
@@ -122,10 +127,6 @@ const { list, containerProps, wrapperProps } = useVirtualList(
|
|
|
122
127
|
|
|
123
128
|
const internalItems = ref<internalT[]>([])
|
|
124
129
|
|
|
125
|
-
watch(items, (newValue) => {
|
|
126
|
-
if (props.expandable) internalItems.value = clone(newValue as any) as internalT[]
|
|
127
|
-
}, { immediate: true })
|
|
128
|
-
|
|
129
130
|
const emit = defineEmits(['update:sort', 'click-row'])
|
|
130
131
|
|
|
131
132
|
function getSort(headerValue: string | undefined, sort: string[]): TableHeaderSort {
|
|
@@ -179,6 +180,81 @@ function getValue(item: any, path: string | string[] | undefined) {
|
|
|
179
180
|
return result ?? ''
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
const allKeys = computed<(number | string)[]>(() => {
|
|
184
|
+
if (!props.selectable) return []
|
|
185
|
+
|
|
186
|
+
return items.value.map((item, index) => props.keyProp ? (item as Record<string, unknown>)[props.keyProp] : index) as (number | string)[]
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const allRowsSelected = computed(() => {
|
|
190
|
+
if (!props.selectable || props.singleSelect) return false
|
|
191
|
+
|
|
192
|
+
return Array.isArray(selected.value) && selected.value.length > 0 && allKeys.value.length > 0 && selected.value.length === allKeys.value.length
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const someRowsSelected = computed(() => {
|
|
196
|
+
if (!props.selectable || props.singleSelect) return false
|
|
197
|
+
|
|
198
|
+
return Array.isArray(selected.value) && selected.value.length > 0 && allKeys.value.length > 0 && selected.value.length !== allKeys.value.length
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
function isRowSelected(rowKey: any) {
|
|
202
|
+
if (!props.selectable) return false
|
|
203
|
+
if (props.singleSelect) {
|
|
204
|
+
return selected.value === rowKey
|
|
205
|
+
} else {
|
|
206
|
+
return Array.isArray(selected.value) && selected.value.includes(rowKey)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function toggleRowSelection(rowKey: any) {
|
|
211
|
+
if (!props.selectable) return
|
|
212
|
+
if (props.singleSelect) {
|
|
213
|
+
selected.value = selected.value === rowKey ? undefined : rowKey
|
|
214
|
+
} else {
|
|
215
|
+
if (!Array.isArray(selected.value)) selected.value = []
|
|
216
|
+
if (selected.value.includes(rowKey)) {
|
|
217
|
+
selected.value = selected.value.filter((k: any) => k !== rowKey)
|
|
218
|
+
} else {
|
|
219
|
+
selected.value = [...selected.value, rowKey]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function toggleSelectAll() {
|
|
225
|
+
if (!props.selectable || props.singleSelect) return
|
|
226
|
+
|
|
227
|
+
if (allRowsSelected.value || someRowsSelected.value) {
|
|
228
|
+
selected.value = []
|
|
229
|
+
} else {
|
|
230
|
+
selected.value = allKeys.value
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function onTableRowClick(item: any, index: number) {
|
|
235
|
+
if (props.selectable && props.singleSelect) {
|
|
236
|
+
toggleRowSelection(props.keyProp ? (item as Record<string, unknown>)[props.keyProp] : index)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
emit('click-row', item, index)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
watch(items, (newValue) => {
|
|
243
|
+
if (props.expandable) internalItems.value = clone(newValue as any) as internalT[]
|
|
244
|
+
|
|
245
|
+
if (props.selectable && props.autoClearSelected) {
|
|
246
|
+
if (props.singleSelect) {
|
|
247
|
+
if (!allKeys.value.includes(selected.value as any)) {
|
|
248
|
+
selected.value = undefined
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
if (Array.isArray(selected.value)) {
|
|
252
|
+
selected.value = selected.value.filter((k: any) => allKeys.value.includes(k))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}, { immediate: true })
|
|
257
|
+
|
|
182
258
|
const { styles, classes, className } = useTheme('Table', {}, props)
|
|
183
259
|
</script>
|
|
184
260
|
|
|
@@ -192,6 +268,7 @@ const { styles, classes, className } = useTheme('Table', {}, props)
|
|
|
192
268
|
|
|
193
269
|
<div
|
|
194
270
|
v-bind="wrapperProps"
|
|
271
|
+
class="relative"
|
|
195
272
|
:class="{
|
|
196
273
|
'!h-auto': props.loading
|
|
197
274
|
}"
|
|
@@ -201,6 +278,16 @@ const { styles, classes, className } = useTheme('Table', {}, props)
|
|
|
201
278
|
:class="classes.table"
|
|
202
279
|
>
|
|
203
280
|
<x-table-head :sticky-header="stickyHeader">
|
|
281
|
+
<x-table-header v-if="props.selectable && !props.singleSelect" width="48" class="!px-3 !py-2.5">
|
|
282
|
+
<x-checkbox
|
|
283
|
+
:model-value="allRowsSelected || someRowsSelected"
|
|
284
|
+
:indeterminate="someRowsSelected"
|
|
285
|
+
hide-footer
|
|
286
|
+
aria-label="Select all rows"
|
|
287
|
+
skip-form-registry
|
|
288
|
+
@click.prevent="toggleSelectAll"
|
|
289
|
+
/>
|
|
290
|
+
</x-table-header>
|
|
204
291
|
<x-table-header v-if="expandable" width="48" class="!p-0"/>
|
|
205
292
|
<x-table-header
|
|
206
293
|
v-for="(header, index) in headers"
|
|
@@ -259,13 +346,23 @@ const { styles, classes, className } = useTheme('Table', {}, props)
|
|
|
259
346
|
</td>
|
|
260
347
|
</tr>
|
|
261
348
|
</template>
|
|
262
|
-
<template v-for="(item, index) in list" v-else :key="keyProp
|
|
349
|
+
<template v-for="(item, index) in list" v-else :key="keyProp ? (item.data as Record<string, unknown>)[keyProp] : item.index">
|
|
263
350
|
<x-table-row
|
|
264
351
|
:pointer="pointer"
|
|
265
352
|
:striped="striped"
|
|
266
|
-
:selected="
|
|
267
|
-
|
|
353
|
+
:selected="isRowSelected(keyProp ? (item.data as Record<string, unknown>)[keyProp] : item.index)"
|
|
354
|
+
:single-select="singleSelect"
|
|
355
|
+
@click="onTableRowClick(item.data, item.index)"
|
|
268
356
|
>
|
|
357
|
+
<x-table-cell v-if="props.selectable && !singleSelect" width="48">
|
|
358
|
+
<x-checkbox
|
|
359
|
+
:model-value="isRowSelected(keyProp ? (item.data as Record<string, unknown>)[keyProp] : item.index)"
|
|
360
|
+
hide-footer
|
|
361
|
+
:aria-label="`Select row ${index + 1}`"
|
|
362
|
+
skip-form-registry
|
|
363
|
+
@click.prevent="toggleRowSelection(keyProp ? (item.data as Record<string, unknown>)[keyProp] : item.index)"
|
|
364
|
+
/>
|
|
365
|
+
</x-table-cell>
|
|
269
366
|
<x-table-cell v-if="expandable" width="48" class="!p-1">
|
|
270
367
|
<button
|
|
271
368
|
type="button"
|
|
@@ -311,13 +408,13 @@ const { styles, classes, className } = useTheme('Table', {}, props)
|
|
|
311
408
|
</tr>
|
|
312
409
|
</template>
|
|
313
410
|
</x-table-body>
|
|
314
|
-
<div
|
|
315
|
-
v-if="loading"
|
|
316
|
-
:class="classes.loadingWrapper"
|
|
317
|
-
>
|
|
318
|
-
<x-spinner size="lg"/>
|
|
319
|
-
</div>
|
|
320
411
|
</table>
|
|
412
|
+
<div
|
|
413
|
+
v-if="loading"
|
|
414
|
+
:class="classes.loadingWrapper"
|
|
415
|
+
>
|
|
416
|
+
<x-spinner size="lg"/>
|
|
417
|
+
</div>
|
|
321
418
|
</div>
|
|
322
419
|
</div>
|
|
323
420
|
</template>
|
|
@@ -51,7 +51,7 @@ const { styles, classes, className } = useTheme('TableHeader', {}, props)
|
|
|
51
51
|
:class="[
|
|
52
52
|
classes.sortIcon,
|
|
53
53
|
[sort && [1, -1].includes(sort) ? '' : 'invisible group-hover/th:visible'],
|
|
54
|
-
[sort !== -1 && sort !== 1 ? 'text-secondary-400' : 'text-primary-700']
|
|
54
|
+
[sort !== -1 && sort !== 1 ? 'text-secondary-400 dark:text-secondary-500' : 'text-primary-700 dark:text-primary-400']
|
|
55
55
|
]"
|
|
56
56
|
width="24"
|
|
57
57
|
height="24"
|
|
@@ -5,8 +5,8 @@ const theme: TableRowTheme = {
|
|
|
5
5
|
row: ({ props }) => {
|
|
6
6
|
const classes = []
|
|
7
7
|
|
|
8
|
-
if (props.selected) {
|
|
9
|
-
classes.push('shadow-[inset_2px_0] shadow-primary-500')
|
|
8
|
+
if (props.selected && props.singleSelect) {
|
|
9
|
+
classes.push('shadow-[inset_2px_0] shadow-primary-500 !bg-secondary-50 dark:!bg-secondary-600')
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
if (props.striped) {
|
|
@@ -1,83 +1,114 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { onUnmounted, type MaybeRef, unref, nextTick } from 'vue'
|
|
1
|
+
import { onUnmounted, unref, nextTick, watch, ref, type Ref, type ComponentPublicInstance } from 'vue'
|
|
3
2
|
|
|
4
3
|
const focusableQuery = 'button:not([tabindex="-1"]), [href], input, select, textarea, li, a, [tabindex]:not([tabindex="-1"])'
|
|
5
4
|
|
|
6
5
|
export function useFocusTrap() {
|
|
7
|
-
|
|
6
|
+
const focusable = ref<HTMLElement[]>([])
|
|
8
7
|
let observer: MutationObserver | null = null
|
|
9
8
|
|
|
10
9
|
let firstFocusableEl: HTMLElement | null = null
|
|
11
10
|
let lastFocusableEl: HTMLElement | null = null
|
|
11
|
+
let prevActiveElement: HTMLElement | null = null
|
|
12
|
+
let currentTarget: HTMLElement | ComponentPublicInstance | null = null
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
function getEl(target: HTMLElement | ComponentPublicInstance | null): HTMLElement | null {
|
|
15
|
+
if (!target) return null
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
return (target as ComponentPublicInstance).$el
|
|
18
|
+
? (target as ComponentPublicInstance).$el as HTMLElement
|
|
19
|
+
: target as HTMLElement
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
function getFocusableElements(target: HTMLElement | ComponentPublicInstance | null) {
|
|
23
|
+
const el = getEl(target)
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
if (!el) return
|
|
26
|
+
const elements = el.querySelectorAll(focusableQuery)
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
focusable.value = Array.from(elements) as HTMLElement[]
|
|
29
|
+
firstFocusableEl = focusable.value[0] || null
|
|
30
|
+
lastFocusableEl = focusable.value[focusable.value.length - 1] || null
|
|
31
|
+
}
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
const handleKeydown = (event: KeyboardEvent) => {
|
|
34
|
+
if (event.key !== 'Tab' || focusable.value.length === 0) return
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
36
|
+
const isShiftPressed = event.shiftKey
|
|
37
|
+
const currentEl = document.activeElement as HTMLElement | null
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
const firstEl = firstFocusableEl
|
|
40
|
+
const lastEl = lastFocusableEl
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
if (!currentEl) {
|
|
43
|
+
event.preventDefault()
|
|
44
|
+
firstEl?.focus()
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
return
|
|
47
|
+
}
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
if (!isShiftPressed && currentEl === lastEl) {
|
|
50
|
+
event.preventDefault()
|
|
51
|
+
firstEl?.focus()
|
|
52
|
+
} else if (isShiftPressed && currentEl === firstEl) {
|
|
53
|
+
event.preventDefault()
|
|
54
|
+
lastEl?.focus()
|
|
55
|
+
}
|
|
42
56
|
}
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
async function initFocusTrap(
|
|
59
|
+
targetRef: Ref<HTMLElement | ComponentPublicInstance | null> | HTMLElement | ComponentPublicInstance | null,
|
|
60
|
+
options?: { initialFocusIndex?: number; returnFocusOnClear?: boolean; },
|
|
61
|
+
) {
|
|
62
|
+
if (typeof window === 'undefined') return
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
// Clean up previous trap if any
|
|
65
|
+
clearFocusTrap()
|
|
66
|
+
|
|
67
|
+
prevActiveElement = document.activeElement as HTMLElement
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
currentTarget = unref(targetRef)
|
|
70
|
+
if (!currentTarget) return
|
|
55
71
|
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
await nextTick()
|
|
73
|
+
getFocusableElements(currentTarget)
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
firstEl?.focus()
|
|
62
|
-
} else if (isShiftPressed && currentEl === firstEl) {
|
|
63
|
-
event.preventDefault()
|
|
64
|
-
lastEl?.focus()
|
|
65
|
-
}
|
|
75
|
+
// Focus initial element
|
|
76
|
+
const idx = options?.initialFocusIndex ?? 0
|
|
66
77
|
|
|
78
|
+
focusable.value[idx]?.focus()
|
|
79
|
+
|
|
80
|
+
document.addEventListener('keydown', handleKeydown)
|
|
81
|
+
observer = new MutationObserver(() => getFocusableElements(currentTarget))
|
|
82
|
+
const el = getEl(currentTarget)
|
|
83
|
+
|
|
84
|
+
if (el) observer.observe(el, { childList: true, subtree: true })
|
|
85
|
+
|
|
86
|
+
// If targetRef is a Ref, watch for changes
|
|
87
|
+
if (typeof targetRef === 'object' && targetRef !== null && 'value' in targetRef) {
|
|
88
|
+
watch(targetRef, (newVal) => {
|
|
89
|
+
clearFocusTrap()
|
|
90
|
+
if (newVal !== null) initFocusTrap(targetRef, options)
|
|
91
|
+
})
|
|
67
92
|
}
|
|
68
93
|
}
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
function clearFocusTrap(options?: { returnFocus?: boolean; }) {
|
|
71
96
|
document.removeEventListener('keydown', handleKeydown)
|
|
72
97
|
observer?.disconnect()
|
|
98
|
+
observer = null
|
|
99
|
+
if (options?.returnFocus && prevActiveElement) {
|
|
100
|
+
prevActiveElement.focus()
|
|
101
|
+
}
|
|
102
|
+
currentTarget = null
|
|
73
103
|
}
|
|
74
104
|
|
|
75
105
|
onUnmounted(() => {
|
|
76
|
-
clearFocusTrap()
|
|
106
|
+
clearFocusTrap({ returnFocus: true })
|
|
77
107
|
})
|
|
78
108
|
|
|
79
109
|
return {
|
|
80
110
|
initFocusTrap,
|
|
81
111
|
clearFocusTrap,
|
|
112
|
+
focusable, // expose for advanced use
|
|
82
113
|
}
|
|
83
114
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '1.
|
|
1
|
+
export default '1.14.0'
|