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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
const props = defineProps({
|
|
3
|
-
disabled: { type: Boolean, required: false, default: false },
|
|
4
3
|
appearance: { type: String, required: false },
|
|
5
4
|
error: { type: [String, null], required: false },
|
|
6
5
|
group: { type: Boolean, required: false },
|
|
@@ -8,7 +7,13 @@ const props = defineProps({
|
|
|
8
7
|
label: { type: String, required: false },
|
|
9
8
|
layout: { type: String, required: false },
|
|
10
9
|
size: { type: String, required: false },
|
|
11
|
-
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, default: false },
|
|
14
|
+
required: { type: Boolean, required: false },
|
|
15
|
+
name: { type: String, required: false },
|
|
16
|
+
ariaLabel: { type: String, required: false }
|
|
12
17
|
});
|
|
13
18
|
const modelValue = defineModel({ type: Boolean, ...{ required: false } });
|
|
14
19
|
function toggle() {
|
|
@@ -18,13 +23,11 @@ function toggle() {
|
|
|
18
23
|
</script>
|
|
19
24
|
|
|
20
25
|
<template>
|
|
21
|
-
<orio-control-element v-slot="{
|
|
26
|
+
<orio-control-element v-slot="{ control }" v-bind="props">
|
|
22
27
|
<button
|
|
23
|
-
|
|
24
|
-
v-bind="$attrs"
|
|
28
|
+
v-bind="{ ...$attrs, ...control }"
|
|
25
29
|
class="switch-button"
|
|
26
|
-
:class="{ active: modelValue, disabled }"
|
|
27
|
-
:disabled="disabled"
|
|
30
|
+
:class="{ active: modelValue, disabled: props.disabled }"
|
|
28
31
|
@click="toggle"
|
|
29
32
|
@keydown.enter.prevent="toggle"
|
|
30
33
|
@keydown.space.prevent="toggle"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Buttons & indicators
|
|
4
|
+
purpose: tag, chip, label, removable chip, category pill
|
|
5
|
+
short: small text chip with neutral or accent variant; static display only (no remove behavior)
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Tag — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-tag>` is a static text chip. No close button, no click handler, no
|
|
12
|
+
removal behavior — display only.
|
|
13
|
+
|
|
14
|
+
## Invariants
|
|
15
|
+
|
|
16
|
+
- **`text` is required.** It is the only way to set the chip's visible
|
|
17
|
+
content — there is no default slot.
|
|
18
|
+
- **`variant`**: `"neutral"` (default, gray) or `"accent"` (themed
|
|
19
|
+
accent). No `danger` / `alert` colors here — use `<orio-badge>` for
|
|
20
|
+
that.
|
|
21
|
+
- **`id` is an optional uniqueness key** used by upstream components
|
|
22
|
+
(`<orio-taggable-selector>`) to match selected tags. Pass it whenever
|
|
23
|
+
the tag participates in selection state.
|
|
24
|
+
- **`user-select: none`** — text inside the chip cannot be selected with
|
|
25
|
+
the cursor.
|
|
26
|
+
|
|
27
|
+
## Gotchas
|
|
28
|
+
|
|
29
|
+
- **No remove / close button.** The `TagProps` shape is intentionally
|
|
30
|
+
passive. For removable chips, wrap a custom button next to it or build
|
|
31
|
+
your own primitive.
|
|
32
|
+
- **No icon support.** No `icon` prop, no slot. For icon+text chips,
|
|
33
|
+
build directly from a `<span>` with `<orio-icon>`.
|
|
34
|
+
- **No size variants.** Padding (`0.25rem 0.6rem`) and font size
|
|
35
|
+
(`--font-sm`) are fixed.
|
|
36
|
+
|
|
37
|
+
## Quick reference
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<template>
|
|
41
|
+
<orio-tag :text="$t('category.urgent')" variant="accent" />
|
|
42
|
+
<orio-tag :text="$t('category.draft')" />
|
|
43
|
+
</template>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Related
|
|
47
|
+
|
|
48
|
+
- `<orio-taggable-selector>` — uses `TagProps` as its option shape.
|
|
49
|
+
- `<orio-badge>` — when you need a status pill with semantic color
|
|
50
|
+
variants.
|
|
51
|
+
- Public API reference: `docs/components/tag.md`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: multi-select with tag chips, taggable selector, chip picker
|
|
5
|
+
short: multi-select Selector that renders chosen options as tag chips in the trigger
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TaggableSelector — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-taggable-selector>` is a thin wrapper over `<orio-selector>` that
|
|
12
|
+
forces `multiple` mode and renders the selection as a row of `<orio-tag>`
|
|
13
|
+
chips inside the trigger. Read `Selector.USAGE.md` for the full contract.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Always multi-select.** The internal `<orio-selector>` is passed
|
|
18
|
+
`multiple` unconditionally; the prop cannot be turned off.
|
|
19
|
+
- **`optionName` defaults to `"text"`** to align with `TagProps.text`. If
|
|
20
|
+
your option type uses a different label key, pass `option-name`.
|
|
21
|
+
- **v-model type is `TagProps[]`** — `{ id?: string; text: string;
|
|
22
|
+
variant?: "neutral" | "accent" }[]`. Options must be tag-shaped or
|
|
23
|
+
augmented to satisfy this contract.
|
|
24
|
+
- **Tags in the trigger are display-only.** Clicking a chip does **not**
|
|
25
|
+
remove the option. The user must reopen the dropdown and uncheck it.
|
|
26
|
+
This is a real usability tradeoff — for click-to-remove, fall back to
|
|
27
|
+
`<orio-selector>` with a custom `#trigger-label`.
|
|
28
|
+
- **Trigger lays chips out as `flex-wrap`** with `0.5rem` gap, left-aligned.
|
|
29
|
+
Selecting many chips grows the trigger height; constrain it if your
|
|
30
|
+
layout depends on a fixed height.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- **`field` (uniqueness key) still defaults to `"id"`** from the underlying
|
|
35
|
+
Selector. Each tag should have a stable `id` — without one, multi-select
|
|
36
|
+
add/remove will mis-match items with identical `text`.
|
|
37
|
+
- **No empty-state placeholder by default in the trigger.** When the
|
|
38
|
+
selection is empty, the trigger is empty space. Pass a `placeholder` so
|
|
39
|
+
the dropdown shows a hint, but the trigger label itself will not show
|
|
40
|
+
it (the `#trigger-label` slot renders an empty chip list).
|
|
41
|
+
- **Variant per chip is per-option.** Set `variant: "accent"` on individual
|
|
42
|
+
option objects to tint specific chips.
|
|
43
|
+
|
|
44
|
+
## Quick reference
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import type { TagProps } from "../components/Tag.vue";
|
|
49
|
+
|
|
50
|
+
const allCategories: TagProps[] = [
|
|
51
|
+
{ id: "fiction", text: "Fiction" },
|
|
52
|
+
{ id: "non-fiction", text: "Non-fiction", variant: "accent" },
|
|
53
|
+
{ id: "poetry", text: "Poetry" },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const selected = defineModel<TagProps[]>({ default: () => [] });
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<orio-taggable-selector
|
|
61
|
+
v-model="selected"
|
|
62
|
+
:options="allCategories"
|
|
63
|
+
:label="$t('book.categories')"
|
|
64
|
+
/>
|
|
65
|
+
</template>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- `<orio-selector>` — base; use with custom `#trigger-label` for
|
|
71
|
+
removable chips or different layouts.
|
|
72
|
+
- `<orio-tag>` — the chip primitive rendered in the trigger.
|
|
73
|
+
- Public API reference: `docs/components/taggable-selector.md`.
|
|
@@ -12,7 +12,13 @@ const props = defineProps({
|
|
|
12
12
|
label: { type: String, required: false },
|
|
13
13
|
layout: { type: String, required: false },
|
|
14
14
|
size: { type: String, required: false },
|
|
15
|
-
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 }
|
|
16
22
|
});
|
|
17
23
|
const modelValue = defineModel({ type: Array });
|
|
18
24
|
</script>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Form inputs
|
|
4
|
+
purpose: textarea, multi-line text, long text input
|
|
5
|
+
short: multi-line text input wrapping ControlElement; supports inner-floating label and vertical resize
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Textarea — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-textarea>` is the multi-line counterpart to `<orio-input>`. Same
|
|
12
|
+
ControlElement wrapping, same layout modes, same slot-bag flow. Read
|
|
13
|
+
`ControlElement.USAGE.md` and `Input.USAGE.md` first — most of the contract
|
|
14
|
+
lives there.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Extends `ControlProps`** with one override: `layout?: InputLayout`
|
|
19
|
+
where `InputLayout = ControlLayout | "inner"`. The `"inner"` mode floats
|
|
20
|
+
the label inside the textarea chrome (same trick as Input).
|
|
21
|
+
- **`layout="inner"` translates to `vertical`** on the inner ControlElement
|
|
22
|
+
and adds an `.inner` class. The label reposition is driven by `:deep()`
|
|
23
|
+
styles, not a duplicate label DOM.
|
|
24
|
+
- **v-model is `string`** (default `""`).
|
|
25
|
+
- **`rows="4"` is the hard default** rendered on the `<textarea>`. Override
|
|
26
|
+
via `$attrs` — `<orio-textarea :rows="2">` flows through.
|
|
27
|
+
- **`resize: vertical`** is set in CSS. The user can drag the bottom edge
|
|
28
|
+
but cannot resize horizontally.
|
|
29
|
+
- **Horizontal layout aligns the label to the top.** When `layout="horizontal"`,
|
|
30
|
+
the `.control-label` is padded with `--control-py` and the row uses
|
|
31
|
+
`align-items: flex-start` — so the label sits next to the top of a tall
|
|
32
|
+
textarea, not centered vertically.
|
|
33
|
+
- **`$attrs` is spread before the control bag** on the inner `<textarea>`:
|
|
34
|
+
`v-bind="{ ...$attrs, ...control }"`. Native attrs (`placeholder`,
|
|
35
|
+
`maxlength`, `rows`, `wrap`) work on `<orio-textarea>` and reach the
|
|
36
|
+
underlying element.
|
|
37
|
+
|
|
38
|
+
## Gotchas
|
|
39
|
+
|
|
40
|
+
- **Resize handle ignores `layout="inner"`.** The drag handle still appears
|
|
41
|
+
bottom-right — the inner label can overlap an aggressively resized
|
|
42
|
+
textarea's content. Cap `max-height` if that matters.
|
|
43
|
+
- **`rows` only sets the initial height** (in line-heights). After the
|
|
44
|
+
user resizes, the manual height wins. To reset, re-mount the component.
|
|
45
|
+
- **The textarea is `width: 100%`** of the slot wrapper, which itself
|
|
46
|
+
follows the wrapper border. Setting `cols` has no effect on rendered
|
|
47
|
+
width.
|
|
48
|
+
|
|
49
|
+
## Quick reference
|
|
50
|
+
|
|
51
|
+
```vue
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
const note = defineModel<string>({ default: "" });
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<template>
|
|
57
|
+
<orio-textarea
|
|
58
|
+
v-model="note"
|
|
59
|
+
:label="$t('order.notes.label')"
|
|
60
|
+
:placeholder="$t('order.notes.placeholder')"
|
|
61
|
+
layout="inner"
|
|
62
|
+
:rows="6"
|
|
63
|
+
maxlength="500"
|
|
64
|
+
/>
|
|
65
|
+
</template>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- `<orio-input>` — single-line text. Same contract.
|
|
71
|
+
- `<orio-control-element>` — the wrapper; owns label/error/a11y.
|
|
72
|
+
- Public API reference: `docs/components/textarea.md`.
|
|
@@ -8,18 +8,28 @@ const props = defineProps({
|
|
|
8
8
|
id: { type: String, required: false },
|
|
9
9
|
label: { type: String, required: false },
|
|
10
10
|
size: { type: String, required: false },
|
|
11
|
-
fill: { type: Boolean, required: false }
|
|
11
|
+
fill: { type: Boolean, required: false },
|
|
12
|
+
tabindex: { type: [Number, String], required: false },
|
|
13
|
+
focusKey: { type: String, required: false },
|
|
14
|
+
disabled: { type: Boolean, required: false },
|
|
15
|
+
required: { type: Boolean, required: false },
|
|
16
|
+
name: { type: String, required: false },
|
|
17
|
+
ariaLabel: { type: String, required: false }
|
|
12
18
|
});
|
|
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
|
-
<textarea
|
|
28
|
+
<textarea
|
|
29
|
+
v-model="modelValue"
|
|
30
|
+
rows="4"
|
|
31
|
+
v-bind="{ ...$attrs, ...control }"
|
|
32
|
+
/>
|
|
23
33
|
</orio-control-element>
|
|
24
34
|
</template>
|
|
25
35
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: tooltip, hover hint, focus hint, label-on-hover
|
|
5
|
+
short: hover/focus-triggered tooltip teleported to body, with delay, arrow, and four placements
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Tooltip — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-tooltip>` wraps a trigger in an `inline-flex` div and shows a small
|
|
12
|
+
floating bubble on hover or focus. It is not for click-driven menus — use
|
|
13
|
+
`<orio-popover>` for that.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Trigger wrapper is `display: inline-flex`** (centered). The slot lives
|
|
18
|
+
inside it. Block-level children are coerced into the flex layout.
|
|
19
|
+
- **Mouse + keyboard both trigger.** `@mouseenter`/`@focus` show,
|
|
20
|
+
`@mouseleave`/`@blur` hide. Touch is not supported.
|
|
21
|
+
- **`delay` (default 500 ms) gates open only.** Hide is immediate. Setting
|
|
22
|
+
`delay: 0` makes it instant.
|
|
23
|
+
- **`disabled` watcher closes an open tooltip.** Flipping to disabled mid-
|
|
24
|
+
display hides it. Re-enabling does not reopen — the user has to re-hover.
|
|
25
|
+
- **Two content sources, slot wins.** `#content` slot renders if provided,
|
|
26
|
+
otherwise the `text` prop. Both being empty renders an empty bubble.
|
|
27
|
+
- **Teleported to body only while visible.** Unlike Popover, the tooltip is
|
|
28
|
+
mounted/unmounted around the visible window — no leftover DOM at rest.
|
|
29
|
+
- **No placement fallback.** `placement` (`top`/`bottom`/`left`/`right`) is
|
|
30
|
+
honored as-is. If the bubble overflows the viewport, it stays offscreen.
|
|
31
|
+
Compare with Popover, which auto-flips.
|
|
32
|
+
- **Position is recalculated on scroll/resize** while visible (capture-phase
|
|
33
|
+
listeners catch nested scrollers).
|
|
34
|
+
- **`pointer-events: none` on the bubble.** It never intercepts the cursor —
|
|
35
|
+
so leaving the trigger always closes it.
|
|
36
|
+
- **Has a CSS-triangle arrow** (`.orio-tooltip-arrow-{placement}`) anchored
|
|
37
|
+
on the appropriate edge.
|
|
38
|
+
- **Styles are intentionally unscoped** in the second `<style>` block,
|
|
39
|
+
because teleported nodes escape scoped CSS. Class names use the
|
|
40
|
+
`orio-tooltip-` prefix to avoid collisions. Consumers **can** override
|
|
41
|
+
them globally — useful for theming, dangerous if not namespaced.
|
|
42
|
+
- **`white-space: nowrap`** on the bubble. Long `text` does not wrap. For
|
|
43
|
+
multi-line, render `#content` with your own line-breaking CSS.
|
|
44
|
+
- **A11y is partial.** The bubble has `role="tooltip"` and `aria-hidden`,
|
|
45
|
+
but the trigger does **not** receive `aria-describedby`. Screen readers
|
|
46
|
+
may not announce the tooltip. Wire `aria-describedby` on the trigger
|
|
47
|
+
child yourself if it matters.
|
|
48
|
+
|
|
49
|
+
## Gotchas
|
|
50
|
+
|
|
51
|
+
- **`text` defaults to English.** Project convention: pass an i18n key —
|
|
52
|
+
`:text="$t('action.delete.hint')"`.
|
|
53
|
+
- **`inline-flex` wrapper can change layout.** Wrapping a block-level
|
|
54
|
+
element (a card, a list row) in a Tooltip squashes it. Wrap a smaller
|
|
55
|
+
trigger (button, icon) instead.
|
|
56
|
+
- **`delay` does not debounce reopens.** Rapid hover toggles can still
|
|
57
|
+
flash the tooltip on the second mount if the first delay completed.
|
|
58
|
+
- **Z-index is `9999`** on the bubble — less than Popover (`999999`) and
|
|
59
|
+
Modal. Tooltips above an open popover may render behind it.
|
|
60
|
+
- **No click-to-dismiss.** Clicking the trigger does not close the bubble.
|
|
61
|
+
The user has to move focus or hover away.
|
|
62
|
+
|
|
63
|
+
## Quick reference
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<template>
|
|
67
|
+
<orio-tooltip :text="$t('action.delete.hint')" placement="top" :delay="200">
|
|
68
|
+
<orio-button icon-only icon="trash" @click="onDelete" />
|
|
69
|
+
</orio-tooltip>
|
|
70
|
+
|
|
71
|
+
<orio-tooltip placement="right">
|
|
72
|
+
<span>Hover me</span>
|
|
73
|
+
<template #content>
|
|
74
|
+
<strong>Custom</strong> content with <em>markup</em>.
|
|
75
|
+
</template>
|
|
76
|
+
</orio-tooltip>
|
|
77
|
+
</template>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Related
|
|
81
|
+
|
|
82
|
+
- `<orio-popover>` — click-driven anchored panel; do not reach for Tooltip
|
|
83
|
+
for menus.
|
|
84
|
+
- Public API reference: `docs/components/tooltip.md`.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Layout & containers
|
|
4
|
+
purpose: pinch/scroll zoom viewport, pan-zoom canvas, infinite board, image inspector
|
|
5
|
+
short: pan + pinch/wheel zoom viewport with inertia, momentum, space-to-grab and bounds clamping
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ZoomableContainer — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-zoomable-container>` is a pan-zoom viewport that transforms a "world"
|
|
12
|
+
(its slot) inside a fixed-size viewport. Pinch on touch, ctrl/cmd+wheel on
|
|
13
|
+
mouse, drag-to-pan with space-or-middle-button.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Viewport sizes to its parent.** The viewport is `width: 100%; height:
|
|
18
|
+
100%`. **A parent with explicit dimensions is required** — without it,
|
|
19
|
+
the viewport collapses to 0 and nothing is visible.
|
|
20
|
+
- **World is the slot.** It is `position: absolute; transform-origin: 0 0`.
|
|
21
|
+
The container measures it via `ResizeObserver`, so the slot can be any
|
|
22
|
+
size — fixed, content-driven, or dynamic.
|
|
23
|
+
- **First-mount auto-center is one-shot.** Once viewport and world both
|
|
24
|
+
have non-zero dimensions, the world centers exactly once. Later size
|
|
25
|
+
changes call `applyBounds` only — they do **not** re-center. Use the
|
|
26
|
+
exposed `centerWorld()` to re-center on demand.
|
|
27
|
+
- **Drag-to-pan needs space, middle button, OR clicking the viewport background.**
|
|
28
|
+
Pointer-down on slot content does **not** pan by default. Hold `Space`
|
|
29
|
+
(cursor becomes `grab`/`grabbing`) or click an area outside the world to
|
|
30
|
+
pan. This is `shouldPan(e)` — confirm interactive children inside the
|
|
31
|
+
slot stop propagation if needed.
|
|
32
|
+
- **Touch gestures use `usePinchZoom`**, mouse/pen uses pointer-capture
|
|
33
|
+
drag. Wheel does pan; **ctrl/cmd + wheel** zooms at cursor; **shift +
|
|
34
|
+
vertical wheel** pans horizontally.
|
|
35
|
+
- **`v-model:scale` works; `v-model:translate` does NOT.** `update:scale`
|
|
36
|
+
emits one value, but `update:translate` emits `(x, y)` as two args — not
|
|
37
|
+
a tuple. Bind a callback to read translate, or read it via `ref` on the
|
|
38
|
+
exposed `tx`/`ty`.
|
|
39
|
+
- **Exposed methods (via `defineExpose`)**: `scale`, `tx`, `ty`,
|
|
40
|
+
`setScaleAt(target, px, py)`, `panBy(dx, dy)`, `resetView()`,
|
|
41
|
+
`centerWorld()`. Get a `ref` on the component to call them.
|
|
42
|
+
- **Pan bounds let the world drift halfway off either edge.** Clamp is
|
|
43
|
+
`tx ∈ [vw/2 − worldW, vw/2]`, so the world can move until its trailing
|
|
44
|
+
edge reaches viewport center. Intentional — keeps something always
|
|
45
|
+
reachable.
|
|
46
|
+
- **`touch-action: none` and `user-select: none`** are set on the viewport.
|
|
47
|
+
Children inside cannot select text or trigger native touch scrolling.
|
|
48
|
+
- **Context menu is suppressed** inside the viewport (`@contextmenu.prevent`).
|
|
49
|
+
- **Global keydown listener for Space.** `useEventListener("keydown", ...)`
|
|
50
|
+
binds to `document`, so holding space in a text input elsewhere on the
|
|
51
|
+
page still flips `spaceHeld` — be aware when composing with form inputs.
|
|
52
|
+
|
|
53
|
+
## Gotchas
|
|
54
|
+
|
|
55
|
+
- **Slot prop shape**: `<template v-slot="{ scale, tx, ty }">`. Use these
|
|
56
|
+
for overlays that need to track the world transform (rulers, minimaps).
|
|
57
|
+
- **No `v-model:translate` shortcut.** Wire it as
|
|
58
|
+
`@update:translate="(x, y) => { ... }"`.
|
|
59
|
+
- **Pinch zoom resets `dragId` on `onPinchStart`** to cancel any in-flight
|
|
60
|
+
single-pointer drag. If you mix touch + pen on a hybrid device, the drag
|
|
61
|
+
state can drop mid-gesture.
|
|
62
|
+
- **`zoomSpeed` is exponential**, not linear. `factor = exp(-deltaY *
|
|
63
|
+
zoomSpeed)`. Default `0.0015` is tuned for typical mousewheel deltas;
|
|
64
|
+
trackpad wheel events with tiny deltas barely zoom — bump to ~0.005 if
|
|
65
|
+
your audience is trackpad-heavy.
|
|
66
|
+
- **`minScale` / `maxScale` are clamped, not normalized.** Setting
|
|
67
|
+
`initialScale` outside the range still applies the clamp on the first
|
|
68
|
+
zoom interaction.
|
|
69
|
+
|
|
70
|
+
## Quick reference
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
import { ref, useTemplateRef } from "vue";
|
|
75
|
+
|
|
76
|
+
const board = useTemplateRef<{ resetView: () => void; centerWorld: () => void }>("board");
|
|
77
|
+
const scale = ref(1);
|
|
78
|
+
const translate = ref({ x: 0, y: 0 });
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<template>
|
|
82
|
+
<div style="width: 100%; height: 80vh">
|
|
83
|
+
<orio-zoomable-container
|
|
84
|
+
ref="board"
|
|
85
|
+
v-model:scale="scale"
|
|
86
|
+
:min-scale="0.25"
|
|
87
|
+
:max-scale="4"
|
|
88
|
+
@update:translate="(x, y) => (translate = { x, y })"
|
|
89
|
+
>
|
|
90
|
+
<template #default="{ scale: worldScale }">
|
|
91
|
+
<div class="board-world" :style="{ width: '2000px', height: '1500px' }">
|
|
92
|
+
<p>Zoom: {{ worldScale.toFixed(2) }}×</p>
|
|
93
|
+
</div>
|
|
94
|
+
</template>
|
|
95
|
+
</orio-zoomable-container>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<orio-button @click="board?.resetView()">Reset</orio-button>
|
|
99
|
+
</template>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Related
|
|
103
|
+
|
|
104
|
+
- `usePinchZoom` — pinch gesture composable used internally.
|
|
105
|
+
- `useInertia` — momentum/decay used for release-after-drag.
|
|
106
|
+
- `<orio-canvas>` — when you need a tool-driven editor, not just a pan-zoom
|
|
107
|
+
viewport. Canvas is built on the same gestures with extras.
|
|
108
|
+
- Public API reference: `docs/components/zoomable-container.md`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Date
|
|
4
|
+
purpose: date input, single date picker, "pick a date"
|
|
5
|
+
short: single date picker built from Calendar plus PickerTrigger
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# date/Picker — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-date-picker>` is the single-date picker: a `<orio-date-picker-trigger>`
|
|
12
|
+
button that opens a popover containing `<orio-calendar>`. For ranges use
|
|
13
|
+
`<orio-date-range-picker>`.
|
|
14
|
+
|
|
15
|
+
## Invariants
|
|
16
|
+
|
|
17
|
+
- **Extends `ControlProps`.** Pass `label`, `error`, `size`, `disabled`,
|
|
18
|
+
`required` straight through — they reach the trigger via ControlElement.
|
|
19
|
+
See `ControlElement.USAGE.md`.
|
|
20
|
+
- **v-model is `string | null`** in ISO `YYYY-MM-DD` form. `null` means
|
|
21
|
+
unpicked.
|
|
22
|
+
- **`min` / `max`** are ISO strings. They merge with the consumer's
|
|
23
|
+
`isDisabled(iso)` callback — Picker's `calendarIsDisabled` returns true
|
|
24
|
+
when either the min/max bound is violated OR the consumer says so.
|
|
25
|
+
- **Selecting a day closes the popover** automatically (`toggle(false)` is
|
|
26
|
+
called inside the `@select` handler). Do not wire your own close.
|
|
27
|
+
- **Markers + getMarker** are forwarded to the inner Calendar unchanged.
|
|
28
|
+
See `Calendar.USAGE.md` for the matching rules.
|
|
29
|
+
- **Placeholder text** falls back to the i18n key `datePicker.placeholder`
|
|
30
|
+
if no `placeholder` prop is given.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- The trigger displays the date via `formatDate(value, locale)` from
|
|
35
|
+
`../../utils/date`. If you need a custom display format, wrap the
|
|
36
|
+
picker — the prop is not exposed.
|
|
37
|
+
- `<orio-date-picker-trigger>` is the popover host. Wiring up your own
|
|
38
|
+
trigger means re-creating popover focus management — prefer composing
|
|
39
|
+
with the existing trigger via its `#default` scoped slot if you need
|
|
40
|
+
custom calendar content (this is exactly how Picker itself works).
|
|
41
|
+
|
|
42
|
+
## Quick reference
|
|
43
|
+
|
|
44
|
+
```vue
|
|
45
|
+
<orio-date-picker
|
|
46
|
+
v-model="checkInDate"
|
|
47
|
+
label="Check-in"
|
|
48
|
+
:min="todayIso"
|
|
49
|
+
:max="maxBookingIso"
|
|
50
|
+
:is-disabled="(iso) => blockedDates.has(iso)"
|
|
51
|
+
/>
|
|
52
|
+
```
|
|
@@ -16,7 +16,13 @@ const props = defineProps({
|
|
|
16
16
|
label: { type: String, required: false },
|
|
17
17
|
layout: { type: String, required: false },
|
|
18
18
|
size: { type: String, required: false },
|
|
19
|
-
fill: { type: Boolean, required: false }
|
|
19
|
+
fill: { type: Boolean, required: false },
|
|
20
|
+
tabindex: { type: [Number, String], required: false },
|
|
21
|
+
focusKey: { type: String, required: false },
|
|
22
|
+
disabled: { type: Boolean, required: false },
|
|
23
|
+
required: { type: Boolean, required: false },
|
|
24
|
+
name: { type: String, required: false },
|
|
25
|
+
ariaLabel: { type: String, required: false }
|
|
20
26
|
});
|
|
21
27
|
const value = defineModel({ type: [String, null], ...{ default: null } });
|
|
22
28
|
const { locale, t } = useI18n();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
kind: component
|
|
3
|
+
category: Date
|
|
4
|
+
purpose: date picker trigger button, date input button, popover-anchored date trigger
|
|
5
|
+
short: shared button + popover trigger used by date Picker and RangePicker; default slot renders the picker body
|
|
6
|
+
invariants: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# date/PickerTrigger — agent-only invariants
|
|
10
|
+
|
|
11
|
+
`<orio-date-picker-trigger>` is the trigger button shared by
|
|
12
|
+
`<orio-date-picker>` and `<orio-date-range-picker>`. You usually do not
|
|
13
|
+
use it directly — pick those higher-level pickers unless you are building
|
|
14
|
+
a new date primitive.
|
|
15
|
+
|
|
16
|
+
## Invariants
|
|
17
|
+
|
|
18
|
+
- **Renders a `<button>` inside `<orio-control-element>` and an
|
|
19
|
+
`<orio-popover>`** with `position="bottom-right"`, `offset: 5`.
|
|
20
|
+
- **`text` prop is the visible display text.** When empty, `placeholder`
|
|
21
|
+
renders muted.
|
|
22
|
+
- **`text` and `placeholder` are stripped from `controlProps`** — they
|
|
23
|
+
don't leak to the ControlElement wrapper.
|
|
24
|
+
- **Default slot is the popover content** and receives `{ toggle }`. Call
|
|
25
|
+
`toggle(false)` to close after a user picks.
|
|
26
|
+
- **Calendar icon (`name="calendar"`) is hardcoded** on the right of the
|
|
27
|
+
button. No prop to swap it.
|
|
28
|
+
- **`aria-expanded` reflects popover state** for screen reader support.
|
|
29
|
+
- **Inherits all ControlElement contract**: `label`, `error`, `size`,
|
|
30
|
+
`layout`, etc. The control bag is bound to the inner `<button>`.
|
|
31
|
+
|
|
32
|
+
## Gotchas
|
|
33
|
+
|
|
34
|
+
- **Not for general "click-to-open" needs.** For a non-date trigger, use
|
|
35
|
+
`<orio-popover>` directly — this one is calendar-themed (icon, padding,
|
|
36
|
+
i18n placeholder).
|
|
37
|
+
- **No multi-popover stacking story.** Both single and range pickers use
|
|
38
|
+
this same component, with the same `bottom-right` placement. Side-by-
|
|
39
|
+
side pickers may collide.
|
|
40
|
+
|
|
41
|
+
## Quick reference
|
|
42
|
+
|
|
43
|
+
You normally consume this through `<orio-date-picker>` or
|
|
44
|
+
`<orio-date-range-picker>`. Direct use:
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<template>
|
|
48
|
+
<orio-date-picker-trigger
|
|
49
|
+
:text="display"
|
|
50
|
+
:placeholder="$t('date.placeholder')"
|
|
51
|
+
:label="$t('date.label')"
|
|
52
|
+
>
|
|
53
|
+
<template #default="{ toggle }">
|
|
54
|
+
<orio-calendar v-model:anchor="anchor" @select="onSelect($event, toggle)" />
|
|
55
|
+
</template>
|
|
56
|
+
</orio-date-picker-trigger>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Related
|
|
61
|
+
|
|
62
|
+
- `<orio-date-picker>` — single-date picker built on this trigger.
|
|
63
|
+
- `<orio-date-range-picker>` — range picker built on this trigger.
|
|
64
|
+
- `<orio-popover>` — for non-date trigger needs.
|
|
65
|
+
- Public API reference: `docs/components/date/`.
|
|
@@ -10,7 +10,13 @@ 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
|
const controlProps = computed(() => {
|
|
16
22
|
const { text, placeholder, ...rest } = props;
|
|
@@ -19,11 +25,11 @@ const controlProps = computed(() => {
|
|
|
19
25
|
</script>
|
|
20
26
|
|
|
21
27
|
<template>
|
|
22
|
-
<orio-control-element v-slot="{
|
|
28
|
+
<orio-control-element v-slot="{ control }" v-bind="controlProps">
|
|
23
29
|
<orio-popover position="bottom-right" :offset="5">
|
|
24
30
|
<template #default="{ toggle, isOpen }">
|
|
25
31
|
<button
|
|
26
|
-
|
|
32
|
+
v-bind="control"
|
|
27
33
|
type="button"
|
|
28
34
|
class="date-trigger"
|
|
29
35
|
:aria-expanded="isOpen"
|