@openreachtech/furo-vue 1.5.0 → 1.7.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/README.ja.md +2 -0
- package/README.md +2 -0
- package/docs/design-tokens.md +419 -0
- package/docs/furo-accordion.md +109 -0
- package/docs/furo-alert-dialog.md +107 -0
- package/docs/furo-attachment.md +133 -0
- package/docs/furo-autocomplete-field.md +719 -0
- package/docs/furo-avatar-group.md +87 -0
- package/docs/furo-avatar.md +101 -0
- package/docs/furo-badge.md +126 -0
- package/docs/furo-breadcrumb.md +89 -0
- package/docs/furo-button.md +263 -0
- package/docs/furo-checkbox.md +384 -0
- package/docs/furo-collapsible.md +104 -0
- package/docs/furo-control-block.md +102 -0
- package/docs/furo-date-picker.md +270 -0
- package/docs/furo-date-time-picker.md +110 -0
- package/docs/furo-dialog.md +88 -0
- package/docs/furo-drawer.md +122 -0
- package/docs/furo-dropdown-menu.md +298 -0
- package/docs/furo-editable-field.md +391 -0
- package/docs/furo-editor.md +304 -0
- package/docs/furo-email-field.md +93 -0
- package/docs/furo-empty-state.md +97 -0
- package/docs/furo-error-state.md +97 -0
- package/docs/furo-file-field.md +163 -0
- package/docs/furo-icon.md +288 -0
- package/docs/furo-number-field.md +397 -0
- package/docs/furo-pagination.md +101 -0
- package/docs/furo-password-field.md +75 -0
- package/docs/furo-popover.md +114 -0
- package/docs/furo-radio.md +125 -0
- package/docs/furo-scroll-area.md +112 -0
- package/docs/furo-search-field.md +94 -0
- package/docs/furo-section-header.md +135 -0
- package/docs/furo-select.md +443 -0
- package/docs/furo-separator.md +87 -0
- package/docs/furo-sidebar.md +178 -0
- package/docs/furo-skeleton.md +92 -0
- package/docs/furo-splitter.md +103 -0
- package/docs/furo-stepper.md +127 -0
- package/docs/furo-switch.md +107 -0
- package/docs/furo-table.md +233 -0
- package/docs/furo-tabs.md +173 -0
- package/docs/furo-text-field.md +249 -0
- package/docs/furo-textarea.md +214 -0
- package/docs/furo-time-field.md +276 -0
- package/docs/furo-toast.md +217 -0
- package/docs/furo-toggle-group.md +123 -0
- package/docs/furo-toggle.md +104 -0
- package/docs/furo-tool-bar.md +88 -0
- package/docs/furo-tooltip.md +107 -0
- package/lib/assets/css/furo/0030.variables-semantic-color.css +15 -1
- package/lib/components/atoms/FuroButton/FuroButtonContext.js +8 -8
- package/lib/components/atoms/FuroCheckbox/FuroCheckboxContext.js +1 -1
- package/lib/components/atoms/FuroIcon/FuroIcon.vue +1 -1
- package/lib/components/atoms/FuroSwitch/FuroSwitchContext.js +1 -1
- package/lib/components/atoms/FuroToggle/FuroToggleContext.js +1 -1
- package/lib/components/molecules/FuroAccordion/FuroAccordionContext.js +1 -1
- package/lib/components/molecules/FuroAttachment/FuroAttachment.vue +1 -1
- package/lib/components/molecules/FuroBreadcrumb/FuroBreadcrumb.vue +1 -1
- package/lib/components/molecules/FuroCollapsible/FuroCollapsibleContext.js +1 -1
- package/lib/components/molecules/FuroControlBlock/FuroControlBlock.vue +2 -2
- package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenu.vue +1 -1
- package/lib/components/molecules/FuroDropdownMenu/FuroDropdownMenuContext.js +1 -2
- package/lib/components/molecules/FuroErrorState/FuroErrorState.vue +1 -1
- package/lib/components/molecules/FuroPopover/FuroPopoverContext.js +1 -1
- package/lib/components/molecules/FuroSearchField/FuroSearchField.vue +1 -1
- package/lib/components/molecules/FuroSelect/FuroSelect.vue +8 -6
- package/lib/components/molecules/FuroSelect/FuroSelectContext.js +35 -0
- package/lib/components/molecules/FuroSelect/FuroSelectOptionText.vue +40 -0
- package/lib/components/molecules/FuroSelect/FuroSelectOptionTextContext.js +275 -0
- package/lib/components/molecules/FuroSplitter/FuroSplitterContext.js +1 -1
- package/lib/components/molecules/FuroToggleGroup/FuroToggleGroupContext.js +1 -1
- package/lib/components/molecules/FuroTooltip/FuroTooltipContext.js +1 -1
- package/lib/components/organisms/FuroSidebar/FuroSidebar.vue +3 -3
- package/lib/components/organisms/FuroTable/FuroTable.vue +1 -1
- package/lib/components/organisms/FuroTable/FuroTableContext.js +3 -3
- package/lib/components/organisms/FuroToast/FuroToastContext.js +6 -2
- package/lib/components/organisms/FuroToast/ToastQueue.js +4 -4
- package/nuxt/index.js +9 -9
- package/package.json +23 -22
- package/public/furo-vue/components.json +1 -1
- package/types/generated/components/atoms/FuroButton/FuroButtonContext.d.ts +4 -4
- package/types/generated/components/molecules/FuroSelect/FuroSelect.vue.d.ts +23 -0
- package/types/generated/components/molecules/FuroSelect/FuroSelectContext.d.ts +14 -0
- package/types/generated/components/molecules/FuroSelect/FuroSelectOptionText.vue.d.ts +50 -0
- package/types/generated/components/molecules/FuroSelect/FuroSelectOptionTextContext.d.ts +141 -0
- package/types/generated/components/organisms/FuroTable/FuroTableContext.d.ts +2 -2
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
--color-foreground-title: var(--palette-gray-900);
|
|
9
9
|
--color-foreground-secondary: var(--palette-gray-700);
|
|
10
10
|
--color-foreground-tertiary: var(--palette-gray-600);
|
|
11
|
+
/* The caption colour for an icon, a caret or a separator. A glyph is held to
|
|
12
|
+
3:1 rather than 4.5:1, so it keeps the step that text may not. See ADR 0043. */
|
|
13
|
+
--color-foreground-glyph: var(--palette-gray-600);
|
|
11
14
|
--color-foreground-light: var(--palette-gray-400);
|
|
12
15
|
--color-foreground-extra-light: var(--palette-gray-300);
|
|
13
16
|
--color-foreground-inverse: var(--palette-gray-050);
|
|
@@ -27,10 +30,15 @@
|
|
|
27
30
|
--color-accent-foreground: var(--palette-gray-800);
|
|
28
31
|
--color-destructive: var(--palette-rose-500);
|
|
29
32
|
--color-destructive-foreground: var(--palette-gray-950);
|
|
33
|
+
/* Text drawn in the danger family. The fill cannot darken to carry it, as
|
|
34
|
+
its label would fall under 4.5:1. See ADR 0043. */
|
|
35
|
+
--color-destructive-text: var(--palette-rose-600);
|
|
30
36
|
--color-border: var(--palette-gray-200);
|
|
31
37
|
--color-input: var(--palette-gray-400);
|
|
32
38
|
--color-ring: var(--palette-blue-500);
|
|
33
|
-
|
|
39
|
+
/* The lightest step clearing 4.5:1 on the page. The step below lands at
|
|
40
|
+
3.68:1. See ADR 0043. */
|
|
41
|
+
--color-link: var(--palette-blue-600);
|
|
34
42
|
|
|
35
43
|
/* Brand: the design's dark neutral family, distinct from the blue action colour */
|
|
36
44
|
--color-brand-surface-subtle: var(--palette-gray-100);
|
|
@@ -65,12 +73,14 @@
|
|
|
65
73
|
design does this deliberately; it is followed rather than normalised. */
|
|
66
74
|
--color-warning: var(--palette-orange-500);
|
|
67
75
|
--color-warning-foreground: var(--palette-gray-950);
|
|
76
|
+
--color-warning-text: var(--palette-orange-700);
|
|
68
77
|
--color-warning-subtle: var(--palette-orange-100);
|
|
69
78
|
/* One step darker than the other three tint labels, because the tint it sits
|
|
70
79
|
on is one step darker. The 700 step lands at 4.52:1 here. */
|
|
71
80
|
--color-warning-subtle-foreground: var(--palette-orange-800);
|
|
72
81
|
--color-info: var(--palette-blue-500);
|
|
73
82
|
--color-info-foreground: var(--palette-gray-950);
|
|
83
|
+
--color-info-text: var(--palette-blue-600);
|
|
74
84
|
--color-info-subtle: var(--palette-blue-050);
|
|
75
85
|
--color-info-subtle-foreground: var(--palette-blue-700);
|
|
76
86
|
/* One step lighter than the role itself, for a border that has to read as
|
|
@@ -102,6 +112,7 @@
|
|
|
102
112
|
--color-foreground-title: var(--palette-white);
|
|
103
113
|
--color-foreground-secondary: var(--palette-gray-200);
|
|
104
114
|
--color-foreground-tertiary: var(--palette-gray-300);
|
|
115
|
+
--color-foreground-glyph: var(--palette-gray-300);
|
|
105
116
|
--color-foreground-light: var(--palette-gray-500);
|
|
106
117
|
--color-foreground-extra-light: var(--palette-gray-600);
|
|
107
118
|
--color-foreground-inverse: var(--palette-gray-900);
|
|
@@ -119,6 +130,7 @@
|
|
|
119
130
|
--color-accent-foreground: var(--palette-gray-050);
|
|
120
131
|
--color-destructive: var(--palette-rose-500);
|
|
121
132
|
--color-destructive-foreground: var(--palette-gray-950);
|
|
133
|
+
--color-destructive-text: var(--palette-rose-500);
|
|
122
134
|
--color-border: var(--palette-gray-800);
|
|
123
135
|
--color-input: var(--palette-gray-600);
|
|
124
136
|
--color-ring: var(--palette-blue-500);
|
|
@@ -155,12 +167,14 @@
|
|
|
155
167
|
--color-success-subtle-foreground: var(--palette-emerald-300);
|
|
156
168
|
--color-warning: var(--palette-orange-400);
|
|
157
169
|
--color-warning-foreground: var(--palette-gray-950);
|
|
170
|
+
--color-warning-text: var(--palette-orange-400);
|
|
158
171
|
/* One step in from 950, mirroring the light block drawing warning one step
|
|
159
172
|
in from 050 while the other three families sit at the extreme. */
|
|
160
173
|
--color-warning-subtle: var(--palette-orange-900);
|
|
161
174
|
--color-warning-subtle-foreground: var(--palette-orange-200);
|
|
162
175
|
--color-info: var(--palette-blue-400);
|
|
163
176
|
--color-info-foreground: var(--palette-gray-950);
|
|
177
|
+
--color-info-text: var(--palette-blue-400);
|
|
164
178
|
--color-info-subtle: var(--palette-blue-950);
|
|
165
179
|
--color-info-subtle-foreground: var(--palette-blue-300);
|
|
166
180
|
/* Keeps the light block's rule, one step lighter than the role itself. */
|
|
@@ -215,22 +215,22 @@ export default class FuroButtonContext extends BaseFuroContext {
|
|
|
215
215
|
* Extract ARIA attributes for the button root.
|
|
216
216
|
*
|
|
217
217
|
* @returns {{
|
|
218
|
-
* 'aria-disabled'
|
|
219
|
-
* 'aria-busy'
|
|
218
|
+
* 'aria-disabled'?: 'true'
|
|
219
|
+
* 'aria-busy'?: 'true'
|
|
220
220
|
* }}
|
|
221
221
|
*/
|
|
222
222
|
extractAriaAttributes () {
|
|
223
223
|
const ariaDisabled = this.isDisabled()
|
|
224
|
-
? 'true'
|
|
225
|
-
:
|
|
224
|
+
? { 'aria-disabled': 'true' }
|
|
225
|
+
: {}
|
|
226
226
|
|
|
227
227
|
const ariaBusy = this.isLoading()
|
|
228
|
-
? 'true'
|
|
229
|
-
:
|
|
228
|
+
? { 'aria-busy': 'true' }
|
|
229
|
+
: {}
|
|
230
230
|
|
|
231
231
|
return {
|
|
232
|
-
|
|
233
|
-
|
|
232
|
+
...ariaDisabled,
|
|
233
|
+
...ariaBusy,
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
@@ -267,7 +267,7 @@ export default class FuroAccordionContext extends BaseFuroContext {
|
|
|
267
267
|
|
|
268
268
|
const { value: attrsValue } = this.attrs ?? {}
|
|
269
269
|
|
|
270
|
-
if (attrsValue !== undefined) {
|
|
270
|
+
if (typeof attrsValue !== 'undefined') {
|
|
271
271
|
return /** @type {FuroAccordionValue} */ (attrsValue)
|
|
272
272
|
}
|
|
273
273
|
|
|
@@ -125,7 +125,7 @@ export default defineComponent({
|
|
|
125
125
|
.unit-form-control-block > .label > .required-mark {
|
|
126
126
|
margin-inline-start: 0.125rem;
|
|
127
127
|
|
|
128
|
-
color: var(--color-destructive);
|
|
128
|
+
color: var(--color-destructive-text);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
.unit-form-control-block.horizontal > .control {
|
|
@@ -138,7 +138,7 @@ export default defineComponent({
|
|
|
138
138
|
|
|
139
139
|
list-style: none;
|
|
140
140
|
|
|
141
|
-
color: var(--color-destructive);
|
|
141
|
+
color: var(--color-destructive-text);
|
|
142
142
|
font: var(--font-body-small);
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -329,7 +329,7 @@ export default defineComponent({
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
.unit-dropdown-menu-content > .menu-option.destructive {
|
|
332
|
-
color: var(--color-destructive);
|
|
332
|
+
color: var(--color-destructive-text);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
.unit-dropdown-menu-content > .menu-option.destructive[data-highlighted]:not([data-disabled]) {
|
|
@@ -170,7 +170,6 @@ export default class FuroDropdownMenuContext extends BaseFuroContext {
|
|
|
170
170
|
*/
|
|
171
171
|
get parcel () {
|
|
172
172
|
return {
|
|
173
|
-
open: undefined,
|
|
174
173
|
disabled: false,
|
|
175
174
|
side: 'bottom',
|
|
176
175
|
align: 'start',
|
|
@@ -199,7 +198,7 @@ export default class FuroDropdownMenuContext extends BaseFuroContext {
|
|
|
199
198
|
* @returns {boolean}
|
|
200
199
|
*/
|
|
201
200
|
isOpenControlled () {
|
|
202
|
-
return this.parcel?.open !== undefined
|
|
201
|
+
return typeof this.parcel?.open !== 'undefined'
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
/**
|
|
@@ -125,7 +125,7 @@ export default class FuroPopoverContext extends BaseFuroContext {
|
|
|
125
125
|
* @returns {boolean} true when `parcel.open` is supplied.
|
|
126
126
|
*/
|
|
127
127
|
isOpenControlled () {
|
|
128
|
-
return this.parcel?.open !== undefined
|
|
128
|
+
return typeof this.parcel?.open !== 'undefined'
|
|
129
129
|
&& this.parcel?.open !== null
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -9,12 +9,14 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import FuroIcon from '../../atoms/FuroIcon/FuroIcon.vue'
|
|
11
11
|
import FuroSelectContext from './FuroSelectContext.js'
|
|
12
|
+
import FuroSelectOptionText from './FuroSelectOptionText.vue'
|
|
12
13
|
|
|
13
14
|
export default defineComponent({
|
|
14
15
|
name: 'FuroSelect',
|
|
15
16
|
|
|
16
17
|
components: {
|
|
17
18
|
FuroIcon,
|
|
19
|
+
FuroSelectOptionText,
|
|
18
20
|
SelectRoot: RekaSelect['Root'],
|
|
19
21
|
SelectTrigger: RekaSelect['Trigger'],
|
|
20
22
|
SelectValue: RekaSelect['Value'],
|
|
@@ -168,7 +170,7 @@ export default defineComponent({
|
|
|
168
170
|
<SelectViewport class="viewport">
|
|
169
171
|
<template
|
|
170
172
|
v-for="(option, index) in context.parcel?.options"
|
|
171
|
-
:key="index"
|
|
173
|
+
:key="context.generateOptionKey({ option, index })"
|
|
172
174
|
>
|
|
173
175
|
<template v-if="context.shouldShowGroupOption({ option })">
|
|
174
176
|
<SelectGroup
|
|
@@ -192,7 +194,7 @@ export default defineComponent({
|
|
|
192
194
|
|
|
193
195
|
<template
|
|
194
196
|
v-for="(groupChildOption, groupChildIndex) in context.extractGroupOptions({ option })"
|
|
195
|
-
:key="groupChildIndex"
|
|
197
|
+
:key="context.generateOptionKey({ option: groupChildOption, index: groupChildIndex })"
|
|
196
198
|
>
|
|
197
199
|
<SelectOption
|
|
198
200
|
class="option"
|
|
@@ -206,7 +208,7 @@ export default defineComponent({
|
|
|
206
208
|
:option="groupChildOption"
|
|
207
209
|
:option-index="groupChildIndex"
|
|
208
210
|
>
|
|
209
|
-
<
|
|
211
|
+
<FuroSelectOptionText>
|
|
210
212
|
<slot
|
|
211
213
|
name="option-text"
|
|
212
214
|
:option="groupChildOption"
|
|
@@ -214,7 +216,7 @@ export default defineComponent({
|
|
|
214
216
|
>
|
|
215
217
|
{{ context.resolveOptionLabel({ option: groupChildOption }) }}
|
|
216
218
|
</slot>
|
|
217
|
-
</
|
|
219
|
+
</FuroSelectOptionText>
|
|
218
220
|
|
|
219
221
|
<SelectOptionIndicator class="select-indicator">
|
|
220
222
|
<slot
|
|
@@ -252,7 +254,7 @@ export default defineComponent({
|
|
|
252
254
|
:option="option"
|
|
253
255
|
:option-index="index"
|
|
254
256
|
>
|
|
255
|
-
<
|
|
257
|
+
<FuroSelectOptionText>
|
|
256
258
|
<slot
|
|
257
259
|
name="option-text"
|
|
258
260
|
:option="option"
|
|
@@ -260,7 +262,7 @@ export default defineComponent({
|
|
|
260
262
|
>
|
|
261
263
|
{{ context.resolveOptionLabel({ option }) }}
|
|
262
264
|
</slot>
|
|
263
|
-
</
|
|
265
|
+
</FuroSelectOptionText>
|
|
264
266
|
|
|
265
267
|
<SelectOptionIndicator class="select-indicator">
|
|
266
268
|
<slot
|
|
@@ -295,6 +295,41 @@ export default class FuroSelectContext extends BaseFuroContext {
|
|
|
295
295
|
?? null
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Build an option's render key from its value and label, so a change to either
|
|
300
|
+
* remounts the item, because the item records both only when it mounts.
|
|
301
|
+
*
|
|
302
|
+
* @param {{
|
|
303
|
+
* option: FuroSelectOption
|
|
304
|
+
* index: number
|
|
305
|
+
* }} params - Parameters of this method.
|
|
306
|
+
* @returns {string} Render key.
|
|
307
|
+
*/
|
|
308
|
+
generateOptionKey ({
|
|
309
|
+
option,
|
|
310
|
+
index,
|
|
311
|
+
}) {
|
|
312
|
+
if (this.shouldShowGroupOption({ option })) {
|
|
313
|
+
return `group-${index}`
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return JSON.stringify(
|
|
317
|
+
[
|
|
318
|
+
this.resolveOptionValue({
|
|
319
|
+
option,
|
|
320
|
+
}),
|
|
321
|
+
this.resolveOptionLabel({
|
|
322
|
+
option,
|
|
323
|
+
}),
|
|
324
|
+
],
|
|
325
|
+
(key, value) => (
|
|
326
|
+
typeof value === 'bigint'
|
|
327
|
+
? `${value}n`
|
|
328
|
+
: value
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
}
|
|
332
|
+
|
|
298
333
|
/**
|
|
299
334
|
* Setup component context.
|
|
300
335
|
*
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import {
|
|
3
|
+
defineComponent,
|
|
4
|
+
} from 'vue'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Select as RekaSelect,
|
|
8
|
+
} from 'reka-ui/namespaced'
|
|
9
|
+
|
|
10
|
+
import FuroSelectOptionTextContext from './FuroSelectOptionTextContext.js'
|
|
11
|
+
|
|
12
|
+
export default defineComponent({
|
|
13
|
+
name: 'FuroSelectOptionText',
|
|
14
|
+
|
|
15
|
+
components: {
|
|
16
|
+
SelectOptionText: RekaSelect['ItemText'],
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
setup (
|
|
20
|
+
props,
|
|
21
|
+
componentContext
|
|
22
|
+
) {
|
|
23
|
+
const context = FuroSelectOptionTextContext.create({
|
|
24
|
+
props,
|
|
25
|
+
componentContext,
|
|
26
|
+
})
|
|
27
|
+
.setupComponent()
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
context,
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<SelectOptionText :ref="context.textComponentRef">
|
|
38
|
+
<slot />
|
|
39
|
+
</SelectOptionText>
|
|
40
|
+
</template>
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import {
|
|
2
|
+
onBeforeUnmount,
|
|
3
|
+
onMounted,
|
|
4
|
+
ref,
|
|
5
|
+
shallowRef,
|
|
6
|
+
} from 'vue'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
injectSelectItemContext,
|
|
10
|
+
injectSelectRootContext,
|
|
11
|
+
} from 'reka-ui'
|
|
12
|
+
|
|
13
|
+
import BaseFuroContext from '../../../contexts/BaseFuroContext.js'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Context class for the text of one FuroSelect option.
|
|
17
|
+
*
|
|
18
|
+
* @extends {BaseFuroContext<null, {}, never>}
|
|
19
|
+
*/
|
|
20
|
+
export default class FuroSelectOptionTextContext extends BaseFuroContext {
|
|
21
|
+
/**
|
|
22
|
+
* Constructor.
|
|
23
|
+
*
|
|
24
|
+
* @param {FuroSelectOptionTextContextParams} params - Parameters of this constructor.
|
|
25
|
+
*/
|
|
26
|
+
constructor ({
|
|
27
|
+
props,
|
|
28
|
+
componentContext,
|
|
29
|
+
refHash,
|
|
30
|
+
selectRootContext,
|
|
31
|
+
selectItemContext,
|
|
32
|
+
}) {
|
|
33
|
+
super({
|
|
34
|
+
props,
|
|
35
|
+
componentContext,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
this.refHash = refHash
|
|
39
|
+
this.selectRootContext = selectRootContext
|
|
40
|
+
this.selectItemContext = selectItemContext
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Factory method.
|
|
45
|
+
*
|
|
46
|
+
* @template {X extends typeof FuroSelectOptionTextContext ? X : never} T, X
|
|
47
|
+
* @override
|
|
48
|
+
* @param {FuroSelectOptionTextContextFactoryParams} params - Parameters of this factory method.
|
|
49
|
+
* @returns {InstanceType<T>} An instance of this class.
|
|
50
|
+
* @this {T}
|
|
51
|
+
*/
|
|
52
|
+
static create ({
|
|
53
|
+
props,
|
|
54
|
+
componentContext,
|
|
55
|
+
|
|
56
|
+
refHash = this.buildRefHash(),
|
|
57
|
+
selectRootContext = this.injectSelectRootContext(),
|
|
58
|
+
selectItemContext = this.injectSelectItemContext(),
|
|
59
|
+
}) {
|
|
60
|
+
return /** @type {InstanceType<T>} */ (
|
|
61
|
+
new this({
|
|
62
|
+
props,
|
|
63
|
+
componentContext,
|
|
64
|
+
|
|
65
|
+
refHash,
|
|
66
|
+
selectRootContext,
|
|
67
|
+
selectItemContext,
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Build ref hash.
|
|
74
|
+
*
|
|
75
|
+
* @override
|
|
76
|
+
* @returns {FuroSelectOptionTextContextParams['refHash']}
|
|
77
|
+
*/
|
|
78
|
+
static buildRefHash () {
|
|
79
|
+
return {
|
|
80
|
+
textComponentRef: shallowRef(null),
|
|
81
|
+
observerRef: shallowRef(null),
|
|
82
|
+
recordedTextRef: ref(null),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Inject the select root context, which holds the text the trigger shows.
|
|
88
|
+
*
|
|
89
|
+
* @returns {FuroSelectRootContext}
|
|
90
|
+
*/
|
|
91
|
+
static injectSelectRootContext () {
|
|
92
|
+
return injectSelectRootContext()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Inject the context of the option that holds this text.
|
|
97
|
+
*
|
|
98
|
+
* @returns {FuroSelectItemContext}
|
|
99
|
+
*/
|
|
100
|
+
static injectSelectItemContext () {
|
|
101
|
+
return injectSelectItemContext()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* get: Ctor
|
|
106
|
+
*
|
|
107
|
+
* @override
|
|
108
|
+
* @returns {typeof FuroSelectOptionTextContext}
|
|
109
|
+
*/
|
|
110
|
+
get Ctor () {
|
|
111
|
+
return /** @type {typeof FuroSelectOptionTextContext} */ (
|
|
112
|
+
this.constructor
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* get: textComponentRef
|
|
118
|
+
*
|
|
119
|
+
* @returns {import('vue').ShallowRef<{ $el: HTMLElement } | null>}
|
|
120
|
+
*/
|
|
121
|
+
get textComponentRef () {
|
|
122
|
+
return this.refHash.textComponentRef
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* get: observerRef
|
|
127
|
+
*
|
|
128
|
+
* @returns {import('vue').ShallowRef<MutationObserver | null>}
|
|
129
|
+
*/
|
|
130
|
+
get observerRef () {
|
|
131
|
+
return this.refHash.observerRef
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* get: recordedTextRef
|
|
136
|
+
*
|
|
137
|
+
* @returns {import('vue').Ref<string | null>}
|
|
138
|
+
*/
|
|
139
|
+
get recordedTextRef () {
|
|
140
|
+
return this.refHash.recordedTextRef
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Setup component context.
|
|
145
|
+
*
|
|
146
|
+
* @template {X extends FuroSelectOptionTextContext ? X : never} T, X
|
|
147
|
+
* @override
|
|
148
|
+
* @returns {T} - For method chaining.
|
|
149
|
+
* @this {T}
|
|
150
|
+
*/
|
|
151
|
+
setupComponent () {
|
|
152
|
+
onMounted(() => {
|
|
153
|
+
this.recordedTextRef.value = this.extractText()
|
|
154
|
+
this.observeTextElement()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
onBeforeUnmount(() => {
|
|
158
|
+
this.disconnectObserver()
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
return this
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Extract the rendered text element.
|
|
166
|
+
*
|
|
167
|
+
* @returns {HTMLElement | null}
|
|
168
|
+
*/
|
|
169
|
+
extractTextElement () {
|
|
170
|
+
return this.textComponentRef.value?.$el
|
|
171
|
+
?? null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Extract the rendered text.
|
|
176
|
+
*
|
|
177
|
+
* @returns {string | null}
|
|
178
|
+
*/
|
|
179
|
+
extractText () {
|
|
180
|
+
return this.extractTextElement()?.textContent
|
|
181
|
+
?? null
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Watch the rendered text for changes.
|
|
186
|
+
*/
|
|
187
|
+
observeTextElement () {
|
|
188
|
+
const textElement = this.extractTextElement()
|
|
189
|
+
|
|
190
|
+
if (!textElement) {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const observer = this.Ctor.createMutationObserver({
|
|
195
|
+
handler: () => this.syncText(),
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
observer.observe(textElement, {
|
|
199
|
+
subtree: true,
|
|
200
|
+
childList: true,
|
|
201
|
+
characterData: true,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
this.observerRef.value = observer
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Replace the recorded option text when the rendered text differs from it.
|
|
209
|
+
*/
|
|
210
|
+
syncText () {
|
|
211
|
+
const text = this.extractText()
|
|
212
|
+
|
|
213
|
+
if (text === null) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (text === this.recordedTextRef.value) {
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.recordedTextRef.value = text
|
|
222
|
+
this.selectRootContext.onOptionAdd({
|
|
223
|
+
value: this.selectItemContext.value,
|
|
224
|
+
disabled: this.selectItemContext.disabled.value,
|
|
225
|
+
textContent: text,
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Stop watching the rendered text.
|
|
231
|
+
*/
|
|
232
|
+
disconnectObserver () {
|
|
233
|
+
this.observerRef.value?.disconnect()
|
|
234
|
+
this.observerRef.value = null
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @typedef {{
|
|
240
|
+
* onOptionAdd: (option: {
|
|
241
|
+
* value: unknown
|
|
242
|
+
* disabled: boolean
|
|
243
|
+
* textContent: string
|
|
244
|
+
* }) => void
|
|
245
|
+
* }} FuroSelectRootContext
|
|
246
|
+
*/
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @typedef {{
|
|
250
|
+
* value: unknown
|
|
251
|
+
* disabled: import('vue').Ref<boolean>
|
|
252
|
+
* }} FuroSelectItemContext
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @typedef {import('../../../contexts/BaseFuroContext.js').BaseFuroContextParams & {
|
|
257
|
+
* props: {}
|
|
258
|
+
* componentContext: import('vue').SetupContext
|
|
259
|
+
* refHash: {
|
|
260
|
+
* textComponentRef: import('vue').ShallowRef<{ $el: HTMLElement } | null>
|
|
261
|
+
* observerRef: import('vue').ShallowRef<MutationObserver | null>
|
|
262
|
+
* recordedTextRef: import('vue').Ref<string | null>
|
|
263
|
+
* }
|
|
264
|
+
* selectRootContext: FuroSelectRootContext
|
|
265
|
+
* selectItemContext: FuroSelectItemContext
|
|
266
|
+
* }} FuroSelectOptionTextContextParams
|
|
267
|
+
*/
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @typedef {Omit<FuroSelectOptionTextContextParams, 'refHash' | 'selectRootContext' | 'selectItemContext'> & {
|
|
271
|
+
* refHash?: FuroSelectOptionTextContextParams['refHash']
|
|
272
|
+
* selectRootContext?: FuroSelectRootContext
|
|
273
|
+
* selectItemContext?: FuroSelectItemContext
|
|
274
|
+
* }} FuroSelectOptionTextContextFactoryParams
|
|
275
|
+
*/
|