orio-ui 1.24.0 → 1.28.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.md +78 -3
- package/bin/orio-ui.mjs +72 -0
- package/dist/agents/ROUTING.md +140 -0
- package/dist/agents/component-finder.md +142 -0
- package/dist/agents/component-worker.md +152 -0
- package/dist/agents/snippet.md +6 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/AnimatedContainer.USAGE.md +79 -0
- package/dist/runtime/components/Badge.USAGE.md +75 -0
- package/dist/runtime/components/Banner.USAGE.md +52 -0
- package/dist/runtime/components/Button.USAGE.md +78 -0
- package/dist/runtime/components/Button.d.vue.ts +3 -2
- package/dist/runtime/components/Button.vue +19 -11
- package/dist/runtime/components/Button.vue.d.ts +3 -2
- package/dist/runtime/components/Calendar.USAGE.md +59 -0
- package/dist/runtime/components/Calendar.vue +254 -87
- package/dist/runtime/components/Canvas/USAGE.md +73 -0
- package/dist/runtime/components/CheckBox.USAGE.md +63 -0
- package/dist/runtime/components/CheckBox.vue +9 -3
- package/dist/runtime/components/CheckboxGroup.USAGE.md +95 -0
- package/dist/runtime/components/CheckboxGroup.vue +7 -1
- package/dist/runtime/components/ControlElement.USAGE.md +77 -0
- package/dist/runtime/components/ControlElement.d.vue.ts +42 -27
- package/dist/runtime/components/ControlElement.vue +28 -9
- package/dist/runtime/components/ControlElement.vue.d.ts +42 -27
- package/dist/runtime/components/DashedContainer.USAGE.md +65 -0
- package/dist/runtime/components/EmptyState.USAGE.md +65 -0
- package/dist/runtime/components/Form.USAGE.md +102 -0
- package/dist/runtime/components/Icon.USAGE.md +61 -0
- package/dist/runtime/components/Input.USAGE.md +57 -0
- package/dist/runtime/components/Input.vue +13 -3
- package/dist/runtime/components/ListItem.USAGE.md +84 -0
- package/dist/runtime/components/LoadingSpinner.USAGE.md +50 -0
- package/dist/runtime/components/LocaleSwitcher.USAGE.md +73 -0
- package/dist/runtime/components/Modal.USAGE.md +72 -0
- package/dist/runtime/components/NavButton.USAGE.md +80 -0
- package/dist/runtime/components/NavButton.d.vue.ts +0 -1
- package/dist/runtime/components/NavButton.vue +9 -5
- package/dist/runtime/components/NavButton.vue.d.ts +0 -1
- package/dist/runtime/components/NumberInput/Horizontal.USAGE.md +61 -0
- package/dist/runtime/components/NumberInput/Horizontal.vue +7 -2
- package/dist/runtime/components/NumberInput/USAGE.md +74 -0
- package/dist/runtime/components/NumberInput/Vertical.USAGE.md +55 -0
- package/dist/runtime/components/NumberInput/Vertical.vue +7 -2
- package/dist/runtime/components/NumberInput/index.d.vue.ts +0 -2
- package/dist/runtime/components/NumberInput/index.vue +9 -7
- package/dist/runtime/components/NumberInput/index.vue.d.ts +0 -2
- package/dist/runtime/components/Popover.USAGE.md +103 -0
- package/dist/runtime/components/RadioButton.USAGE.md +72 -0
- package/dist/runtime/components/RadioButton.d.vue.ts +0 -2
- package/dist/runtime/components/RadioButton.vue +9 -4
- package/dist/runtime/components/RadioButton.vue.d.ts +0 -2
- package/dist/runtime/components/Selector.USAGE.md +131 -0
- package/dist/runtime/components/Selector.d.vue.ts +1 -0
- package/dist/runtime/components/Selector.vue +10 -4
- package/dist/runtime/components/Selector.vue.d.ts +1 -0
- package/dist/runtime/components/SwitchButton.USAGE.md +62 -0
- package/dist/runtime/components/SwitchButton.d.vue.ts +1 -4
- package/dist/runtime/components/SwitchButton.vue +10 -7
- package/dist/runtime/components/SwitchButton.vue.d.ts +1 -4
- package/dist/runtime/components/Tag.USAGE.md +51 -0
- package/dist/runtime/components/TaggableSelector.USAGE.md +73 -0
- package/dist/runtime/components/TaggableSelector.vue +7 -1
- package/dist/runtime/components/Textarea.USAGE.md +72 -0
- package/dist/runtime/components/Textarea.vue +13 -3
- package/dist/runtime/components/Tooltip.USAGE.md +84 -0
- package/dist/runtime/components/ZoomableContainer.USAGE.md +108 -0
- package/dist/runtime/components/date/Picker.USAGE.md +52 -0
- package/dist/runtime/components/date/Picker.vue +7 -1
- package/dist/runtime/components/date/PickerTrigger.USAGE.md +65 -0
- package/dist/runtime/components/date/PickerTrigger.vue +9 -3
- package/dist/runtime/components/date/RangePicker.USAGE.md +97 -0
- package/dist/runtime/components/date/RangePicker.vue +7 -1
- package/dist/runtime/components/gallery/Carousel.USAGE.md +98 -0
- package/dist/runtime/components/gallery/CarouselPreview.USAGE.md +51 -0
- package/dist/runtime/components/upload/USAGE.md +91 -0
- package/dist/runtime/components/view/Dates.USAGE.md +67 -0
- package/dist/runtime/components/view/KeyBinds.USAGE.md +58 -0
- package/dist/runtime/components/view/Separator.USAGE.md +57 -0
- package/dist/runtime/components/view/Text.USAGE.md +68 -0
- package/dist/runtime/composables/useApi.USAGE.md +64 -0
- package/dist/runtime/composables/useControlSize.USAGE.md +73 -0
- package/dist/runtime/composables/useDecimalFormatter.USAGE.md +72 -0
- package/dist/runtime/composables/useFilter.USAGE.md +120 -0
- package/dist/runtime/composables/useFilter.d.ts +91 -0
- package/dist/runtime/composables/useFilter.js +111 -0
- package/dist/runtime/composables/useFuzzySearch.USAGE.md +68 -0
- package/dist/runtime/composables/useInertia.USAGE.md +80 -0
- package/dist/runtime/composables/useListKeyboard.USAGE.md +97 -0
- package/dist/runtime/composables/useModal.USAGE.md +82 -0
- package/dist/runtime/composables/usePinchZoom.USAGE.md +95 -0
- package/dist/runtime/composables/usePressAndHold.USAGE.md +70 -0
- package/dist/runtime/composables/useRovingGrid.USAGE.md +106 -0
- package/dist/runtime/composables/useRovingGrid.d.ts +35 -0
- package/dist/runtime/composables/useRovingGrid.js +115 -0
- package/dist/runtime/composables/useSound.USAGE.md +74 -0
- package/dist/runtime/composables/useTheme.USAGE.md +76 -0
- package/dist/runtime/composables/useUrlSync.USAGE.md +91 -0
- package/dist/runtime/composables/useValidation.USAGE.md +100 -0
- package/dist/runtime/i18n/en.json +4 -1
- package/dist/runtime/i18n/uk.json +4 -1
- package/package.json +12 -2
|
@@ -6,7 +6,13 @@ const props = defineProps({
|
|
|
6
6
|
label: { type: String, required: false },
|
|
7
7
|
layout: { type: String, required: false, default: "vertical" },
|
|
8
8
|
size: { type: String, required: false, default: "md" },
|
|
9
|
-
fill: { type: Boolean, required: false }
|
|
9
|
+
fill: { type: Boolean, required: false },
|
|
10
|
+
tabindex: { type: [Number, String], required: false },
|
|
11
|
+
focusKey: { type: String, required: false },
|
|
12
|
+
disabled: { type: Boolean, required: false },
|
|
13
|
+
required: { type: Boolean, required: false },
|
|
14
|
+
name: { type: String, required: false },
|
|
15
|
+
ariaLabel: { type: String, required: false }
|
|
10
16
|
});
|
|
11
17
|
const modelValue = defineModel({ type: Array, ...{ default: () => [] } });
|
|
12
18
|
function isChecked(value) {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: label + error + a11y wrapper for any form control
|
|
5
|
+
short: label/legend wrapper, owns a11y attrs, exposes the `control` slot prop bag
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ControlElement — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`ControlElement` is the wrapper every form input uses (Input, Textarea,
|
|
12
|
+
NumberInput, Selector, CheckBox, etc.). When you build a new orio form
|
|
13
|
+
component, wrap it in `<orio-control-element>`. When you consume an existing
|
|
14
|
+
one, pass `ControlProps` straight through — they are usually re-exported.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **`inheritAttrs: false`.** Attrs do **not** auto-flow onto the wrapper or
|
|
19
|
+
the inner element. The component exposes a `control` slot prop containing
|
|
20
|
+
the a11y/form attr bag (`id`, `ariaDescribedby`, `ariaInvalid`,
|
|
21
|
+
`ariaRequired`, plus passthrough `tabindex`, `focusKey`, `disabled`,
|
|
22
|
+
`required`, `name`, `ariaLabel`). The inner element **must** spread it:
|
|
23
|
+
```vue
|
|
24
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
25
|
+
<input v-bind="control" />
|
|
26
|
+
</orio-control-element>
|
|
27
|
+
```
|
|
28
|
+
- **`group` prop changes the semantic root.** When `true`, the wrapper gets
|
|
29
|
+
`role="group"` + `aria-labelledby`, and the label renders as `<span>`
|
|
30
|
+
(still id-linked) instead of `<label>`. Use this for `CheckboxGroup`,
|
|
31
|
+
radio groups, anything where the "control" is multiple inputs.
|
|
32
|
+
- **Error wiring is automatic.** Setting `error` to a non-null string:
|
|
33
|
+
- Renders a `.control-error` span below the slot.
|
|
34
|
+
- Sets `aria-invalid` and `aria-describedby` on the inner element via the
|
|
35
|
+
`control` slot prop.
|
|
36
|
+
- Adds a red border to `.slot-wrapper` (unless the wrapper contains a
|
|
37
|
+
`:deep(.error-fields)` element, in which case the inner component owns
|
|
38
|
+
error styling — see TaggableSelector).
|
|
39
|
+
- **`size` is provided to descendants** via `provideControlSize`. Children
|
|
40
|
+
that use `useControlSize()` (e.g. inner buttons, icons) inherit it
|
|
41
|
+
automatically — do not re-pass `size` down manually.
|
|
42
|
+
- **`appearance="minimal"`** zeros margin and strips border + box-shadow from
|
|
43
|
+
the first slot child. Use for inputs embedded in a row with their own
|
|
44
|
+
surrounding chrome.
|
|
45
|
+
|
|
46
|
+
## Gotchas
|
|
47
|
+
|
|
48
|
+
- The slot's `id` matches what the `<label>` points to via `for`. The inner
|
|
49
|
+
element receives the same `id` through the `control` bag. Do **not**
|
|
50
|
+
override it — it is `useId()` by default and accessibility breaks if two
|
|
51
|
+
inputs in the same render share an id.
|
|
52
|
+
- `disabled` is forwarded to the inner element AND drives the wrapper's
|
|
53
|
+
`.disabled` class for styling. Pass `disabled` on the wrapper, never on the
|
|
54
|
+
inner element directly.
|
|
55
|
+
- The exported types are the contract:
|
|
56
|
+
- `ControlProps` — what consumers pass to ControlElement.
|
|
57
|
+
- `ControlPassthroughProps` — the subset that travels to the inner element.
|
|
58
|
+
- `ControlSlotAttrs` — the bag exposed via the `control` slot prop.
|
|
59
|
+
- `ControlLayout = "vertical" | "horizontal"`.
|
|
60
|
+
Components that wrap ControlElement extend `ControlProps`, e.g.
|
|
61
|
+
`interface Props extends ControlProps { ... }`.
|
|
62
|
+
|
|
63
|
+
## Quick reference
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<script setup lang="ts">
|
|
67
|
+
import type { ControlProps } from "./ControlElement.vue";
|
|
68
|
+
interface Props extends ControlProps { /* component-specific props */ }
|
|
69
|
+
const props = defineProps<Props>();
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
74
|
+
<my-inner-element v-bind="control" />
|
|
75
|
+
</orio-control-element>
|
|
76
|
+
</template>
|
|
77
|
+
```
|
|
@@ -1,43 +1,58 @@
|
|
|
1
1
|
export type ControlLayout = "vertical" | "horizontal";
|
|
2
2
|
export type ControlSize = "sm" | "md" | "lg" | "xl";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A11y + form attrs that flow from the caller through ControlElement to the
|
|
5
|
+
* actual interactive element via the `control` slot prop. Never rendered on
|
|
6
|
+
* the wrapper itself.
|
|
7
|
+
*/
|
|
8
|
+
export interface ControlPassthroughProps {
|
|
9
|
+
/** Native `tabindex` for the inner element. */
|
|
10
|
+
tabindex?: number | string;
|
|
11
|
+
/** Roving-focus identifier — see useRovingGrid. Rendered as `focus-key` DOM attr. */
|
|
12
|
+
focusKey?: string;
|
|
13
|
+
/** Disables the inner element AND drives wrapper disabled styling. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Marks the inner element required and sets `aria-required`. */
|
|
16
|
+
required?: boolean;
|
|
17
|
+
/** Native `name` attribute for the inner form element. */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Accessible name for the inner element when no visible label is set. */
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ControlProps extends ControlPassthroughProps {
|
|
23
|
+
/** Minimal resets margin and removes border/box-shadow from every element inside the slot. */
|
|
7
24
|
appearance?: "normal" | "minimal";
|
|
8
|
-
/**
|
|
9
|
-
* Error message to display below the control
|
|
10
|
-
*/
|
|
25
|
+
/** Error message to display below the control. Also drives `aria-invalid` and `aria-describedby` on the inner element. */
|
|
11
26
|
error?: string | null;
|
|
12
|
-
/**
|
|
13
|
-
* Marks this control as a group (adds role="group" and aria-labelledby).
|
|
14
|
-
* The label renders as a <span> instead of <label>.
|
|
15
|
-
* Use for groups of related controls (e.g. CheckboxGroup).
|
|
16
|
-
*/
|
|
27
|
+
/** Marks this control as a group (adds role="group" + aria-labelledby on the wrapper). The label renders as a <span> instead of <label>. Use for groups of related controls (e.g. CheckboxGroup). */
|
|
17
28
|
group?: boolean;
|
|
18
|
-
/**
|
|
19
|
-
* ID for the control's form element, auto-generated if not provided
|
|
20
|
-
*/
|
|
29
|
+
/** ID for the inner form element. Auto-generated if not provided. */
|
|
21
30
|
id?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Label text for the control (or legend text when group is true)
|
|
24
|
-
*/
|
|
31
|
+
/** Label text for the control (or legend text when group is true). */
|
|
25
32
|
label?: string;
|
|
26
|
-
/**
|
|
27
|
-
* Label position relative to the control
|
|
28
|
-
*/
|
|
33
|
+
/** Label position relative to the control. */
|
|
29
34
|
layout?: ControlLayout;
|
|
30
|
-
/**
|
|
31
|
-
* Size of the control and its inner elements
|
|
32
|
-
*/
|
|
35
|
+
/** Size of the control and its inner elements. */
|
|
33
36
|
size?: ControlSize;
|
|
34
|
-
/**
|
|
35
|
-
* Whether element should fill the container
|
|
36
|
-
*/
|
|
37
|
+
/** Whether the element should fill the container. */
|
|
37
38
|
fill?: boolean;
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Bag the consumer spreads onto the inner interactive element via the `control`
|
|
42
|
+
* slot prop: `<input v-bind="control" />`. Extends the caller-facing
|
|
43
|
+
* passthrough props with attrs derived from ControlElement state
|
|
44
|
+
* (`aria-invalid` from `error`, `aria-describedby` pointing at the error span,
|
|
45
|
+
* etc.) and the required `id`.
|
|
46
|
+
*/
|
|
47
|
+
export interface ControlSlotAttrs extends ControlPassthroughProps {
|
|
48
|
+
id: string;
|
|
49
|
+
ariaDescribedby?: string;
|
|
50
|
+
ariaInvalid?: boolean;
|
|
51
|
+
ariaRequired?: boolean;
|
|
52
|
+
}
|
|
39
53
|
declare var __VLS_7: {
|
|
40
54
|
id: string;
|
|
55
|
+
control: ControlSlotAttrs;
|
|
41
56
|
};
|
|
42
57
|
type __VLS_Slots = {} & {
|
|
43
58
|
default?: (props: typeof __VLS_7) => any;
|
|
@@ -10,10 +10,29 @@ const props = defineProps({
|
|
|
10
10
|
label: { type: String, required: false },
|
|
11
11
|
layout: { type: String, required: false, default: "vertical" },
|
|
12
12
|
size: { type: String, required: false, default: "md" },
|
|
13
|
-
fill: { type: Boolean, required: false, default: false }
|
|
13
|
+
fill: { type: Boolean, required: false, default: false },
|
|
14
|
+
tabindex: { type: [Number, String], required: false },
|
|
15
|
+
focusKey: { type: String, required: false },
|
|
16
|
+
disabled: { type: Boolean, required: false },
|
|
17
|
+
required: { type: Boolean, required: false },
|
|
18
|
+
name: { type: String, required: false },
|
|
19
|
+
ariaLabel: { type: String, required: false }
|
|
14
20
|
});
|
|
15
21
|
provideControlSize(toRef(props, "size"));
|
|
16
22
|
const sizeStyle = computed(() => sizeTokens[props.size]);
|
|
23
|
+
const errorId = computed(() => props.error ? `${props.id}-error` : void 0);
|
|
24
|
+
const control = computed(() => ({
|
|
25
|
+
id: props.id,
|
|
26
|
+
tabindex: props.tabindex,
|
|
27
|
+
focusKey: props.focusKey,
|
|
28
|
+
disabled: props.disabled || void 0,
|
|
29
|
+
required: props.required || void 0,
|
|
30
|
+
name: props.name,
|
|
31
|
+
ariaLabel: props.ariaLabel,
|
|
32
|
+
ariaDescribedby: errorId.value,
|
|
33
|
+
ariaInvalid: props.error ? true : void 0,
|
|
34
|
+
ariaRequired: props.required || void 0
|
|
35
|
+
}));
|
|
17
36
|
</script>
|
|
18
37
|
|
|
19
38
|
<template>
|
|
@@ -23,13 +42,13 @@ const sizeStyle = computed(() => sizeTokens[props.size]);
|
|
|
23
42
|
appearance,
|
|
24
43
|
layout,
|
|
25
44
|
`size-${size}`,
|
|
26
|
-
{ 'has-error': error, group, fill }
|
|
45
|
+
{ 'has-error': error, group, fill, disabled },
|
|
46
|
+
$attrs.class
|
|
27
47
|
]"
|
|
28
|
-
:style="sizeStyle"
|
|
29
|
-
v-bind="
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}"
|
|
48
|
+
:style="[sizeStyle, $attrs.style]"
|
|
49
|
+
v-bind="
|
|
50
|
+
group ? { role: 'group', ...label ? { 'aria-labelledby': id } : {} } : {}
|
|
51
|
+
"
|
|
33
52
|
>
|
|
34
53
|
<component
|
|
35
54
|
:is="group ? 'span' : 'label'"
|
|
@@ -41,9 +60,9 @@ const sizeStyle = computed(() => sizeTokens[props.size]);
|
|
|
41
60
|
</component>
|
|
42
61
|
<div class="control-group">
|
|
43
62
|
<div class="slot-wrapper">
|
|
44
|
-
<slot :id />
|
|
63
|
+
<slot :id :control />
|
|
45
64
|
</div>
|
|
46
|
-
<span v-if="error" class="control-error">{{ error }}</span>
|
|
65
|
+
<span v-if="error" :id="errorId" class="control-error">{{ error }}</span>
|
|
47
66
|
</div>
|
|
48
67
|
</div>
|
|
49
68
|
</template>
|
|
@@ -1,43 +1,58 @@
|
|
|
1
1
|
export type ControlLayout = "vertical" | "horizontal";
|
|
2
2
|
export type ControlSize = "sm" | "md" | "lg" | "xl";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* A11y + form attrs that flow from the caller through ControlElement to the
|
|
5
|
+
* actual interactive element via the `control` slot prop. Never rendered on
|
|
6
|
+
* the wrapper itself.
|
|
7
|
+
*/
|
|
8
|
+
export interface ControlPassthroughProps {
|
|
9
|
+
/** Native `tabindex` for the inner element. */
|
|
10
|
+
tabindex?: number | string;
|
|
11
|
+
/** Roving-focus identifier — see useRovingGrid. Rendered as `focus-key` DOM attr. */
|
|
12
|
+
focusKey?: string;
|
|
13
|
+
/** Disables the inner element AND drives wrapper disabled styling. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Marks the inner element required and sets `aria-required`. */
|
|
16
|
+
required?: boolean;
|
|
17
|
+
/** Native `name` attribute for the inner form element. */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Accessible name for the inner element when no visible label is set. */
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ControlProps extends ControlPassthroughProps {
|
|
23
|
+
/** Minimal resets margin and removes border/box-shadow from every element inside the slot. */
|
|
7
24
|
appearance?: "normal" | "minimal";
|
|
8
|
-
/**
|
|
9
|
-
* Error message to display below the control
|
|
10
|
-
*/
|
|
25
|
+
/** Error message to display below the control. Also drives `aria-invalid` and `aria-describedby` on the inner element. */
|
|
11
26
|
error?: string | null;
|
|
12
|
-
/**
|
|
13
|
-
* Marks this control as a group (adds role="group" and aria-labelledby).
|
|
14
|
-
* The label renders as a <span> instead of <label>.
|
|
15
|
-
* Use for groups of related controls (e.g. CheckboxGroup).
|
|
16
|
-
*/
|
|
27
|
+
/** Marks this control as a group (adds role="group" + aria-labelledby on the wrapper). The label renders as a <span> instead of <label>. Use for groups of related controls (e.g. CheckboxGroup). */
|
|
17
28
|
group?: boolean;
|
|
18
|
-
/**
|
|
19
|
-
* ID for the control's form element, auto-generated if not provided
|
|
20
|
-
*/
|
|
29
|
+
/** ID for the inner form element. Auto-generated if not provided. */
|
|
21
30
|
id?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Label text for the control (or legend text when group is true)
|
|
24
|
-
*/
|
|
31
|
+
/** Label text for the control (or legend text when group is true). */
|
|
25
32
|
label?: string;
|
|
26
|
-
/**
|
|
27
|
-
* Label position relative to the control
|
|
28
|
-
*/
|
|
33
|
+
/** Label position relative to the control. */
|
|
29
34
|
layout?: ControlLayout;
|
|
30
|
-
/**
|
|
31
|
-
* Size of the control and its inner elements
|
|
32
|
-
*/
|
|
35
|
+
/** Size of the control and its inner elements. */
|
|
33
36
|
size?: ControlSize;
|
|
34
|
-
/**
|
|
35
|
-
* Whether element should fill the container
|
|
36
|
-
*/
|
|
37
|
+
/** Whether the element should fill the container. */
|
|
37
38
|
fill?: boolean;
|
|
38
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Bag the consumer spreads onto the inner interactive element via the `control`
|
|
42
|
+
* slot prop: `<input v-bind="control" />`. Extends the caller-facing
|
|
43
|
+
* passthrough props with attrs derived from ControlElement state
|
|
44
|
+
* (`aria-invalid` from `error`, `aria-describedby` pointing at the error span,
|
|
45
|
+
* etc.) and the required `id`.
|
|
46
|
+
*/
|
|
47
|
+
export interface ControlSlotAttrs extends ControlPassthroughProps {
|
|
48
|
+
id: string;
|
|
49
|
+
ariaDescribedby?: string;
|
|
50
|
+
ariaInvalid?: boolean;
|
|
51
|
+
ariaRequired?: boolean;
|
|
52
|
+
}
|
|
39
53
|
declare var __VLS_7: {
|
|
40
54
|
id: string;
|
|
55
|
+
control: ControlSlotAttrs;
|
|
41
56
|
};
|
|
42
57
|
type __VLS_Slots = {} & {
|
|
43
58
|
default?: (props: typeof __VLS_7) => any;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: dashed empty/drop zone, add-item card, upload tile, empty state with action
|
|
5
|
+
short: clickable dashed-border tile with icon and label, used for add/upload affordances
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# DashedContainer — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-dashed-container>` is a self-styled clickable tile that renders an
|
|
12
|
+
optional icon and a label inside a dashed border. It is **not** a generic
|
|
13
|
+
slot wrapper.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **No default slot.** The template only renders `icon` and `text` props.
|
|
18
|
+
Children placed between the tags are dropped. To compose richer content,
|
|
19
|
+
use a different primitive.
|
|
20
|
+
- **Always clickable.** The wrapper has `cursor: pointer` and emits
|
|
21
|
+
`click` unconditionally — even with no listener attached, the tile looks
|
|
22
|
+
interactive. Do not use as a passive container.
|
|
23
|
+
- **`size` only scales the icon.** `small` → 2rem, `medium` → 3rem, `large`
|
|
24
|
+
→ 5rem. Padding (2rem), gap (0.5rem), text size, and border are fixed
|
|
25
|
+
regardless of `size`.
|
|
26
|
+
- **`icon` is forwarded to `<orio-icon :name>`.** Must be a name registered
|
|
27
|
+
in `utils/iconRegistry`. Missing names render nothing; check the registry
|
|
28
|
+
before passing a string.
|
|
29
|
+
- **Hover effect comes from the global `gradient-hover` class**, not from
|
|
30
|
+
scoped styles. The dashed border, padding, and layout are scoped; the
|
|
31
|
+
hover gradient is project-global.
|
|
32
|
+
|
|
33
|
+
## Gotchas
|
|
34
|
+
|
|
35
|
+
- **`text` defaults to English in consumer code.** Project convention
|
|
36
|
+
(see CLAUDE.md / translations note) is to pass an i18n key: `:text="$t('addItem')"`,
|
|
37
|
+
not `text="Add Item"`.
|
|
38
|
+
- **Both `icon` and `text` are optional.** With neither, the tile is an
|
|
39
|
+
empty dashed box that still emits `click`. Confirm at least one is set
|
|
40
|
+
unless that empty look is intentional.
|
|
41
|
+
- **The `<span :size>` shorthand** in the template forwards `size` as a
|
|
42
|
+
DOM attribute on the label. It's not styled — harmless, but visible
|
|
43
|
+
in devtools.
|
|
44
|
+
- **No `disabled` state.** If a consumer needs disabled-looking behaviour,
|
|
45
|
+
wrap or override styles externally; do not rely on a prop.
|
|
46
|
+
|
|
47
|
+
## Quick reference
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<orio-dashed-container
|
|
52
|
+
icon="plus"
|
|
53
|
+
:text="$t('gallery.addImage')"
|
|
54
|
+
size="medium"
|
|
55
|
+
@click="openPicker"
|
|
56
|
+
/>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Related
|
|
61
|
+
|
|
62
|
+
- `orio-icon` — icon renderer driven by the same `name` string.
|
|
63
|
+
- `orio-upload` — full file-picker widget; prefer it over hand-rolling
|
|
64
|
+
click-to-upload on a `DashedContainer`.
|
|
65
|
+
- Public API reference: `docs/components/dashed-container.md`.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: empty state, no-results placeholder, blank slate, empty list
|
|
5
|
+
short: centered empty-list placeholder with optional icon, title, description, and action slot
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# EmptyState — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-empty-state>` is a centered placeholder for empty lists, no-search-
|
|
12
|
+
results screens, and similar blank slates.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`title` is required.** Renders as `<orio-view-text type="title">` —
|
|
17
|
+
size scales with the empty-state `size` (large → medium title, others →
|
|
18
|
+
small title).
|
|
19
|
+
- **`description` is optional**, max-width `30ch`. Wraps after that.
|
|
20
|
+
- **`icon` is optional.** Rendered as `<orio-icon>` above the title with
|
|
21
|
+
reduced opacity (`0.5`). Icon size scales with `size`:
|
|
22
|
+
`small` → 2rem, `medium` → 3rem, `large` → 4rem.
|
|
23
|
+
- **Default slot is rendered after the description** — typically a CTA
|
|
24
|
+
button.
|
|
25
|
+
- **Always vertically stacked, centered, text-centered.** No prop to
|
|
26
|
+
switch to horizontal.
|
|
27
|
+
- **`size`**: `"small"` / `"medium"` (default) / `"large"`. Affects
|
|
28
|
+
padding, gap, and icon size — not title color or weight.
|
|
29
|
+
|
|
30
|
+
## Gotchas
|
|
31
|
+
|
|
32
|
+
- **`title` and `description` are plain strings.** Pass i18n keys
|
|
33
|
+
through `$t()` from the parent; no `#title` / `#description` slot.
|
|
34
|
+
For inline `<strong>` or links inside copy, build a custom empty
|
|
35
|
+
state from view primitives.
|
|
36
|
+
- **Slot CTA spacing**: the default slot inherits the column gap
|
|
37
|
+
(`0.25` / `0.5` / `1rem` per size). Buttons sit tight against the
|
|
38
|
+
description — add margin if you need separation.
|
|
39
|
+
- **Used internally by `<orio-selector>` as the `no-options` default.**
|
|
40
|
+
When overriding via the `#no-options` slot, keep the layout similar
|
|
41
|
+
for visual consistency.
|
|
42
|
+
|
|
43
|
+
## Quick reference
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<template>
|
|
47
|
+
<orio-empty-state
|
|
48
|
+
icon="inbox"
|
|
49
|
+
:title="$t('inbox.empty.title')"
|
|
50
|
+
:description="$t('inbox.empty.description')"
|
|
51
|
+
size="large"
|
|
52
|
+
>
|
|
53
|
+
<orio-button @click="createMessage">
|
|
54
|
+
{{ $t("inbox.empty.compose") }}
|
|
55
|
+
</orio-button>
|
|
56
|
+
</orio-empty-state>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Related
|
|
61
|
+
|
|
62
|
+
- `<orio-view-text>` — used internally for title/description.
|
|
63
|
+
- `<orio-banner>` — for inline notice strips instead of full-block
|
|
64
|
+
empty states.
|
|
65
|
+
- Public API reference: `docs/components/empty-state.md`.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: form wrapper, submission, validation surface, auto-bind form model
|
|
5
|
+
short: form wrapper that auto-binds child controls by `name` prop to a single object v-model
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Form — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-form>` is a form wrapper that walks its slot vnodes and **auto-binds
|
|
12
|
+
child controls to a single object v-model** by matching each child's
|
|
13
|
+
`name` prop to a key (or dot path) in the model. Generic over `T extends
|
|
14
|
+
object`.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **v-model is an object `T`.** Each child with a `name="..."` prop
|
|
19
|
+
receives `modelValue` and `onUpdate:modelValue` cloned in via
|
|
20
|
+
`cloneVNode`. The user's hand-written v-model on that child is
|
|
21
|
+
overridden if `name` matches a field.
|
|
22
|
+
- **Auto-bind requires both `name` and a matching field path.** Children
|
|
23
|
+
without `name`, or with a `name` that doesn't resolve via `getByPath`,
|
|
24
|
+
render unchanged. Auto-bind is silent — no warning when a name doesn't
|
|
25
|
+
match.
|
|
26
|
+
- **Dot-paths supported**: `name="user.email"` writes to `model.user.email`.
|
|
27
|
+
The intermediate path must already exist (`setByPath` no-ops if the
|
|
28
|
+
parent is missing).
|
|
29
|
+
- **Children must follow the `modelValue` / `update:modelValue` contract.**
|
|
30
|
+
Auto-bind injects these props directly; v-model sugar is not used. Any
|
|
31
|
+
child that doesn't honor that pair is silently un-bound. All orio
|
|
32
|
+
form components do; custom children need to follow the contract.
|
|
33
|
+
- **Slot walking is recursive.** Vnode children, slot functions, and slot
|
|
34
|
+
objects are all descended. Wrappers (`<div>` groupers, layout columns)
|
|
35
|
+
are transparent.
|
|
36
|
+
- **`disabled` wraps the children in `<fieldset disabled>`** with
|
|
37
|
+
`display: contents` so nothing visual changes. The native `disabled`
|
|
38
|
+
attribute on the fieldset gates every form control inside (browser-
|
|
39
|
+
native behavior).
|
|
40
|
+
- **`loading` adds `pointer-events: none`** plus 0.6 opacity on the form.
|
|
41
|
+
Visual + interaction lock without disabling form values.
|
|
42
|
+
- **`novalidate` is set on the `<form>`** — HTML5 native validation
|
|
43
|
+
bubbles are off. Pair with `useValidation` for declarative checks.
|
|
44
|
+
- **`@submit` emits a single `submit` event** after `preventDefault`. The
|
|
45
|
+
emit is gated by `disabled || loading`. There is **no** built-in submit
|
|
46
|
+
button — render `<orio-button type="submit">` (or similar) inside.
|
|
47
|
+
|
|
48
|
+
## Gotchas
|
|
49
|
+
|
|
50
|
+
- **No per-field error wiring.** `error` props on children are still
|
|
51
|
+
user-managed. The Form is only a binder, not a validator.
|
|
52
|
+
- **Reactivity through `setByPath` requires mutable nested objects.** A
|
|
53
|
+
frozen / readonly model breaks silently. Use a plain ref'd object.
|
|
54
|
+
- **Auto-bind clones the vnode** — keyed lists work, but if a child also
|
|
55
|
+
emits something other than `update:modelValue` via the same `onUpdate`
|
|
56
|
+
pattern, the clone replaces only the listed handlers.
|
|
57
|
+
- **Slot is descended into every render** — performance scales linearly
|
|
58
|
+
with slot depth. For very large forms (hundreds of fields), consider
|
|
59
|
+
splitting into nested `<orio-form>`s by section.
|
|
60
|
+
- **`name` collisions silently overwrite.** Two children with the same
|
|
61
|
+
`name` both bind to the same field; both updates write to the same
|
|
62
|
+
spot. Order is undefined.
|
|
63
|
+
|
|
64
|
+
## Quick reference
|
|
65
|
+
|
|
66
|
+
```vue
|
|
67
|
+
<script setup lang="ts">
|
|
68
|
+
interface Profile {
|
|
69
|
+
name: string;
|
|
70
|
+
email: string;
|
|
71
|
+
preferences: { newsletter: boolean };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const profile = ref<Profile>({
|
|
75
|
+
name: "",
|
|
76
|
+
email: "",
|
|
77
|
+
preferences: { newsletter: false },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function save() {
|
|
81
|
+
// Submit profile.value to API
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<orio-form v-model="profile" :loading="saving" @submit="save">
|
|
87
|
+
<orio-input name="name" :label="$t('profile.name')" />
|
|
88
|
+
<orio-input name="email" :label="$t('profile.email')" type="email" />
|
|
89
|
+
<orio-check-box name="preferences.newsletter">
|
|
90
|
+
{{ $t("profile.newsletter") }}
|
|
91
|
+
</orio-check-box>
|
|
92
|
+
|
|
93
|
+
<orio-button type="submit">{{ $t("common.save") }}</orio-button>
|
|
94
|
+
</orio-form>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- `useValidation` — declarative validation rules for form fields.
|
|
101
|
+
- `<orio-control-element>` — wraps every form input with label/error.
|
|
102
|
+
- Public API reference: `docs/components/form.md`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: icon, SVG renderer, glyph, symbol
|
|
5
|
+
short: SVG icon renderer that pulls from `utils/icon-registry` and renders via v-html
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Icon — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-icon>` renders an SVG icon from `utils/icon-registry` into a
|
|
12
|
+
`<span>` via `v-html`. Unknown icon names render empty.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`name` is required.** Either a registered `IconName` (typed) or any
|
|
17
|
+
string. Unregistered names render as empty string (no visible
|
|
18
|
+
output, no warning).
|
|
19
|
+
- **`size`**: `string | number`. A number is treated as pixels (`"24px"`).
|
|
20
|
+
A string is used verbatim (`"1.5em"`, `"2rem"`, `"100%"`). When
|
|
21
|
+
`undefined`, falls back to the CSS var `--control-icon-size` (default
|
|
22
|
+
`1.5em`).
|
|
23
|
+
- **`color` defaults to `"currentColor"`** — the icon inherits parent
|
|
24
|
+
text color. Pass a CSS color string to override.
|
|
25
|
+
- **`v-html` is used to inject the raw SVG markup.** The icon registry
|
|
26
|
+
is the trust boundary — never render dynamic / user-controlled SVG
|
|
27
|
+
strings through it.
|
|
28
|
+
- **SVGs inside use `fill: currentColor`** so the `color` prop / parent
|
|
29
|
+
color flows through.
|
|
30
|
+
- **`flex-shrink: 0`** is set on the span. The icon never shrinks inside
|
|
31
|
+
a flex layout — important for inline labels with overflow.
|
|
32
|
+
- **The wrapper is `display: inline-flex; align-items: center; justify-content: center`**
|
|
33
|
+
so the SVG is centered when the size exceeds the SVG viewBox.
|
|
34
|
+
|
|
35
|
+
## Gotchas
|
|
36
|
+
|
|
37
|
+
- **Bypass-XSS surface is the registry.** Anything in `utils/icon-registry`
|
|
38
|
+
is rendered as raw HTML. Do not extend the registry with strings
|
|
39
|
+
derived from user input.
|
|
40
|
+
- **Sizing a string `100%` requires a sized parent.** The span goes to
|
|
41
|
+
the size you pass; the inner SVG fills 100% of that.
|
|
42
|
+
- **`color` is applied via inline style**, so it beats class-based
|
|
43
|
+
styling. To recolor via CSS class, omit the `color` prop.
|
|
44
|
+
- **Spelling errors silently render nothing.** If an icon is missing
|
|
45
|
+
during dev, check the registry — there's no console warning.
|
|
46
|
+
|
|
47
|
+
## Quick reference
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<orio-icon name="check" :size="24" />
|
|
52
|
+
<orio-icon name="warning" size="1.5rem" color="var(--color-alert)" />
|
|
53
|
+
<orio-icon name="search" />
|
|
54
|
+
</template>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Related
|
|
58
|
+
|
|
59
|
+
- `<orio-loading-spinner>` — thin wrapper for the `loading-loop` icon.
|
|
60
|
+
- `utils/icon-registry` — the source of all available icon names.
|
|
61
|
+
- Public API reference: `docs/components/icon.md`.
|