ropav 0.12.0 → 0.12.2
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/AGENTS.md +150 -0
- package/README.md +5 -1
- package/dist/components/breadcrumbs/breadcrumbs-item.js +2 -0
- package/dist/components/calendar-year-picker/calendar-year-picker-grid.js +1 -0
- package/dist/components/checkbox/checkbox-root.js +2 -0
- package/dist/components/combo-box/combo-box-root.js +1 -0
- package/dist/components/date-input-group/date-input-group-hidden-input.js +2 -1
- package/dist/components/date-picker/date-picker-root.js +1 -0
- package/dist/components/date-range-picker/date-range-picker-root.js +1 -0
- package/dist/components/form/form-root.js +7 -1
- package/dist/components/radio-group/radio-group-root.js +2 -1
- package/dist/components/search-field/search-field-group.js +21 -1
- package/dist/components/select/select-root.js +1 -0
- package/dist/components/splitter/splitter.state.js +6 -1
- package/dist/components/switch/switch-root.js +2 -0
- package/dist/components/table/table-body.js +1 -1
- package/dist/components/table/table-cell.js +3 -1
- package/dist/components/table/table-column-resizer.js +2 -1
- package/dist/components/table/table-load-more.js +1 -1
- package/dist/components/table/use-grid-selection-announcement.js +3 -1
- package/dist/components/table/use-table-collection.js +3 -0
- package/dist/components/tabs/tabs-list-container.js +2 -1
- package/dist/composables/use-calendar-grid.js +2 -1
- package/dist/composables/use-checkbox-group-state.js +2 -0
- package/dist/composables/use-collection.js +12 -0
- package/dist/composables/use-color-field-state.js +1 -0
- package/dist/composables/use-color-field.js +31 -29
- package/dist/composables/use-combo-box-state.d.ts +2 -0
- package/dist/composables/use-combo-box-state.js +5 -2
- package/dist/composables/use-combo-box.js +2 -1
- package/dist/composables/use-date-field-state.js +1 -0
- package/dist/composables/use-date-picker-state.d.ts +2 -0
- package/dist/composables/use-date-picker-state.js +1 -0
- package/dist/composables/use-date-range-picker-state.d.ts +2 -0
- package/dist/composables/use-date-range-picker-state.js +2 -0
- package/dist/composables/use-description.js +1 -0
- package/dist/composables/use-disclosure-panel.js +2 -1
- package/dist/composables/use-drag.js +9 -6
- package/dist/composables/use-drop.js +2 -1
- package/dist/composables/use-droppable-collection.js +2 -1
- package/dist/composables/use-form-validation-state.d.ts +48 -5
- package/dist/composables/use-form-validation-state.js +76 -7
- package/dist/composables/use-input-otp.js +5 -2
- package/dist/composables/use-move.js +2 -1
- package/dist/composables/use-number-field-state.js +1 -0
- package/dist/composables/use-radio-group-state.js +2 -0
- package/dist/composables/use-select-state.d.ts +2 -0
- package/dist/composables/use-select-state.js +2 -0
- package/dist/composables/use-single-select-list-state.js +1 -0
- package/dist/composables/use-text-field.js +1 -0
- package/dist/composables/use-time-field-state.js +2 -1
- package/dist/composables/use-toolbar.js +3 -1
- package/dist/composables/use-virtualizer.js +1 -0
- package/dist/index.js +2 -2
- package/dist/ropav.min.css +1 -1
- package/dist/utils/create-context.js +2 -1
- package/dist/utils/platform.js +2 -1
- package/dist/utils/position.js +4 -2
- package/dist/version.js +1 -1
- package/package.json +4 -7
- package/dist/styles-compat-0.10.css +0 -3
package/AGENTS.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Writing Ropav code
|
|
2
|
+
|
|
3
|
+
Ropav components are compound: a family root provides context, and named parts arrange inside it.
|
|
4
|
+
The arrangement is the API, and it is not guessable from the names. This file is the short list of
|
|
5
|
+
rules that go wrong most often. For a worked arrangement and the props of every family, fetch
|
|
6
|
+
<https://ropav.netlify.app/llms-full.txt> — one file, the whole package.
|
|
7
|
+
|
|
8
|
+
## Importing
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { Button, Select, SelectPopover } from "ropav";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The published package also carries one subpath per family — `import { Button } from "ropav/button"` —
|
|
15
|
+
so a bundler drops what is never imported. The stylesheet is a separate import:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import "ropav/styles/bundled.css";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Every component takes `class`, `aria-label`, `aria-labelledby` and `aria-describedby`. Props are
|
|
22
|
+
camelCase in the types and kebab-case in a template: `itemTextValue` is written `item-text-value`.
|
|
23
|
+
|
|
24
|
+
## The rules
|
|
25
|
+
|
|
26
|
+
**1. A part only works inside its root.** Contexts are strict, so a part rendered outside its
|
|
27
|
+
family throws during setup rather than rendering something degraded. There is no standalone
|
|
28
|
+
`SelectTrigger`.
|
|
29
|
+
|
|
30
|
+
**2. Collections arrive as data, not as children.** `Select` takes a required `items` array — it
|
|
31
|
+
does not read its options by walking children, because a closed select has rendered none.
|
|
32
|
+
`item-text-value` is the string typeahead matches; `item-key` is the identity.
|
|
33
|
+
|
|
34
|
+
```vue
|
|
35
|
+
<Select :item-text-value="(city) => city.name" :items="cities" placeholder="Pick a city">
|
|
36
|
+
<Label>Destination</Label>
|
|
37
|
+
<SelectTrigger>
|
|
38
|
+
<SelectValue />
|
|
39
|
+
<SelectIndicator />
|
|
40
|
+
</SelectTrigger>
|
|
41
|
+
<SelectPopover>
|
|
42
|
+
<ListBox>
|
|
43
|
+
<ListBoxItem v-for="city in cities" :id="city.id" :key="city.id" :text-value="city.name">
|
|
44
|
+
{{ city.name }}
|
|
45
|
+
<ListBoxItemIndicator />
|
|
46
|
+
</ListBoxItem>
|
|
47
|
+
</ListBox>
|
|
48
|
+
</SelectPopover>
|
|
49
|
+
</Select>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**3. Overlays take a positional trigger.** `Modal`, `Popover`, `Dropdown`, `Drawer` and
|
|
53
|
+
`AlertDialog` make their *first child* the trigger. There is no trigger slot.
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<Modal>
|
|
57
|
+
<Button>Open</Button>
|
|
58
|
+
<ModalBackdrop>
|
|
59
|
+
<ModalContainer>
|
|
60
|
+
<ModalDialog>
|
|
61
|
+
<ModalHeader>
|
|
62
|
+
<ModalHeading>Delete workspace</ModalHeading>
|
|
63
|
+
</ModalHeader>
|
|
64
|
+
<ModalBody>This cannot be undone.</ModalBody>
|
|
65
|
+
</ModalDialog>
|
|
66
|
+
</ModalContainer>
|
|
67
|
+
</ModalBackdrop>
|
|
68
|
+
</Modal>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**4. The part holding the state is not always the root.** `Table` takes `class` and `variant` and
|
|
72
|
+
nothing else; `TableContent` owns `aria-label`, `selection-mode`, `v-model:selected-keys` and
|
|
73
|
+
`v-model:sort-descriptor`. Putting them on `Table` compiles and does nothing.
|
|
74
|
+
|
|
75
|
+
```vue
|
|
76
|
+
<Table>
|
|
77
|
+
<TableScrollContainer>
|
|
78
|
+
<TableContent aria-label="Members" selection-mode="multiple">
|
|
79
|
+
<TableHeader>
|
|
80
|
+
<TableColumn id="name" is-row-header>Member</TableColumn>
|
|
81
|
+
</TableHeader>
|
|
82
|
+
<TableBody>
|
|
83
|
+
<TableRow id="1">
|
|
84
|
+
<TableCell>Ada Lovelace</TableCell>
|
|
85
|
+
</TableRow>
|
|
86
|
+
</TableBody>
|
|
87
|
+
</TableContent>
|
|
88
|
+
</TableScrollContainer>
|
|
89
|
+
</Table>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**5. An indicator belongs to its item.** `TabsIndicator` goes inside each `TabsTab`, not beside the
|
|
93
|
+
list. Same for `ListBoxItemIndicator` inside `ListBoxItem` and `MenuItemIndicator` inside
|
|
94
|
+
`MenuItem`.
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<Tabs>
|
|
98
|
+
<TabsListContainer>
|
|
99
|
+
<TabsList aria-label="Views">
|
|
100
|
+
<TabsTab id="all">
|
|
101
|
+
All
|
|
102
|
+
<TabsIndicator />
|
|
103
|
+
</TabsTab>
|
|
104
|
+
</TabsList>
|
|
105
|
+
</TabsListContainer>
|
|
106
|
+
<TabsPanel id="all">Everything.</TabsPanel>
|
|
107
|
+
</Tabs>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**6. Menu parts are shared.** There is no `DropdownItem` export — compose `MenuItem` inside
|
|
111
|
+
`DropdownMenu`. The stories alias the name for brevity, so markup copied from one will not compile.
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<Dropdown>
|
|
115
|
+
<Button>Actions</Button>
|
|
116
|
+
<DropdownPopover>
|
|
117
|
+
<DropdownMenu>
|
|
118
|
+
<MenuItem id="rename" text-value="Rename">
|
|
119
|
+
<Label>Rename</Label>
|
|
120
|
+
</MenuItem>
|
|
121
|
+
</DropdownMenu>
|
|
122
|
+
</DropdownPopover>
|
|
123
|
+
</Dropdown>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**7. `Menu` is the open list; `Dropdown` is that menu in a popover.** A bare `Menu` needs its own
|
|
127
|
+
`Surface` to have a background and a border. A `Dropdown` brings one.
|
|
128
|
+
|
|
129
|
+
**8. Every collection item needs an `id`**, and a `text-value` whenever its content is not plain
|
|
130
|
+
text. `id` is what a selection or an action event reports; `text-value` is what typeahead matches.
|
|
131
|
+
|
|
132
|
+
## Restyling
|
|
133
|
+
|
|
134
|
+
Every component takes a `class`, and every colour, radius and curve is a custom property, so a
|
|
135
|
+
change is a CSS change rather than a fork. State is published on data attributes — `data-hovered`,
|
|
136
|
+
`data-pressed`, `data-selected`, `data-focus-visible` — which is how a single state is retuned
|
|
137
|
+
without a wrapper.
|
|
138
|
+
|
|
139
|
+
```css
|
|
140
|
+
.thing {
|
|
141
|
+
background-color: var(--rp-accent);
|
|
142
|
+
color: var(--rp-accent-foreground);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Where to look next
|
|
147
|
+
|
|
148
|
+
- <https://ropav.netlify.app/llms-full.txt> — every family's arrangement and props, in one file.
|
|
149
|
+
- <https://ropav.netlify.app/llms.txt> — the index, when you want a specific page.
|
|
150
|
+
- <https://ropav.netlify.app/patterns/> — whole screen fragments to copy.
|
package/README.md
CHANGED
|
@@ -5,7 +5,11 @@ A Vue 3 component library built on [Vue Vapor](https://github.com/vuejs/core-vap
|
|
|
5
5
|
[`ropav`](https://www.npmjs.com/package/ropav).
|
|
6
6
|
|
|
7
7
|
**[Documentation](https://ropav.netlify.app)** · [Components](https://ropav.netlify.app/components/)
|
|
8
|
-
· [
|
|
8
|
+
· [Patterns](https://ropav.netlify.app/patterns/) · [Theming](https://ropav.netlify.app/theming/)
|
|
9
|
+
· [Storybook](https://ropav-storybook.netlify.app)
|
|
10
|
+
|
|
11
|
+
Writing this with a coding agent: point it at [`AGENTS.md`](./AGENTS.md), which ships in the
|
|
12
|
+
package, or at [llms-full.txt](https://ropav.netlify.app/llms-full.txt) for every component at once.
|
|
9
13
|
|
|
10
14
|
> Ropav is a port of [HeroUI v3](https://github.com/heroui-inc/heroui) to Vue Vapor: the style layer is
|
|
11
15
|
> vendored from `@heroui/styles@3.2.4`, the behaviour layer is re-implemented from React Aria.
|
|
@@ -54,6 +54,8 @@ var breadcrumbs_item_vue_vue_type_script_setup_true_vapor_true_lang_default = /*
|
|
|
54
54
|
immediate: true
|
|
55
55
|
});
|
|
56
56
|
const isCurrent = computed(() => {
|
|
57
|
+
context.collection.size.value;
|
|
58
|
+
context.orderVersion.value;
|
|
57
59
|
return context.collection.getLastKey() === itemKey.value;
|
|
58
60
|
});
|
|
59
61
|
const isDisabled = computed(() => Boolean(props.isDisabled || context.isDisabled.value || isCurrent.value));
|
|
@@ -55,6 +55,7 @@ var calendar_year_picker_grid_vue_vue_type_script_setup_true_vapor_true_lang_def
|
|
|
55
55
|
const grid = element.value;
|
|
56
56
|
const calendar = calendarElement.value;
|
|
57
57
|
if (!grid || !calendar) return;
|
|
58
|
+
state.focusedDate.value;
|
|
58
59
|
const dayGrid = calendar.querySelector(`[data-slot='${calendarGridSlot}']`);
|
|
59
60
|
if (!dayGrid) return;
|
|
60
61
|
grid.style.top = `${dayGrid.offsetTop}px`;
|
|
@@ -81,7 +81,9 @@ var checkbox_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__
|
|
|
81
81
|
const styles = computed(() => checkboxVariants({ variant: resolvedVariant.value }));
|
|
82
82
|
const ownValidation = useFormValidationState({
|
|
83
83
|
isInvalid: () => props.isInvalid,
|
|
84
|
+
isRequired: resolvedIsRequired,
|
|
84
85
|
name: () => props.name,
|
|
86
|
+
requiredControl: "checkbox",
|
|
85
87
|
validate: () => props.validate,
|
|
86
88
|
validationBehavior: () => props.validationBehavior,
|
|
87
89
|
value: () => isSelected.value
|
|
@@ -115,6 +115,7 @@ var combo_box_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@_
|
|
|
115
115
|
inputValue: () => props.inputValue,
|
|
116
116
|
isInvalid: () => props.isInvalid,
|
|
117
117
|
isReadOnly: () => props.isReadOnly,
|
|
118
|
+
isRequired: () => props.isRequired,
|
|
118
119
|
itemDisabled: props.itemDisabled,
|
|
119
120
|
itemKey: props.itemKey,
|
|
120
121
|
items: () => props.items,
|
|
@@ -60,7 +60,8 @@ var date_input_group_hidden_input_vue_vue_type_script_setup_true_vapor_true_lang
|
|
|
60
60
|
const text = event.target.value;
|
|
61
61
|
if (!text) return;
|
|
62
62
|
const granularity = state.granularity.value;
|
|
63
|
-
const
|
|
63
|
+
const depth = granularity === "day" ? 0 : GRANULARITY_DEPTH[granularity];
|
|
64
|
+
const times = TIME_SEGMENTS.slice(0, depth);
|
|
64
65
|
try {
|
|
65
66
|
const parsed = granularity === "day" ? parseDate(text) : parseDateTime(text);
|
|
66
67
|
for (const segment of DATE_SEGMENTS) if (segment in parsed) state.setSegment(segment, parsed[segment]);
|
|
@@ -99,6 +99,7 @@ var date_picker_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*
|
|
|
99
99
|
isDateUnavailable: props.isDateUnavailable,
|
|
100
100
|
isInvalid: () => props.isInvalid,
|
|
101
101
|
isOpen: () => props.isOpen,
|
|
102
|
+
isRequired: () => props.isRequired,
|
|
102
103
|
maxValue: () => props.maxValue,
|
|
103
104
|
minValue: () => props.minValue,
|
|
104
105
|
name: () => props.name,
|
|
@@ -102,6 +102,7 @@ var date_range_picker_root_vue_vue_type_script_setup_true_vapor_true_lang_defaul
|
|
|
102
102
|
isDateUnavailable: props.isDateUnavailable,
|
|
103
103
|
isInvalid: () => props.isInvalid,
|
|
104
104
|
isOpen: () => props.isOpen,
|
|
105
|
+
isRequired: () => props.isRequired,
|
|
105
106
|
maxValue: () => props.maxValue,
|
|
106
107
|
minValue: () => props.minValue,
|
|
107
108
|
onChange: (value) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { provideFormContext } from "../../composables/use-form-validation-state.js";
|
|
2
|
-
import { computed, createSlot, defineVaporComponent, renderEffect, setClass, setInsertionState, setProp, template } from "vue";
|
|
2
|
+
import { computed, createSlot, defineVaporComponent, on, renderEffect, setClass, setInsertionState, setProp, shallowRef, template } from "vue";
|
|
3
3
|
//#region src/components/form/form-root.vue?vue&type=script&setup=true&vapor=true&lang.ts
|
|
4
4
|
var t0 = template("<form>", 1);
|
|
5
5
|
var form_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PURE__*/ defineVaporComponent({
|
|
@@ -11,7 +11,12 @@ var form_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PURE
|
|
|
11
11
|
},
|
|
12
12
|
setup(__props) {
|
|
13
13
|
const props = __props;
|
|
14
|
+
const submitCount = shallowRef(0);
|
|
15
|
+
const onSubmit = () => {
|
|
16
|
+
submitCount.value += 1;
|
|
17
|
+
};
|
|
14
18
|
provideFormContext({
|
|
19
|
+
submitCount,
|
|
15
20
|
validationBehavior: computed(() => props.validationBehavior ?? "native"),
|
|
16
21
|
validationErrors: computed(() => props.validationErrors ?? {})
|
|
17
22
|
});
|
|
@@ -23,6 +28,7 @@ var form_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PURE
|
|
|
23
28
|
});
|
|
24
29
|
setInsertionState(n1);
|
|
25
30
|
createSlot();
|
|
31
|
+
on(n1, "submit", onSubmit);
|
|
26
32
|
return n1;
|
|
27
33
|
}
|
|
28
34
|
});
|
|
@@ -101,7 +101,8 @@ var radio_group_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*
|
|
|
101
101
|
const onKeydown = (event) => {
|
|
102
102
|
const isHorizontalFlip = locale.value.direction === "rtl" && resolvedOrientation.value !== "vertical";
|
|
103
103
|
const isNext = event.key === "ArrowDown" || event.key === (isHorizontalFlip ? "ArrowLeft" : "ArrowRight");
|
|
104
|
-
|
|
104
|
+
const isPrevious = event.key === "ArrowUp" || event.key === (isHorizontalFlip ? "ArrowRight" : "ArrowLeft");
|
|
105
|
+
if (!isNext && !isPrevious) return;
|
|
105
106
|
if (state.isDisabled.value || state.isReadOnly.value) return;
|
|
106
107
|
const radios = focusableRadios();
|
|
107
108
|
if (radios.length === 0) return;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { dataAttr } from "../../utils/assertion.js";
|
|
2
2
|
import { useFocusWithin, useInteractionStates } from "../../composables/use-interaction-states.js";
|
|
3
|
+
import { isNestedControl } from "../../utils/focus.js";
|
|
3
4
|
import { useTextFieldControlContext } from "../../composables/use-text-field.js";
|
|
4
5
|
import { useSearchFieldContext } from "./search-field.context.js";
|
|
5
|
-
import { computed, createSlot, defineVaporComponent, on, renderEffect, setAttr, setClass, setInsertionState, template, unref } from "vue";
|
|
6
|
+
import { computed, createSlot, defineVaporComponent, on, renderEffect, setAttr, setClass, setInsertionState, setTemplateRefBinding, shallowRef, template, unref } from "vue";
|
|
6
7
|
//#region src/components/search-field/search-field-group.vue?vue&type=script&setup=true&vapor=true&lang.ts
|
|
7
8
|
var t0 = template("<div data-slot=search-field-group role=group>", 1);
|
|
8
9
|
var search_field_group_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PURE__*/ defineVaporComponent({
|
|
@@ -12,11 +13,28 @@ var search_field_group_vue_vue_type_script_setup_true_vapor_true_lang_default =
|
|
|
12
13
|
const props = __props;
|
|
13
14
|
const { slots } = useSearchFieldContext();
|
|
14
15
|
const control = useTextFieldControlContext();
|
|
16
|
+
const element = shallowRef(null);
|
|
17
|
+
const setElement = (next) => {
|
|
18
|
+
element.value = next instanceof HTMLDivElement ? next : null;
|
|
19
|
+
};
|
|
15
20
|
const styles = computed(() => slots.value.group({ class: props.class }));
|
|
16
21
|
const isDisabled = computed(() => control?.isDisabled.value ?? false);
|
|
17
22
|
const isInvalid = computed(() => control?.isInvalid.value ?? false);
|
|
18
23
|
const interaction = useInteractionStates({ isDisabled });
|
|
19
24
|
const focusWithin = useFocusWithin();
|
|
25
|
+
/**
|
|
26
|
+
* The clear button keeps its place in the layout while the field is empty — the stylesheet only
|
|
27
|
+
* fades it out and takes it out of hit-testing — so a click aimed at it lands on the group, and
|
|
28
|
+
* the caret would otherwise stay wherever it was. The search icon is unhittable for the same
|
|
29
|
+
* reason, and so is the padding either side of the control.
|
|
30
|
+
*
|
|
31
|
+
* A click that did reach a control belongs to that control: the browser has already put the caret
|
|
32
|
+
* in the input, and the clear button takes focus back on the way down by itself.
|
|
33
|
+
*/
|
|
34
|
+
const onClick = (event) => {
|
|
35
|
+
if (isNestedControl(event.target)) return;
|
|
36
|
+
element.value?.querySelector("input")?.focus();
|
|
37
|
+
};
|
|
20
38
|
const n1 = t0();
|
|
21
39
|
renderEffect(() => {
|
|
22
40
|
const _dataAttr = unref(dataAttr);
|
|
@@ -36,10 +54,12 @@ var search_field_group_vue_vue_type_script_setup_true_vapor_true_lang_default =
|
|
|
36
54
|
isHovered: () => unref(interaction).isHovered.value,
|
|
37
55
|
isInvalid: () => isInvalid.value
|
|
38
56
|
});
|
|
57
|
+
on(n1, "click", onClick);
|
|
39
58
|
on(n1, "focusin", (e) => unref(focusWithin).onFocusin(e));
|
|
40
59
|
on(n1, "focusout", (e) => unref(focusWithin).onFocusout(e));
|
|
41
60
|
on(n1, "pointerenter", (e) => unref(interaction).onPointerenter(e));
|
|
42
61
|
on(n1, "pointerleave", (e) => unref(interaction).onPointerleave(e));
|
|
62
|
+
setTemplateRefBinding(n1, () => setElement);
|
|
43
63
|
return n1;
|
|
44
64
|
}
|
|
45
65
|
});
|
|
@@ -88,6 +88,7 @@ var select_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PU
|
|
|
88
88
|
disabledKeys: () => props.disabledKeys,
|
|
89
89
|
isInvalid: () => props.isInvalid,
|
|
90
90
|
isOpen: () => props.isOpen,
|
|
91
|
+
isRequired: () => props.isRequired,
|
|
91
92
|
itemDisabled: props.itemDisabled,
|
|
92
93
|
itemKey: props.itemKey,
|
|
93
94
|
items: () => props.items,
|
|
@@ -25,9 +25,11 @@ var registry = () => {
|
|
|
25
25
|
const version = shallowRef(0);
|
|
26
26
|
return {
|
|
27
27
|
get: (key) => {
|
|
28
|
+
version.value;
|
|
28
29
|
return items.get(key);
|
|
29
30
|
},
|
|
30
31
|
keys: computed(() => {
|
|
32
|
+
version.value;
|
|
31
33
|
return sortByDocumentOrder([...items.entries()], ([, meta]) => meta.element()).map(([key]) => key);
|
|
32
34
|
}),
|
|
33
35
|
register: (key, meta) => {
|
|
@@ -83,7 +85,8 @@ var useSplitterState = (options) => {
|
|
|
83
85
|
const indexOf = (key) => panels.keys.value.indexOf(key);
|
|
84
86
|
const getPanelSize = (key) => layout.value[indexOf(key)] ?? 0;
|
|
85
87
|
const boundsOf = (index) => {
|
|
86
|
-
const
|
|
88
|
+
const key = panels.keys.value[index];
|
|
89
|
+
const panel = panels.get(key);
|
|
87
90
|
const available = availableSize.value;
|
|
88
91
|
const min = getMinSize(panel.minSize(), available);
|
|
89
92
|
const shut = panel.isCollapsible() ? getMinSize(panel.collapsedSize() ?? 0, available) : min;
|
|
@@ -206,6 +209,8 @@ var useSplitterState = (options) => {
|
|
|
206
209
|
};
|
|
207
210
|
};
|
|
208
211
|
const neighbourCache = computed(() => {
|
|
212
|
+
panels.keys.value;
|
|
213
|
+
handles.keys.value;
|
|
209
214
|
return /* @__PURE__ */ new Map();
|
|
210
215
|
});
|
|
211
216
|
const neighboursOf = (handleKey) => {
|
|
@@ -50,7 +50,9 @@ var switch_root_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PU
|
|
|
50
50
|
const styles = computed(() => switchVariants({ size: props.size }));
|
|
51
51
|
const validation = useFormValidationState({
|
|
52
52
|
isInvalid: () => props.isInvalid,
|
|
53
|
+
isRequired: () => props.isRequired,
|
|
53
54
|
name: () => props.name,
|
|
55
|
+
requiredControl: "checkbox",
|
|
54
56
|
validate: () => props.validate,
|
|
55
57
|
validationBehavior: () => props.validationBehavior,
|
|
56
58
|
value: () => state.value
|
|
@@ -107,7 +107,7 @@ var table_body_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PUR
|
|
|
107
107
|
return createDynamicComponent(() => unref(virtualizer) ? "div" : "td", {
|
|
108
108
|
"aria-colspan": () => unref(virtualizer) ? columnCount.value : void 0,
|
|
109
109
|
colspan: () => unref(virtualizer) ? void 0 : columnCount.value,
|
|
110
|
-
role: "
|
|
110
|
+
role: "gridcell",
|
|
111
111
|
style: () => emptyStateStyle.value
|
|
112
112
|
}, extend(() => {
|
|
113
113
|
return createSlot("empty", null, null, 4);
|
|
@@ -58,7 +58,8 @@ var table_cell_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PUR
|
|
|
58
58
|
const virtualizer = useTableVirtualizerContext();
|
|
59
59
|
const layoutInfo = computed(() => {
|
|
60
60
|
if (!virtualizer || columnKey.value == null) return null;
|
|
61
|
-
|
|
61
|
+
const collection = virtualizer.collection.value;
|
|
62
|
+
return virtualizer.getLayoutInfo(collection.cellKey(rowKey.value, columnKey.value));
|
|
62
63
|
});
|
|
63
64
|
const parentLayoutInfo = computed(() => virtualizer ? virtualizer.getLayoutInfo(rowKey.value) : null);
|
|
64
65
|
const placement = virtualizer ? useVirtualizerItem({
|
|
@@ -69,6 +70,7 @@ var table_cell_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@__PUR
|
|
|
69
70
|
const n1 = createDynamicComponent(() => unref(virtualizer) ? "div" : "td", {
|
|
70
71
|
id: () => cellId.value,
|
|
71
72
|
"aria-colindex": () => unref(virtualizer) && index.value >= 0 ? index.value + 1 : void 0,
|
|
73
|
+
"aria-label": () => isRowHeader.value ? props.textValue : void 0,
|
|
72
74
|
class: () => ["rp-table__cell", props.class],
|
|
73
75
|
"data-collection": () => unref(collectionId),
|
|
74
76
|
"data-column-index": () => index.value < 0 ? void 0 : index.value,
|
|
@@ -103,7 +103,8 @@ var table_column_resizer_vue_vue_type_script_setup_true_vapor_true_lang_default
|
|
|
103
103
|
* arrow press does.
|
|
104
104
|
*/
|
|
105
105
|
const onChange = (event) => {
|
|
106
|
-
|
|
106
|
+
const next = parseFloat(event.target.value);
|
|
107
|
+
resize(next > columnWidth.value ? columnWidth.value + KEYBOARD_STEP : columnWidth.value - KEYBOARD_STEP);
|
|
107
108
|
};
|
|
108
109
|
const n1 = t0();
|
|
109
110
|
const n0 = child(n1);
|
|
@@ -100,7 +100,7 @@ var table_load_more_vue_vue_type_script_setup_true_vapor_true_lang_default = /*@
|
|
|
100
100
|
return createDynamicComponent(() => unref(virtualizer) ? "div" : "td", {
|
|
101
101
|
"aria-colspan": () => unref(virtualizer) ? columnCount.value : void 0,
|
|
102
102
|
colspan: () => unref(virtualizer) ? void 0 : columnCount.value,
|
|
103
|
-
role: "
|
|
103
|
+
role: "gridcell",
|
|
104
104
|
style: () => indicatorStyle.value
|
|
105
105
|
}, extend(() => {
|
|
106
106
|
return createSlot("default", null, null, 4);
|
|
@@ -41,7 +41,9 @@ var useGridSelectionAnnouncement = (options) => {
|
|
|
41
41
|
else if (added.size === 1 && removed.size === 0) single([...added][0], "selected");
|
|
42
42
|
else if (removed.size === 1 && added.size === 0) single([...removed][0], "not selected");
|
|
43
43
|
if (selection.selectionMode.value === "multiple") {
|
|
44
|
-
|
|
44
|
+
const size = current === "all" ? 0 : current.size;
|
|
45
|
+
const lastSize = last === "all" ? 0 : last.size;
|
|
46
|
+
if (messages.length === 0 || current === "all" || size > 1 || last === "all" || lastSize > 1) messages.push(current === "all" ? "All items selected." : countMessage(selection.selectedKeys.value.size));
|
|
45
47
|
}
|
|
46
48
|
if (messages.length > 0) announce(messages.join(" "));
|
|
47
49
|
});
|
|
@@ -19,10 +19,12 @@ var createTableRegistry = () => {
|
|
|
19
19
|
const items = /* @__PURE__ */ new Map();
|
|
20
20
|
const version = shallowRef(0);
|
|
21
21
|
const orderedKeys = computed(() => {
|
|
22
|
+
version.value;
|
|
22
23
|
return [...items.entries()].filter(([, meta]) => meta.element()?.isConnected).sort(([, a], [, b]) => a.element().compareDocumentPosition(b.element()) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1).map(([key]) => key);
|
|
23
24
|
});
|
|
24
25
|
return {
|
|
25
26
|
getItem: (key) => {
|
|
27
|
+
version.value;
|
|
26
28
|
return items.get(key);
|
|
27
29
|
},
|
|
28
30
|
indexOf: (key) => orderedKeys.value.indexOf(key),
|
|
@@ -39,6 +41,7 @@ var createTableRegistry = () => {
|
|
|
39
41
|
};
|
|
40
42
|
},
|
|
41
43
|
size: computed(() => {
|
|
44
|
+
version.value;
|
|
42
45
|
return orderedKeys.value.length;
|
|
43
46
|
})
|
|
44
47
|
};
|
|
@@ -26,7 +26,8 @@ var tabs_list_container_vue_vue_type_script_setup_true_vapor_true_lang_default =
|
|
|
26
26
|
if (!element) return;
|
|
27
27
|
const vertical = isVertical.value;
|
|
28
28
|
const size = vertical ? element.clientHeight : element.clientWidth;
|
|
29
|
-
const
|
|
29
|
+
const scrollSize = vertical ? element.scrollHeight : element.scrollWidth;
|
|
30
|
+
const maxScroll = Math.max(0, scrollSize - size);
|
|
30
31
|
const isRtl = !vertical && getComputedStyle(element).direction === "rtl";
|
|
31
32
|
const delta = direction * size * SCROLL_FRACTION * (isRtl ? -1 : 1);
|
|
32
33
|
const current = vertical ? element.scrollTop : element.scrollLeft;
|
|
@@ -92,7 +92,8 @@ var useCalendarGrid = (options, state, shared) => {
|
|
|
92
92
|
const isDayView = Boolean(duration.days && duration.days < 7);
|
|
93
93
|
const firstDayOfWeek = toValue(options.firstDayOfWeek);
|
|
94
94
|
const weekStart = isDayView ? startDate.value : startOfWeek(today(state.timeZone.value), locale.value.locale, firstDayOfWeek);
|
|
95
|
-
|
|
95
|
+
const days = isDayView ? duration.days : 7;
|
|
96
|
+
return Array.from({ length: days }, (_, index) => dayFormatter.value.format(weekStart.add({ days: index }).toDate(state.timeZone.value)));
|
|
96
97
|
});
|
|
97
98
|
return {
|
|
98
99
|
attrs: computed(() => ({
|
|
@@ -39,7 +39,9 @@ var useCheckboxGroupState = (options = {}) => {
|
|
|
39
39
|
const isItemRequired = computed(() => isRequired.value && value.value.length === 0);
|
|
40
40
|
const validation = useFormValidationState({
|
|
41
41
|
isInvalid: () => toValue(options.isInvalid),
|
|
42
|
+
isRequired,
|
|
42
43
|
name: () => toValue(options.name),
|
|
44
|
+
requiredControl: "checkbox",
|
|
43
45
|
validate: () => toValue(options.validate),
|
|
44
46
|
validationBehavior: () => toValue(options.validationBehavior),
|
|
45
47
|
value: () => value.value
|
|
@@ -123,13 +123,24 @@ var useCollection = (options = {}) => {
|
|
|
123
123
|
if (index === -1) return null;
|
|
124
124
|
return keys[index + step] ?? null;
|
|
125
125
|
};
|
|
126
|
+
/**
|
|
127
|
+
* Read the registration counter, so a lookup made inside a `computed` re-runs once the item
|
|
128
|
+
* it asked about registers. `getItem` and `getElement` are read during render — an item's
|
|
129
|
+
* own disabled state and tabindex come from them — unlike `orderedKeys()`, which is only ever
|
|
130
|
+
* read on an interaction and so deliberately stays outside reactivity.
|
|
131
|
+
*/
|
|
132
|
+
const track = () => {
|
|
133
|
+
version.value;
|
|
134
|
+
};
|
|
126
135
|
return {
|
|
127
136
|
getElement: (key) => {
|
|
137
|
+
track();
|
|
128
138
|
return items.get(key)?.element() ?? null;
|
|
129
139
|
},
|
|
130
140
|
getFirstKey: () => keyAt(0),
|
|
131
141
|
getIndex: (key) => order().indexOf(key),
|
|
132
142
|
getItem: (key) => {
|
|
143
|
+
track();
|
|
133
144
|
return items.get(key) ?? metaFromSource(key);
|
|
134
145
|
},
|
|
135
146
|
getKeyAfter: (key) => neighbour(key, 1),
|
|
@@ -138,6 +149,7 @@ var useCollection = (options = {}) => {
|
|
|
138
149
|
orderedKeys,
|
|
139
150
|
register,
|
|
140
151
|
size: computed(() => {
|
|
152
|
+
track();
|
|
141
153
|
return source()?.itemCount ?? items.size;
|
|
142
154
|
}),
|
|
143
155
|
withOrder
|
|
@@ -68,6 +68,7 @@ var useColorFieldState = (options = {}) => {
|
|
|
68
68
|
const inputValue = shallowRef(hasInitialValue && colorValue.value ? colorValue.value.toString("hex") : "");
|
|
69
69
|
const validation = useFormValidationState({
|
|
70
70
|
isInvalid: () => toValue(options.isInvalid),
|
|
71
|
+
isRequired: () => toValue(options.isRequired),
|
|
71
72
|
name: () => toValue(options.name),
|
|
72
73
|
validate: () => toValue(options.validate),
|
|
73
74
|
validationBehavior: () => toValue(options.validationBehavior),
|
|
@@ -108,38 +108,40 @@ var useColorField = (options = {}) => {
|
|
|
108
108
|
else if (deltaY < 0) state.decrement();
|
|
109
109
|
}
|
|
110
110
|
});
|
|
111
|
+
const attrs = computed(() => {
|
|
112
|
+
const all = {
|
|
113
|
+
...field.attrs.value,
|
|
114
|
+
"aria-disabled": isDisabled.value || void 0,
|
|
115
|
+
"aria-readonly": isReadOnly.value || void 0
|
|
116
|
+
};
|
|
117
|
+
for (const key of Object.keys(all)) if (all[key] === void 0) delete all[key];
|
|
118
|
+
return all;
|
|
119
|
+
});
|
|
120
|
+
const handlers = {
|
|
121
|
+
onBlur: (event) => {
|
|
122
|
+
isFocused.value = false;
|
|
123
|
+
spin.onBlur();
|
|
124
|
+
state.commit();
|
|
125
|
+
reassert();
|
|
126
|
+
field.handlers.onBlur(event);
|
|
127
|
+
},
|
|
128
|
+
onFocus: (event) => {
|
|
129
|
+
isFocused.value = true;
|
|
130
|
+
spin.onFocus();
|
|
131
|
+
field.handlers.onFocus(event);
|
|
132
|
+
},
|
|
133
|
+
onInput: field.handlers.onInput,
|
|
134
|
+
onKeydown: (event) => {
|
|
135
|
+
spin.onKeydown(event);
|
|
136
|
+
field.handlers.onKeydown(event);
|
|
137
|
+
},
|
|
138
|
+
onKeyup: field.handlers.onKeyup
|
|
139
|
+
};
|
|
111
140
|
return {
|
|
112
|
-
attrs
|
|
113
|
-
const all = {
|
|
114
|
-
...field.attrs.value,
|
|
115
|
-
"aria-disabled": isDisabled.value || void 0,
|
|
116
|
-
"aria-readonly": isReadOnly.value || void 0
|
|
117
|
-
};
|
|
118
|
-
for (const key of Object.keys(all)) if (all[key] === void 0) delete all[key];
|
|
119
|
-
return all;
|
|
120
|
-
}),
|
|
141
|
+
attrs,
|
|
121
142
|
element,
|
|
122
143
|
fieldIds: field.fieldIds,
|
|
123
|
-
handlers
|
|
124
|
-
onBlur: (event) => {
|
|
125
|
-
isFocused.value = false;
|
|
126
|
-
spin.onBlur();
|
|
127
|
-
state.commit();
|
|
128
|
-
reassert();
|
|
129
|
-
field.handlers.onBlur(event);
|
|
130
|
-
},
|
|
131
|
-
onFocus: (event) => {
|
|
132
|
-
isFocused.value = true;
|
|
133
|
-
spin.onFocus();
|
|
134
|
-
field.handlers.onFocus(event);
|
|
135
|
-
},
|
|
136
|
-
onInput: field.handlers.onInput,
|
|
137
|
-
onKeydown: (event) => {
|
|
138
|
-
spin.onKeydown(event);
|
|
139
|
-
field.handlers.onKeydown(event);
|
|
140
|
-
},
|
|
141
|
-
onKeyup: field.handlers.onKeyup
|
|
142
|
-
},
|
|
144
|
+
handlers,
|
|
143
145
|
isDisabled,
|
|
144
146
|
isInvalid: field.isInvalid,
|
|
145
147
|
isReadOnly,
|
|
@@ -78,6 +78,8 @@ export interface UseComboBoxStateOptions<T> {
|
|
|
78
78
|
disabledBehavior?: MaybeRefOrGetter<DisabledBehavior | undefined>;
|
|
79
79
|
isReadOnly?: MaybeRefOrGetter<boolean | undefined>;
|
|
80
80
|
isInvalid?: MaybeRefOrGetter<boolean | undefined>;
|
|
81
|
+
/** Whether the field has to hold a value. Enforced by the validation state under `"aria"`. */
|
|
82
|
+
isRequired?: MaybeRefOrGetter<boolean | undefined>;
|
|
81
83
|
validate?: MaybeRefOrGetter<ValidationFunction<ComboBoxValidationValue> | undefined>;
|
|
82
84
|
validationBehavior?: MaybeRefOrGetter<ValidationBehavior | undefined>;
|
|
83
85
|
name?: MaybeRefOrGetter<string | undefined>;
|
|
@@ -2,7 +2,7 @@ import { useControllableState } from "./use-controllable-state.js";
|
|
|
2
2
|
import { useMenuTriggerState } from "./use-overlay-trigger-state.js";
|
|
3
3
|
import { useCollection } from "./use-collection.js";
|
|
4
4
|
import { createListCollection } from "../utils/virtualizer-collection.js";
|
|
5
|
-
import { useFormValidationState } from "./use-form-validation-state.js";
|
|
5
|
+
import { isValueMissing, useFormValidationState } from "./use-form-validation-state.js";
|
|
6
6
|
import { useSelectionManager } from "./use-selection-manager.js";
|
|
7
7
|
import { defaultItemTextValue } from "./use-select-state.js";
|
|
8
8
|
import { useFilter } from "./use-filter.js";
|
|
@@ -128,7 +128,8 @@ var useComboBoxState = (options) => {
|
|
|
128
128
|
const given = toValue(options.defaultInputValue);
|
|
129
129
|
if (given != null) return given;
|
|
130
130
|
const value = defaultValue.value;
|
|
131
|
-
|
|
131
|
+
const key = Array.isArray(value) ? value[0] ?? null : value;
|
|
132
|
+
return selectionMode.value === "single" ? textOf(key) : "";
|
|
132
133
|
});
|
|
133
134
|
const input = useControllableState({
|
|
134
135
|
defaultValue: defaultInputValue.value,
|
|
@@ -137,7 +138,9 @@ var useComboBoxState = (options) => {
|
|
|
137
138
|
});
|
|
138
139
|
const inputValue = computed(() => input.state.value);
|
|
139
140
|
const validation = useFormValidationState({
|
|
141
|
+
isEmpty: (current) => !current || isValueMissing(current.value),
|
|
140
142
|
isInvalid: options.isInvalid,
|
|
143
|
+
isRequired: options.isRequired,
|
|
141
144
|
name: options.name,
|
|
142
145
|
validate: options.validate,
|
|
143
146
|
validationBehavior: options.validationBehavior,
|