orio-ui 1.24.0 → 1.27.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 +2 -2
- package/dist/module.json +1 -1
- 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 +51 -0
- package/dist/runtime/components/Calendar.vue +254 -87
- package/dist/runtime/components/Canvas/USAGE.md +65 -0
- package/dist/runtime/components/CheckBox.vue +9 -3
- package/dist/runtime/components/CheckboxGroup.vue +7 -1
- package/dist/runtime/components/ControlElement.USAGE.md +69 -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/Input.USAGE.md +49 -0
- package/dist/runtime/components/Input.vue +13 -3
- package/dist/runtime/components/Modal.USAGE.md +64 -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.vue +7 -2
- 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/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.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.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/TaggableSelector.vue +7 -1
- package/dist/runtime/components/Textarea.vue +13 -3
- package/dist/runtime/components/date/Picker.USAGE.md +44 -0
- package/dist/runtime/components/date/Picker.vue +7 -1
- package/dist/runtime/components/date/PickerTrigger.vue +9 -3
- package/dist/runtime/components/date/RangePicker.vue +7 -1
- package/dist/runtime/composables/useFilter.d.ts +91 -0
- package/dist/runtime/composables/useFilter.js +111 -0
- package/dist/runtime/composables/useRovingGrid.d.ts +35 -0
- package/dist/runtime/composables/useRovingGrid.js +115 -0
- package/dist/runtime/i18n/en.json +4 -1
- package/dist/runtime/i18n/uk.json +4 -1
- package/package.json +1 -1
|
@@ -10,19 +10,25 @@ const props = defineProps({
|
|
|
10
10
|
label: { type: String, required: false },
|
|
11
11
|
layout: { type: String, required: false },
|
|
12
12
|
size: { type: String, required: false },
|
|
13
|
-
fill: { type: Boolean, required: false }
|
|
13
|
+
fill: { type: Boolean, required: 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
|
</script>
|
|
16
22
|
|
|
17
23
|
<template>
|
|
18
|
-
<orio-control-element v-bind="props" class="checkbox" fill>
|
|
24
|
+
<orio-control-element v-slot="{ control }" v-bind="props" class="checkbox" fill>
|
|
19
25
|
<label class="checkbox-label">
|
|
20
26
|
<input
|
|
21
27
|
v-model="modelValue"
|
|
28
|
+
v-bind="{ ...$attrs, ...control }"
|
|
22
29
|
type="checkbox"
|
|
23
30
|
class="checkbox-input"
|
|
24
31
|
tabindex="-1"
|
|
25
|
-
v-bind="$attrs"
|
|
26
32
|
/>
|
|
27
33
|
<span
|
|
28
34
|
class="checkbox-box"
|
|
@@ -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,69 @@
|
|
|
1
|
+
# ControlElement — agent-only invariants
|
|
2
|
+
|
|
3
|
+
`ControlElement` is the wrapper every form input uses (Input, Textarea,
|
|
4
|
+
NumberInput, Selector, CheckBox, etc.). When you build a new orio form
|
|
5
|
+
component, wrap it in `<orio-control-element>`. When you consume an existing
|
|
6
|
+
one, pass `ControlProps` straight through — they are usually re-exported.
|
|
7
|
+
|
|
8
|
+
## Invariants
|
|
9
|
+
|
|
10
|
+
- **`inheritAttrs: false`.** Attrs do **not** auto-flow onto the wrapper or
|
|
11
|
+
the inner element. The component exposes a `control` slot prop containing
|
|
12
|
+
the a11y/form attr bag (`id`, `ariaDescribedby`, `ariaInvalid`,
|
|
13
|
+
`ariaRequired`, plus passthrough `tabindex`, `focusKey`, `disabled`,
|
|
14
|
+
`required`, `name`, `ariaLabel`). The inner element **must** spread it:
|
|
15
|
+
```vue
|
|
16
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
17
|
+
<input v-bind="control" />
|
|
18
|
+
</orio-control-element>
|
|
19
|
+
```
|
|
20
|
+
- **`group` prop changes the semantic root.** When `true`, the wrapper gets
|
|
21
|
+
`role="group"` + `aria-labelledby`, and the label renders as `<span>`
|
|
22
|
+
(still id-linked) instead of `<label>`. Use this for `CheckboxGroup`,
|
|
23
|
+
radio groups, anything where the "control" is multiple inputs.
|
|
24
|
+
- **Error wiring is automatic.** Setting `error` to a non-null string:
|
|
25
|
+
- Renders a `.control-error` span below the slot.
|
|
26
|
+
- Sets `aria-invalid` and `aria-describedby` on the inner element via the
|
|
27
|
+
`control` slot prop.
|
|
28
|
+
- Adds a red border to `.slot-wrapper` (unless the wrapper contains a
|
|
29
|
+
`:deep(.error-fields)` element, in which case the inner component owns
|
|
30
|
+
error styling — see TaggableSelector).
|
|
31
|
+
- **`size` is provided to descendants** via `provideControlSize`. Children
|
|
32
|
+
that use `useControlSize()` (e.g. inner buttons, icons) inherit it
|
|
33
|
+
automatically — do not re-pass `size` down manually.
|
|
34
|
+
- **`appearance="minimal"`** zeros margin and strips border + box-shadow from
|
|
35
|
+
the first slot child. Use for inputs embedded in a row with their own
|
|
36
|
+
surrounding chrome.
|
|
37
|
+
|
|
38
|
+
## Gotchas
|
|
39
|
+
|
|
40
|
+
- The slot's `id` matches what the `<label>` points to via `for`. The inner
|
|
41
|
+
element receives the same `id` through the `control` bag. Do **not**
|
|
42
|
+
override it — it is `useId()` by default and accessibility breaks if two
|
|
43
|
+
inputs in the same render share an id.
|
|
44
|
+
- `disabled` is forwarded to the inner element AND drives the wrapper's
|
|
45
|
+
`.disabled` class for styling. Pass `disabled` on the wrapper, never on the
|
|
46
|
+
inner element directly.
|
|
47
|
+
- The exported types are the contract:
|
|
48
|
+
- `ControlProps` — what consumers pass to ControlElement.
|
|
49
|
+
- `ControlPassthroughProps` — the subset that travels to the inner element.
|
|
50
|
+
- `ControlSlotAttrs` — the bag exposed via the `control` slot prop.
|
|
51
|
+
- `ControlLayout = "vertical" | "horizontal"`.
|
|
52
|
+
Components that wrap ControlElement extend `ControlProps`, e.g.
|
|
53
|
+
`interface Props extends ControlProps { ... }`.
|
|
54
|
+
|
|
55
|
+
## Quick reference
|
|
56
|
+
|
|
57
|
+
```vue
|
|
58
|
+
<script setup lang="ts">
|
|
59
|
+
import type { ControlProps } from "./ControlElement.vue";
|
|
60
|
+
interface Props extends ControlProps { /* component-specific props */ }
|
|
61
|
+
const props = defineProps<Props>();
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
66
|
+
<my-inner-element v-bind="control" />
|
|
67
|
+
</orio-control-element>
|
|
68
|
+
</template>
|
|
69
|
+
```
|
|
@@ -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,49 @@
|
|
|
1
|
+
# Input — agent-only invariants
|
|
2
|
+
|
|
3
|
+
`<orio-input>` is the text input wrapping `ControlElement`. Read
|
|
4
|
+
`ControlElement.USAGE.md` first — most of the contract lives there.
|
|
5
|
+
|
|
6
|
+
## Invariants
|
|
7
|
+
|
|
8
|
+
- **Extends `ControlProps`** with one override: `layout?: InputLayout` where
|
|
9
|
+
`InputLayout = ControlLayout | "inner"`. The extra `"inner"` is an
|
|
10
|
+
Input-specific mode that floats the label inside the input chrome.
|
|
11
|
+
- **`layout="inner"` translates internally** to `layout="vertical"` on
|
|
12
|
+
ControlElement and adds an `.inner` class on the wrapper. The label
|
|
13
|
+
reposition is driven by `:deep()` styles on ControlElement internals — no
|
|
14
|
+
duplicate label DOM is created.
|
|
15
|
+
- **The `control` slot bag is spread onto the inner `<input>`** alongside
|
|
16
|
+
`$attrs`: `v-bind="{ ...$attrs, ...control }"`. Attrs like `type`,
|
|
17
|
+
`autocomplete`, `placeholder`, `inputmode` work on `<orio-input>` and land
|
|
18
|
+
on the underlying `<input>`.
|
|
19
|
+
- **v-model is `string`** (default `""`). For numeric input use
|
|
20
|
+
`<orio-number-input>` instead.
|
|
21
|
+
|
|
22
|
+
## Gotchas
|
|
23
|
+
|
|
24
|
+
- The `.slot-wrapper` uses `display: flex; align-items: center;` so `before`
|
|
25
|
+
and `after` slots sit inline with the input. Don't add wrapping divs inside
|
|
26
|
+
those slots — they'll break alignment.
|
|
27
|
+
- `:placeholder-shown` is used internally for the inner-label "empty" state.
|
|
28
|
+
If you pass an empty placeholder, the inner-label trick still works because
|
|
29
|
+
the wrapper sets `placeholder=" "` upstream when needed.
|
|
30
|
+
- The default browser input border is removed; the visible border lives on
|
|
31
|
+
`.slot-wrapper`. Custom inputs swapped in via slots will not inherit it —
|
|
32
|
+
prefer `before`/`after` slots over replacing the input.
|
|
33
|
+
|
|
34
|
+
## Quick reference
|
|
35
|
+
|
|
36
|
+
```vue
|
|
37
|
+
<orio-input
|
|
38
|
+
v-model="email"
|
|
39
|
+
label="Email"
|
|
40
|
+
layout="inner"
|
|
41
|
+
type="email"
|
|
42
|
+
autocomplete="email"
|
|
43
|
+
:error="emailError"
|
|
44
|
+
>
|
|
45
|
+
<template #before>
|
|
46
|
+
<orio-icon name="mail" />
|
|
47
|
+
</template>
|
|
48
|
+
</orio-input>
|
|
49
|
+
```
|
|
@@ -7,20 +7,30 @@ const props = defineProps({
|
|
|
7
7
|
id: { type: String, required: false },
|
|
8
8
|
label: { type: String, required: false },
|
|
9
9
|
size: { type: String, required: false },
|
|
10
|
-
fill: { type: Boolean, required: false }
|
|
10
|
+
fill: { type: Boolean, required: false },
|
|
11
|
+
tabindex: { type: [Number, String], required: false },
|
|
12
|
+
focusKey: { type: String, required: false },
|
|
13
|
+
disabled: { type: Boolean, required: false },
|
|
14
|
+
required: { type: Boolean, required: false },
|
|
15
|
+
name: { type: String, required: false },
|
|
16
|
+
ariaLabel: { type: String, required: false }
|
|
11
17
|
});
|
|
12
18
|
const modelValue = defineModel({ type: String, ...{ default: "" } });
|
|
13
19
|
</script>
|
|
14
20
|
|
|
15
21
|
<template>
|
|
16
22
|
<orio-control-element
|
|
17
|
-
v-slot="{
|
|
23
|
+
v-slot="{ control }"
|
|
18
24
|
v-bind="props"
|
|
19
25
|
:layout="layout === 'inner' ? 'vertical' : layout"
|
|
20
26
|
:class="{ inner: layout === 'inner' }"
|
|
21
27
|
>
|
|
22
28
|
<slot name="before" />
|
|
23
|
-
<input
|
|
29
|
+
<input
|
|
30
|
+
v-model="modelValue"
|
|
31
|
+
type="text"
|
|
32
|
+
v-bind="{ ...$attrs, ...control }"
|
|
33
|
+
/>
|
|
24
34
|
<slot name="after" />
|
|
25
35
|
</orio-control-element>
|
|
26
36
|
</template>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Modal — agent-only invariants
|
|
2
|
+
|
|
3
|
+
`<orio-modal>` is the teleported overlay dialog.
|
|
4
|
+
|
|
5
|
+
## Invariants
|
|
6
|
+
|
|
7
|
+
- **Teleported to `<body>`.** Renders outside the parent DOM subtree. Any
|
|
8
|
+
CSS that targets `.modal` from a parent will not apply; scope styles via
|
|
9
|
+
`:deep()` from a parent or write global styles.
|
|
10
|
+
- **`origin` prop drives the open animation.** Pass the `getBoundingClientRect`
|
|
11
|
+
of the element that triggered the open (e.g. the clicked button) to
|
|
12
|
+
animate the modal **from** that rect to centered. Pass `null` to fade in
|
|
13
|
+
at center with no scale-from.
|
|
14
|
+
- **`v-model:show`** controls visibility. Backdrop click closes (`@click.self`
|
|
15
|
+
on the overlay). The default close button (rendered when no `header`
|
|
16
|
+
slot is supplied) also toggles `show`.
|
|
17
|
+
- **Body scroll lock** is applied automatically while `show` is true, via
|
|
18
|
+
`useScrollLock` from `@vueuse/core`. SSR-safe: ref defaults to `false`
|
|
19
|
+
on the server.
|
|
20
|
+
- **Header/footer are auto-hidden** when no `title` prop and no
|
|
21
|
+
`#header`/`#footer` slot is present. The content section (`#default`)
|
|
22
|
+
always renders.
|
|
23
|
+
|
|
24
|
+
## Gotchas
|
|
25
|
+
|
|
26
|
+
- Multiple modals stacked at once will all lock body scroll; closing one
|
|
27
|
+
releases the lock for all. If you nest modals, manage the lock yourself.
|
|
28
|
+
- `origin`'s `width` / `height` should be the trigger's rendered size, not
|
|
29
|
+
the modal's. The animation derives the inverse scale from
|
|
30
|
+
`width / modalWidth` — wrong size = visible jump.
|
|
31
|
+
- The component uses inline styles via direct `.style.transform`
|
|
32
|
+
assignment on the wrapper ref. Do not animate `transform` from outside;
|
|
33
|
+
the component will overwrite it on the next open.
|
|
34
|
+
|
|
35
|
+
## Quick reference
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import { ref } from "vue";
|
|
40
|
+
const open = ref(false);
|
|
41
|
+
const origin = ref<DOMRect | null>(null);
|
|
42
|
+
|
|
43
|
+
function trigger(event: MouseEvent) {
|
|
44
|
+
origin.value = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
45
|
+
open.value = true;
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<template>
|
|
50
|
+
<orio-button @click="trigger">Open settings</orio-button>
|
|
51
|
+
|
|
52
|
+
<orio-modal v-model:show="open" :origin="origin" title="Settings">
|
|
53
|
+
<p>Modal body…</p>
|
|
54
|
+
<template #footer>
|
|
55
|
+
<orio-button @click="open = false">Done</orio-button>
|
|
56
|
+
</template>
|
|
57
|
+
</orio-modal>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Related
|
|
62
|
+
|
|
63
|
+
- `useModal` composable — programmatic open/close API without managing
|
|
64
|
+
`show` yourself. See `docs/composables/use-modal.md`.
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { computed, toRefs, useSlots } from "vue";
|
|
3
3
|
const props = defineProps({
|
|
4
4
|
icon: { type: String, required: false },
|
|
5
|
-
disabled: { type: Boolean, required: false },
|
|
6
5
|
active: { type: Boolean, required: false, default: false },
|
|
7
6
|
appearance: { type: String, required: false },
|
|
8
7
|
error: { type: [String, null], required: false },
|
|
@@ -11,7 +10,13 @@ const props = defineProps({
|
|
|
11
10
|
label: { type: String, required: false },
|
|
12
11
|
layout: { type: String, required: false },
|
|
13
12
|
size: { type: String, required: false },
|
|
14
|
-
fill: { type: Boolean, required: false }
|
|
13
|
+
fill: { type: Boolean, required: 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 }
|
|
15
20
|
});
|
|
16
21
|
const { disabled, active } = toRefs(props);
|
|
17
22
|
const slots = useSlots();
|
|
@@ -28,11 +33,10 @@ function click(event) {
|
|
|
28
33
|
</script>
|
|
29
34
|
|
|
30
35
|
<template>
|
|
31
|
-
<orio-control-element v-bind="props">
|
|
36
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
32
37
|
<button
|
|
33
|
-
v-bind="
|
|
38
|
+
v-bind="{ ...$attrs, ...control }"
|
|
34
39
|
:class="{ 'icon-only': isIconOnly, active }"
|
|
35
|
-
:disabled
|
|
36
40
|
:aria-current="active ? 'page' : void 0"
|
|
37
41
|
@click="click"
|
|
38
42
|
>
|
|
@@ -6,14 +6,19 @@ defineProps({
|
|
|
6
6
|
max: { type: Number, required: false, default: void 0 },
|
|
7
7
|
step: { type: Number, required: false, default: 1 },
|
|
8
8
|
decimalPlaces: { type: Number, required: false, default: 0 },
|
|
9
|
-
disabled: { type: Boolean, required: false, default: false },
|
|
10
9
|
appearance: { type: String, required: false },
|
|
11
10
|
error: { type: [String, null], required: false },
|
|
12
11
|
group: { type: Boolean, required: false },
|
|
13
12
|
id: { type: String, required: false },
|
|
14
13
|
label: { type: String, required: false },
|
|
15
14
|
size: { type: String, required: false },
|
|
16
|
-
fill: { type: Boolean, required: false }
|
|
15
|
+
fill: { type: Boolean, required: false },
|
|
16
|
+
tabindex: { type: [Number, String], required: false },
|
|
17
|
+
focusKey: { type: String, required: false },
|
|
18
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
19
|
+
required: { type: Boolean, required: false },
|
|
20
|
+
name: { type: String, required: false },
|
|
21
|
+
ariaLabel: { type: String, required: false }
|
|
17
22
|
});
|
|
18
23
|
const modelValue = defineModel({ type: Number, ...{ default: 0 } });
|
|
19
24
|
const { pressAndHold, stop } = usePressAndHold();
|
|
@@ -6,14 +6,19 @@ defineProps({
|
|
|
6
6
|
max: { type: Number, required: false, default: void 0 },
|
|
7
7
|
step: { type: Number, required: false, default: 1 },
|
|
8
8
|
decimalPlaces: { type: Number, required: false, default: 0 },
|
|
9
|
-
disabled: { type: Boolean, required: false, default: false },
|
|
10
9
|
appearance: { type: String, required: false },
|
|
11
10
|
error: { type: [String, null], required: false },
|
|
12
11
|
group: { type: Boolean, required: false },
|
|
13
12
|
id: { type: String, required: false },
|
|
14
13
|
label: { type: String, required: false },
|
|
15
14
|
size: { type: String, required: false },
|
|
16
|
-
fill: { type: Boolean, required: false }
|
|
15
|
+
fill: { type: Boolean, required: false },
|
|
16
|
+
tabindex: { type: [Number, String], required: false },
|
|
17
|
+
focusKey: { type: String, required: false },
|
|
18
|
+
disabled: { type: Boolean, required: false, default: false },
|
|
19
|
+
required: { type: Boolean, required: false },
|
|
20
|
+
name: { type: String, required: false },
|
|
21
|
+
ariaLabel: { type: String, required: false }
|
|
17
22
|
});
|
|
18
23
|
const modelValue = defineModel({ type: Number, ...{ default: 0 } });
|
|
19
24
|
const { pressAndHold, stop } = usePressAndHold();
|
|
@@ -6,7 +6,6 @@ export interface NumberInputProps extends Omit<ControlProps, "layout"> {
|
|
|
6
6
|
max?: number;
|
|
7
7
|
step?: number;
|
|
8
8
|
decimalPlaces?: number;
|
|
9
|
-
disabled?: boolean;
|
|
10
9
|
}
|
|
11
10
|
type __VLS_Props = NumberInputProps;
|
|
12
11
|
declare function increase(): void;
|
|
@@ -30,7 +29,6 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
30
29
|
"onUpdate:modelValue"?: ((value: number) => any) | undefined;
|
|
31
30
|
}>, {
|
|
32
31
|
layout: InputLayout;
|
|
33
|
-
disabled: boolean;
|
|
34
32
|
step: number;
|
|
35
33
|
min: number;
|
|
36
34
|
max: number;
|
|
@@ -6,14 +6,19 @@ const props = defineProps({
|
|
|
6
6
|
max: { type: Number, required: false, default: void 0 },
|
|
7
7
|
step: { type: Number, required: false, default: 1 },
|
|
8
8
|
decimalPlaces: { type: Number, required: false, default: 0 },
|
|
9
|
-
disabled: { type: Boolean, required: false, default: false },
|
|
10
9
|
appearance: { type: String, required: false },
|
|
11
10
|
error: { type: [String, null], required: false },
|
|
12
11
|
group: { type: Boolean, required: false },
|
|
13
12
|
id: { type: String, required: false },
|
|
14
13
|
label: { type: String, required: false },
|
|
15
14
|
size: { type: String, required: false },
|
|
16
|
-
fill: { type: Boolean, required: false }
|
|
15
|
+
fill: { type: Boolean, required: false },
|
|
16
|
+
tabindex: { type: [Number, String], required: false },
|
|
17
|
+
focusKey: { type: String, required: false },
|
|
18
|
+
disabled: { type: Boolean, required: false },
|
|
19
|
+
required: { type: Boolean, required: false },
|
|
20
|
+
name: { type: String, required: false },
|
|
21
|
+
ariaLabel: { type: String, required: false }
|
|
17
22
|
});
|
|
18
23
|
const { min, max, step, decimalPlaces } = toRefs(props);
|
|
19
24
|
const modelValue = defineModel({ type: Number, ...{ default: 0 } });
|
|
@@ -49,7 +54,6 @@ const controlProps = computed(() => {
|
|
|
49
54
|
max: _max,
|
|
50
55
|
step: _step,
|
|
51
56
|
decimalPlaces: _decimalPlaces,
|
|
52
|
-
disabled: _disabled,
|
|
53
57
|
...rest
|
|
54
58
|
} = props;
|
|
55
59
|
return rest;
|
|
@@ -64,19 +68,17 @@ const slotExpose = computed(() => ({
|
|
|
64
68
|
|
|
65
69
|
<template>
|
|
66
70
|
<orio-control-element
|
|
67
|
-
v-slot="{
|
|
71
|
+
v-slot="{ control }"
|
|
68
72
|
v-bind="controlProps"
|
|
69
73
|
:layout="layout === 'inner' ? 'vertical' : layout"
|
|
70
74
|
:class="{ inner: layout === 'inner' }"
|
|
71
75
|
>
|
|
72
76
|
<div class="wrapper">
|
|
73
77
|
<input
|
|
74
|
-
v-bind="
|
|
75
|
-
:id
|
|
78
|
+
v-bind="{ ...$attrs, ...control }"
|
|
76
79
|
v-model="modelValue"
|
|
77
80
|
type="number"
|
|
78
81
|
class="number-input"
|
|
79
|
-
:disabled
|
|
80
82
|
:min
|
|
81
83
|
:max
|
|
82
84
|
:step
|