iryx-ui 0.3.0 → 0.5.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 +129 -27
- package/dist/component-names.d.ts +1 -1
- package/dist/components/App.vue.d.ts +1 -1
- package/dist/components/Combobox.vue.d.ts +112 -0
- package/dist/components/DropdownMenu.vue.d.ts +1 -1
- package/dist/components/FormField.vue.d.ts +0 -9
- package/dist/components/Label.vue.d.ts +0 -6
- package/dist/components/NumberInput.vue.d.ts +63 -0
- package/dist/components/Select.vue.d.ts +13 -2
- package/dist/components/index.d.ts +2 -0
- package/dist/composables/decimal.d.ts +53 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +49 -44
- package/dist/nuxt.d.ts +1 -1
- package/dist/packages/iryx-ui/src/component-names.js +1 -1
- package/dist/packages/iryx-ui/src/components/Combobox.js +5 -0
- package/dist/packages/iryx-ui/src/components/Combobox.vue_vue_type_script_setup_true_lang.js +284 -0
- package/dist/packages/iryx-ui/src/components/FormField.vue_vue_type_script_setup_true_lang.js +3 -5
- package/dist/packages/iryx-ui/src/components/Label.vue_vue_type_script_setup_true_lang.js +0 -2
- package/dist/packages/iryx-ui/src/components/NumberInput.js +5 -0
- package/dist/packages/iryx-ui/src/components/NumberInput.vue_vue_type_script_setup_true_lang.js +136 -0
- package/dist/packages/iryx-ui/src/components/Select.vue_vue_type_script_setup_true_lang.js +72 -27
- package/dist/packages/iryx-ui/src/components/index.js +48 -44
- package/dist/packages/iryx-ui/src/composables/decimal.js +79 -0
- package/dist/packages/iryx-ui/src/theme/alert.js +2 -2
- package/dist/packages/iryx-ui/src/theme/button.js +2 -2
- package/dist/packages/iryx-ui/src/theme/card.js +1 -1
- package/dist/packages/iryx-ui/src/theme/checkbox.js +1 -1
- package/dist/packages/iryx-ui/src/theme/combobox.js +29 -0
- package/dist/packages/iryx-ui/src/theme/dialog.js +2 -2
- package/dist/packages/iryx-ui/src/theme/dropdown-menu.js +4 -4
- package/dist/packages/iryx-ui/src/theme/form.js +10 -34
- package/dist/packages/iryx-ui/src/theme/input.js +1 -1
- package/dist/packages/iryx-ui/src/theme/label.js +1 -10
- package/dist/packages/iryx-ui/src/theme/number-input.js +49 -0
- package/dist/packages/iryx-ui/src/theme/pagination.js +1 -1
- package/dist/packages/iryx-ui/src/theme/presets.js +0 -42
- package/dist/packages/iryx-ui/src/theme/radio-group.js +1 -1
- package/dist/packages/iryx-ui/src/theme/select.js +5 -3
- package/dist/packages/iryx-ui/src/theme/skeleton.js +1 -1
- package/dist/packages/iryx-ui/src/theme/stepper.js +1 -1
- package/dist/packages/iryx-ui/src/theme/switch.js +1 -1
- package/dist/packages/iryx-ui/src/theme/tabs.js +3 -3
- package/dist/packages/iryx-ui/src/theme/toast.js +3 -3
- package/dist/packages/iryx-ui/src/theme/tooltip.js +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/theme/button.d.ts +2 -2
- package/dist/theme/combobox.d.ts +101 -0
- package/dist/theme/form.d.ts +35 -62
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/input.d.ts +5 -5
- package/dist/theme/label.d.ts +0 -18
- package/dist/theme/number-input.d.ts +92 -0
- package/dist/theme/presets.d.ts +0 -42
- package/dist/theme/select.d.ts +6 -0
- package/package.json +1 -1
- package/theme.css +14 -0
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ and every component updates.
|
|
|
104
104
|
|
|
105
105
|
```vue
|
|
106
106
|
<template>
|
|
107
|
-
<IApp theme="
|
|
107
|
+
<IApp theme="rose" appearance="system" dir="ltr">
|
|
108
108
|
<RouterView />
|
|
109
109
|
</IApp>
|
|
110
110
|
</template>
|
|
@@ -165,14 +165,14 @@ utilities (shadcn-style `@custom-variant dark`).
|
|
|
165
165
|
|
|
166
166
|
## Theming
|
|
167
167
|
|
|
168
|
-
Pick a built-in color preset — `violet` (default)
|
|
169
|
-
|
|
168
|
+
Pick a built-in color preset — `violet` (default) or `rose` — at startup or at
|
|
169
|
+
runtime:
|
|
170
170
|
|
|
171
171
|
```ts
|
|
172
172
|
import { applyTheme } from 'iryx-ui'
|
|
173
173
|
|
|
174
|
-
app.use(createIryxUi({ theme: '
|
|
175
|
-
// nuxt.config.ts → iryxUi: { theme: '
|
|
174
|
+
app.use(createIryxUi({ theme: 'rose' }))
|
|
175
|
+
// nuxt.config.ts → iryxUi: { theme: 'rose' }
|
|
176
176
|
|
|
177
177
|
applyTheme('rose') // runtime, e.g. from a theme picker
|
|
178
178
|
```
|
|
@@ -202,10 +202,19 @@ Available tokens, each usable as a Tailwind color (`bg-primary`,
|
|
|
202
202
|
|
|
203
203
|
| Group | Tokens |
|
|
204
204
|
| --- | --- |
|
|
205
|
-
| Surfaces | `background`, `foreground`, `accent`, `accent-foreground`, `muted`, `muted-foreground`, `border` |
|
|
205
|
+
| Surfaces | `background`, `foreground`, `accent`, `accent-foreground`, `muted`, `muted-foreground`, `border`, `input` |
|
|
206
206
|
| Brand | `primary`, `primary-foreground`, `primary-from`, `primary-to` |
|
|
207
207
|
| Status | `success`, `warning`, `danger`, `info` — each with `-foreground`, `-muted`, `-muted-foreground` and `-border` |
|
|
208
208
|
|
|
209
|
+
`input` is the fill behind the fields — `IInput`, `ITextarea`, `INumberInput`,
|
|
210
|
+
the `ISelect` trigger, the `ICombobox` anchor — and behind `IButton`'s
|
|
211
|
+
`outline` variant, so the two read as the same kind of surface. It's a token
|
|
212
|
+
of its own rather than a reused `muted`, so how raised a control looks can be
|
|
213
|
+
tuned without moving every muted surface with it. It matches the page
|
|
214
|
+
background in light mode and lifts off it in dark, where the difference is
|
|
215
|
+
legible; that split lives in the token values, so no component needs a `dark:`
|
|
216
|
+
class for it.
|
|
217
|
+
|
|
209
218
|
`primary-from` / `primary-to` are the stops of the solid button's vertical
|
|
210
219
|
gradient. The status tokens carry their own dark-mode values, so components
|
|
211
220
|
never need a `dark:` class for them: `success` is the solid fill,
|
|
@@ -262,10 +271,12 @@ app.use(createIryxUi({ unstyled: true }))
|
|
|
262
271
|
| `IForm` | Validating form wrapper — any Standard Schema validator, or your own function |
|
|
263
272
|
| `IFormField` | Label, description, hint, help and error text around a control |
|
|
264
273
|
| `IInput` | Text field with `sm`/`md`/`lg` sizes, `invalid` state, `v-model` |
|
|
274
|
+
| `INumberInput` | Decimal-safe numeric field — the model is a **string**, with `min`/`max`/`step`, `precision` and locale-aware display |
|
|
265
275
|
| `ITextarea` | Multi-line field with matching sizes and `invalid` state |
|
|
266
|
-
| `ILabel` | Field label with optional `required` asterisk
|
|
276
|
+
| `ILabel` | Field label with optional `required` asterisk |
|
|
267
277
|
| `ICheckbox` | Tri-state checkbox (`true` / `false` / `'indeterminate'`), optional `label` + `description` |
|
|
268
|
-
| `ISelect` | Listbox with keyboard nav and typeahead, driven by an `items` array |
|
|
278
|
+
| `ISelect` | Listbox with keyboard nav and typeahead, driven by an `items` array, with optional groups |
|
|
279
|
+
| `ICombobox` | Searchable select — filters as you type, with optional groups, virtualized rows and a "create from query" row |
|
|
269
280
|
| `IRadioGroup` | Radio list with labels wired up automatically; items take a `description` |
|
|
270
281
|
| `ISwitch` | Accessible toggle, optional `label` + `description` |
|
|
271
282
|
|
|
@@ -405,6 +416,116 @@ const framework = ref('vue')
|
|
|
405
416
|
|
|
406
417
|
`ISelect` and `IRadioGroup` accept plain strings or `{ label, value, disabled }` objects. Both also take a default slot if you'd rather compose the Reka primitives yourself.
|
|
407
418
|
|
|
419
|
+
`ISelect` also takes groups — an entry with its own `items` becomes a labelled
|
|
420
|
+
heading, the same shape `ICombobox` uses:
|
|
421
|
+
|
|
422
|
+
```vue
|
|
423
|
+
<ISelect
|
|
424
|
+
v-model="framework"
|
|
425
|
+
:items="[
|
|
426
|
+
{ label: 'Virtual DOM', items: ['Vue', 'React'] },
|
|
427
|
+
{ label: 'Compiled', items: [{ label: 'Svelte', value: 'svelte' }] },
|
|
428
|
+
]"
|
|
429
|
+
/>
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Searchable selects
|
|
433
|
+
|
|
434
|
+
`ICombobox` takes the same `items` as `ISelect` and filters them against what
|
|
435
|
+
the user types, which is what you want once a list runs to hundreds of entries.
|
|
436
|
+
The field shows the selected option's **label** while the model holds its value.
|
|
437
|
+
|
|
438
|
+
```vue
|
|
439
|
+
<ICombobox
|
|
440
|
+
v-model="clientId"
|
|
441
|
+
:items="clients"
|
|
442
|
+
placeholder="Search clients"
|
|
443
|
+
empty-text="No clients found."
|
|
444
|
+
/>
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Set `create` to offer a row for whatever the user typed when nothing matches.
|
|
448
|
+
Choosing it emits `create` with the query and does **not** change the model —
|
|
449
|
+
the option doesn't exist yet, so you add it and select it yourself:
|
|
450
|
+
|
|
451
|
+
```vue
|
|
452
|
+
<ICombobox
|
|
453
|
+
v-model="clientId"
|
|
454
|
+
:items="clients"
|
|
455
|
+
create
|
|
456
|
+
:create-label="query => `Add ${query}`"
|
|
457
|
+
@create="query => clients.push({ label: query, value: addClient(query) })"
|
|
458
|
+
/>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Both `empty-text` and `create-label` are props precisely so a non-English app
|
|
462
|
+
never inherits an English string; `empty` and `create` slots take over the
|
|
463
|
+
markup entirely if you need more than text.
|
|
464
|
+
|
|
465
|
+
An entry with its own `items` becomes a labelled group. A group disappears on
|
|
466
|
+
its own once nothing inside it matches:
|
|
467
|
+
|
|
468
|
+
```vue
|
|
469
|
+
<ICombobox
|
|
470
|
+
v-model="clientId"
|
|
471
|
+
:items="[
|
|
472
|
+
{ label: 'Recent', items: ['Acme Industries', 'Bolt Logistics'] },
|
|
473
|
+
{ label: 'Archived', items: [{ label: 'Cirrus Systems', value: 'cirrus' }] },
|
|
474
|
+
]"
|
|
475
|
+
/>
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
For lists in the thousands, `virtual` renders only the rows on screen:
|
|
479
|
+
|
|
480
|
+
```vue
|
|
481
|
+
<ICombobox v-model="sku" virtual :items="fiveThousandItems" :estimate-size="32" />
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
`virtual` and grouped items are **mutually exclusive** — the underlying
|
|
485
|
+
virtualizer is a flat window with no notion of group headings, so groups are
|
|
486
|
+
flattened and their labels dropped (with a warning in dev). `estimate-size` is
|
|
487
|
+
the assumed row height in px, used to size the scrollbar before rows are
|
|
488
|
+
measured; set it if you have restyled rows to a different height.
|
|
489
|
+
|
|
490
|
+
### Numbers and money
|
|
491
|
+
|
|
492
|
+
`INumberInput` never turns your value into a `number`. The model is a decimal
|
|
493
|
+
**string**, because binary floating point cannot represent decimal money —
|
|
494
|
+
`0.1 + 0.2` is `0.30000000000000004`, and `10.00` becomes `10`. Values are
|
|
495
|
+
added, compared and rounded with `BigInt` internally, so precision survives
|
|
496
|
+
regardless of magnitude.
|
|
497
|
+
|
|
498
|
+
```vue
|
|
499
|
+
<script setup lang="ts">
|
|
500
|
+
import { ref } from 'vue'
|
|
501
|
+
|
|
502
|
+
// A string, and it stays one.
|
|
503
|
+
const amount = ref('1234.56')
|
|
504
|
+
</script>
|
|
505
|
+
|
|
506
|
+
<template>
|
|
507
|
+
<INumberInput v-model="amount" locale="sl" :precision="2" step="0.01" min="0" />
|
|
508
|
+
</template>
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
`locale` affects the **display only** — `sl` shows `1.234,56` while the model
|
|
512
|
+
stays `"1234.56"`. Typing in the locale's own format works too. While the field
|
|
513
|
+
is focused it shows the canonical value so separators can't fight your typing.
|
|
514
|
+
|
|
515
|
+
`precision` fixes the number of decimal places, rounding half-up, and preserves
|
|
516
|
+
trailing zeros (`"10.00"` stays `"10.00"`). `min`, `max` and `step` are decimal
|
|
517
|
+
strings as well, and stepping is exact: `0.1 + 0.2` gives `"0.3"`.
|
|
518
|
+
|
|
519
|
+
The underlying helpers are exported if you need them elsewhere:
|
|
520
|
+
|
|
521
|
+
```ts
|
|
522
|
+
import { addDecimals, compareDecimals, roundDecimal } from 'iryx-ui'
|
|
523
|
+
|
|
524
|
+
addDecimals('0.1', '0.2') // '0.3'
|
|
525
|
+
roundDecimal('1.005', 2) // '1.01'
|
|
526
|
+
compareDecimals('1.10', '1.1') // 0
|
|
527
|
+
```
|
|
528
|
+
|
|
408
529
|
### Validated forms
|
|
409
530
|
|
|
410
531
|
`IForm` handles client-side validation. It accepts any [Standard Schema](https://standardschema.dev) validator — Zod 3.24+, Valibot, ArkType — so Iryx doesn't depend on a validation library. Wrap each control in an `IFormField` with a `name` matching the schema path and errors wire themselves up.
|
|
@@ -456,25 +577,6 @@ The control inside a field automatically inherits its `id`, invalid styling and
|
|
|
456
577
|
|
|
457
578
|
**Server errors and manual control** — grab a template ref to the form and call `validate()`, `clear(name?)` or `setErrors()`. `IFormField` also takes a plain `error` prop that bypasses validation entirely.
|
|
458
579
|
|
|
459
|
-
### Label alignment
|
|
460
|
-
|
|
461
|
-
`ILabel` and `IFormField` indent their text so it lines up with the control's
|
|
462
|
-
text rather than its outer edge, putting the label directly above the
|
|
463
|
-
placeholder. The offset matches the input's horizontal padding — `sm` 10px,
|
|
464
|
-
`md` 12px, `lg` 16px — so pass the same size you gave the control:
|
|
465
|
-
|
|
466
|
-
```vue
|
|
467
|
-
<template>
|
|
468
|
-
<ILabel for="amount" indent="lg">
|
|
469
|
-
Amount
|
|
470
|
-
</ILabel>
|
|
471
|
-
<IInput id="amount" size="lg" />
|
|
472
|
-
</template>
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
Use `indent="none"` when the label wraps its control, as with a checkbox, since
|
|
476
|
-
there is no text to line up with.
|
|
477
|
-
|
|
478
580
|
### Labels and descriptions
|
|
479
581
|
|
|
480
582
|
`ICheckbox` and `ISwitch` render bare by default. Give them a `label` and/or `description` and they render a wired-up layout instead — the text is clickable, and the description is linked with `aria-describedby`. `IRadioGroup` items take a `description` too.
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* Consumed by the Vue plugin (global registration) and the Nuxt module
|
|
4
4
|
* (auto-imports). Keep in sync with `src/components/index.ts`.
|
|
5
5
|
*/
|
|
6
|
-
export declare const componentNames: readonly ["Alert", "App", "Badge", "Breadcrumb", "Button", "ButtonGroup", "Card", "Checkbox", "ConfirmDialog", "Dialog", "DropdownMenu", "EmptyState", "Form", "FormField", "Input", "Label", "Pagination", "Progress", "RadioGroup", "Select", "Separator", "Skeleton", "Stat", "Stepper", "Switch", "Tabs", "Textarea", "Toaster", "Tooltip"];
|
|
6
|
+
export declare const componentNames: readonly ["Alert", "App", "Badge", "Breadcrumb", "Button", "ButtonGroup", "Card", "Checkbox", "Combobox", "ConfirmDialog", "Dialog", "DropdownMenu", "EmptyState", "Form", "FormField", "Input", "Label", "NumberInput", "Pagination", "Progress", "RadioGroup", "Select", "Separator", "Skeleton", "Stat", "Stepper", "Switch", "Tabs", "Textarea", "Toaster", "Tooltip"];
|
|
7
7
|
export type ComponentName = (typeof componentNames)[number];
|
|
@@ -7,7 +7,7 @@ export interface AppProps extends /* @vue-ignore */ ConfigProviderProps {
|
|
|
7
7
|
* below. Reactive, unlike the install-time plugin option.
|
|
8
8
|
*/
|
|
9
9
|
unstyled?: boolean;
|
|
10
|
-
/** Colour theme: a preset name (`'
|
|
10
|
+
/** Colour theme: a preset name (`'violet'`, `'rose'`) or a custom theme. */
|
|
11
11
|
theme?: Theme | ThemePresetName;
|
|
12
12
|
/** Startup appearance. A preference the user already stored wins over this. */
|
|
13
13
|
appearance?: Appearance;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { AcceptableValue, ComboboxRootProps } from 'reka-ui';
|
|
2
|
+
export interface ComboboxItemOption {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** A labelled run of options. Reka hides the whole group once nothing in it matches. */
|
|
8
|
+
export interface ComboboxItemGroup {
|
|
9
|
+
label: string;
|
|
10
|
+
items: (ComboboxItemOption | string)[];
|
|
11
|
+
}
|
|
12
|
+
export type ComboboxItems = (ComboboxItemOption | ComboboxItemGroup | string)[];
|
|
13
|
+
export interface ComboboxProps extends ComboboxRootProps {
|
|
14
|
+
/**
|
|
15
|
+
* Options to render. Strings are expanded to `{ label, value }`, and an
|
|
16
|
+
* entry with its own `items` becomes a labelled group.
|
|
17
|
+
*/
|
|
18
|
+
items?: ComboboxItems;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
size?: 'sm' | 'md' | 'lg';
|
|
21
|
+
/** Mark the field as invalid — styles the border and ring red. */
|
|
22
|
+
invalid?: boolean;
|
|
23
|
+
id?: string;
|
|
24
|
+
/** Shown when the query matches nothing. Override for non-English apps. */
|
|
25
|
+
emptyText?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Offer a "create" row when the query matches no option's label, for
|
|
28
|
+
* adding a client or item without leaving the field. Selecting it emits
|
|
29
|
+
* `create` with the query — the value is *not* set, since the option
|
|
30
|
+
* doesn't exist yet; add it to `items` and set the model yourself.
|
|
31
|
+
*/
|
|
32
|
+
create?: boolean;
|
|
33
|
+
/** Label for that row. Receives the current query. */
|
|
34
|
+
createLabel?: (query: string) => string;
|
|
35
|
+
/**
|
|
36
|
+
* Render only the rows in view. Worth it in the thousands, not the hundreds.
|
|
37
|
+
* Groups are flattened — Reka's virtualizer is a flat window — so `virtual`
|
|
38
|
+
* and grouped `items` are mutually exclusive.
|
|
39
|
+
*/
|
|
40
|
+
virtual?: boolean;
|
|
41
|
+
/** Row height in px, used to size the scrollbar before rows are measured. */
|
|
42
|
+
estimateSize?: number;
|
|
43
|
+
/** Rows rendered beyond the viewport on each side. */
|
|
44
|
+
overscan?: number;
|
|
45
|
+
/** Skip built-in classes; you take over styling entirely. */
|
|
46
|
+
unstyled?: boolean;
|
|
47
|
+
class?: string;
|
|
48
|
+
ui?: {
|
|
49
|
+
anchor?: string;
|
|
50
|
+
input?: string;
|
|
51
|
+
trigger?: string;
|
|
52
|
+
content?: string;
|
|
53
|
+
viewport?: string;
|
|
54
|
+
item?: string;
|
|
55
|
+
empty?: string;
|
|
56
|
+
group?: string;
|
|
57
|
+
groupLabel?: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
declare var __VLS_50: {
|
|
61
|
+
query: string;
|
|
62
|
+
}, __VLS_58: {
|
|
63
|
+
query: string;
|
|
64
|
+
}, __VLS_60: {}, __VLS_139: {
|
|
65
|
+
query: string;
|
|
66
|
+
};
|
|
67
|
+
type __VLS_Slots = {} & {
|
|
68
|
+
empty?: (props: typeof __VLS_50) => any;
|
|
69
|
+
} & {
|
|
70
|
+
empty?: (props: typeof __VLS_58) => any;
|
|
71
|
+
} & {
|
|
72
|
+
default?: (props: typeof __VLS_60) => any;
|
|
73
|
+
} & {
|
|
74
|
+
create?: (props: typeof __VLS_139) => any;
|
|
75
|
+
};
|
|
76
|
+
declare const __VLS_base: import("vue").DefineComponent<ComboboxProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
77
|
+
"update:modelValue": (value: AcceptableValue) => any;
|
|
78
|
+
create: (query: string) => any;
|
|
79
|
+
highlight: (payload: {
|
|
80
|
+
ref: HTMLElement;
|
|
81
|
+
value: AcceptableValue;
|
|
82
|
+
} | undefined) => any;
|
|
83
|
+
"update:open": (value: boolean) => any;
|
|
84
|
+
}, string, import("vue").PublicProps, Readonly<ComboboxProps> & Readonly<{
|
|
85
|
+
"onUpdate:modelValue"?: ((value: AcceptableValue) => any) | undefined;
|
|
86
|
+
onCreate?: ((query: string) => any) | undefined;
|
|
87
|
+
onHighlight?: ((payload: {
|
|
88
|
+
ref: HTMLElement;
|
|
89
|
+
value: AcceptableValue;
|
|
90
|
+
} | undefined) => any) | undefined;
|
|
91
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
92
|
+
}>, {
|
|
93
|
+
unstyled: boolean;
|
|
94
|
+
disabled: boolean;
|
|
95
|
+
invalid: boolean;
|
|
96
|
+
emptyText: string;
|
|
97
|
+
create: boolean;
|
|
98
|
+
createLabel: (query: string) => string;
|
|
99
|
+
virtual: boolean;
|
|
100
|
+
estimateSize: number;
|
|
101
|
+
overscan: number;
|
|
102
|
+
open: boolean;
|
|
103
|
+
defaultOpen: boolean;
|
|
104
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
105
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
106
|
+
declare const _default: typeof __VLS_export;
|
|
107
|
+
export default _default;
|
|
108
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
109
|
+
new (): {
|
|
110
|
+
$slots: S;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
@@ -36,8 +36,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
36
36
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
37
37
|
}>, {
|
|
38
38
|
unstyled: boolean;
|
|
39
|
-
align: "start" | "center" | "end";
|
|
40
39
|
sideOffset: number;
|
|
40
|
+
align: "start" | "center" | "end";
|
|
41
41
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
42
42
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
43
43
|
declare const _default: typeof __VLS_export;
|
|
@@ -11,15 +11,6 @@ export interface FormFieldProps {
|
|
|
11
11
|
required?: boolean;
|
|
12
12
|
/** Force an error message, bypassing the form's validation. */
|
|
13
13
|
error?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Indent the label, description, error and help text so they line up with
|
|
16
|
-
* the control's own text rather than its outer edge.
|
|
17
|
-
*
|
|
18
|
-
* The values match the input's horizontal padding, so pass the same size you
|
|
19
|
-
* gave the control. Use `none` for controls that draw their own label, like
|
|
20
|
-
* a checkbox or switch.
|
|
21
|
-
*/
|
|
22
|
-
indent?: 'none' | 'sm' | 'md' | 'lg';
|
|
23
14
|
/** Skip built-in classes; you take over styling entirely. */
|
|
24
15
|
unstyled?: boolean;
|
|
25
16
|
class?: string;
|
|
@@ -2,12 +2,6 @@ import type { LabelProps as RekaLabelProps } from 'reka-ui';
|
|
|
2
2
|
export interface LabelProps extends RekaLabelProps {
|
|
3
3
|
/** Append a red asterisk to mark the associated field as required. */
|
|
4
4
|
required?: boolean;
|
|
5
|
-
/**
|
|
6
|
-
* Indent the label to line up with the control's text rather than its outer
|
|
7
|
-
* edge. Match it to the control's size; use `none` when the label wraps its
|
|
8
|
-
* control, as with a checkbox.
|
|
9
|
-
*/
|
|
10
|
-
indent?: 'none' | 'sm' | 'md' | 'lg';
|
|
11
5
|
/** Skip built-in classes; you take over styling entirely. */
|
|
12
6
|
unstyled?: boolean;
|
|
13
7
|
class?: string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface NumberInputProps {
|
|
2
|
+
/** Smallest allowed value, as a decimal string. */
|
|
3
|
+
min?: string;
|
|
4
|
+
/** Largest allowed value, as a decimal string. */
|
|
5
|
+
max?: string;
|
|
6
|
+
/** Amount the stepper adds or subtracts, as a decimal string. */
|
|
7
|
+
step?: string;
|
|
8
|
+
/** Fixed decimal places. Values are rounded half-up to this scale. */
|
|
9
|
+
precision?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Locale used for the *display* only — `sl` shows `1.234,56`. The model
|
|
12
|
+
* stays canonical (`"1234.56"`) whatever the locale.
|
|
13
|
+
*/
|
|
14
|
+
locale?: string;
|
|
15
|
+
/** Show the stacked increment/decrement controls. */
|
|
16
|
+
stepper?: boolean;
|
|
17
|
+
size?: 'sm' | 'md' | 'lg';
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
required?: boolean;
|
|
21
|
+
/** Mark the field as invalid — styles the border and ring red. */
|
|
22
|
+
invalid?: boolean;
|
|
23
|
+
id?: string;
|
|
24
|
+
/** Accessible names for the stepper controls — override for non-English apps. */
|
|
25
|
+
incrementLabel?: string;
|
|
26
|
+
decrementLabel?: string;
|
|
27
|
+
/** Skip built-in classes; you take over styling entirely. */
|
|
28
|
+
unstyled?: boolean;
|
|
29
|
+
class?: string;
|
|
30
|
+
/** Override classes per slot, e.g. `{ step: 'px-2' }`. */
|
|
31
|
+
ui?: {
|
|
32
|
+
root?: string;
|
|
33
|
+
input?: string;
|
|
34
|
+
stepper?: string;
|
|
35
|
+
step?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
type __VLS_Props = NumberInputProps;
|
|
39
|
+
type __VLS_ModelProps = {
|
|
40
|
+
/**
|
|
41
|
+
* The model is a decimal **string**, never a number.
|
|
42
|
+
*
|
|
43
|
+
* Money in a finance app is computed with decimal arithmetic; coercing to a
|
|
44
|
+
* float here would silently lose precision before the value ever reached the
|
|
45
|
+
* caller. An empty string means "no value".
|
|
46
|
+
*/
|
|
47
|
+
modelValue?: string;
|
|
48
|
+
};
|
|
49
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
50
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
51
|
+
"update:modelValue": (value: string) => any;
|
|
52
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
53
|
+
"onUpdate:modelValue"?: ((value: string) => any) | undefined;
|
|
54
|
+
}>, {
|
|
55
|
+
unstyled: boolean;
|
|
56
|
+
step: string;
|
|
57
|
+
invalid: boolean;
|
|
58
|
+
stepper: boolean;
|
|
59
|
+
incrementLabel: string;
|
|
60
|
+
decrementLabel: string;
|
|
61
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
62
|
+
declare const _default: typeof __VLS_export;
|
|
63
|
+
export default _default;
|
|
@@ -4,9 +4,18 @@ export interface SelectItemOption {
|
|
|
4
4
|
value: string;
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
}
|
|
7
|
+
/** A labelled run of options, rendered under a heading. */
|
|
8
|
+
export interface SelectItemGroup {
|
|
9
|
+
label: string;
|
|
10
|
+
items: (SelectItemOption | string)[];
|
|
11
|
+
}
|
|
12
|
+
export type SelectItems = (SelectItemOption | SelectItemGroup | string)[];
|
|
7
13
|
export interface SelectProps extends SelectRootProps {
|
|
8
|
-
/**
|
|
9
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Options to render. Strings are expanded to `{ label, value }`, and an
|
|
16
|
+
* entry with its own `items` becomes a labelled group.
|
|
17
|
+
*/
|
|
18
|
+
items?: SelectItems;
|
|
10
19
|
placeholder?: string;
|
|
11
20
|
size?: 'sm' | 'md' | 'lg';
|
|
12
21
|
/** Skip built-in classes; you take over styling entirely. */
|
|
@@ -17,6 +26,8 @@ export interface SelectProps extends SelectRootProps {
|
|
|
17
26
|
content?: string;
|
|
18
27
|
viewport?: string;
|
|
19
28
|
item?: string;
|
|
29
|
+
group?: string;
|
|
30
|
+
groupLabel?: string;
|
|
20
31
|
};
|
|
21
32
|
}
|
|
22
33
|
declare var __VLS_48: {};
|
|
@@ -6,6 +6,7 @@ export { default as Button } from './Button.vue';
|
|
|
6
6
|
export { default as ButtonGroup } from './ButtonGroup.vue';
|
|
7
7
|
export { default as Card } from './Card.vue';
|
|
8
8
|
export { default as Checkbox } from './Checkbox.vue';
|
|
9
|
+
export { default as Combobox } from './Combobox.vue';
|
|
9
10
|
export { default as ConfirmDialog } from './ConfirmDialog.vue';
|
|
10
11
|
export { default as Dialog } from './Dialog.vue';
|
|
11
12
|
export { default as DropdownMenu } from './DropdownMenu.vue';
|
|
@@ -14,6 +15,7 @@ export { default as Form } from './Form.vue';
|
|
|
14
15
|
export { default as FormField } from './FormField.vue';
|
|
15
16
|
export { default as Input } from './Input.vue';
|
|
16
17
|
export { default as Label } from './Label.vue';
|
|
18
|
+
export { default as NumberInput } from './NumberInput.vue';
|
|
17
19
|
export { default as Pagination } from './Pagination.vue';
|
|
18
20
|
export { default as Progress } from './Progress.vue';
|
|
19
21
|
export { default as RadioGroup } from './RadioGroup.vue';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decimal-string arithmetic, exact by construction.
|
|
3
|
+
*
|
|
4
|
+
* Money is stored and computed as decimal strings — `0.1 + 0.2` in binary
|
|
5
|
+
* floating point is `0.30000000000000004`, which silently corrupts a total.
|
|
6
|
+
* Everything here scales to integers and uses `BigInt`, so a value only ever
|
|
7
|
+
* round-trips through `Number` if the caller asks for it.
|
|
8
|
+
*/
|
|
9
|
+
interface Decimal {
|
|
10
|
+
/** Unscaled value: `1234.56` at scale 2 is `123456n`. */
|
|
11
|
+
units: bigint;
|
|
12
|
+
/** Number of decimal places. */
|
|
13
|
+
scale: number;
|
|
14
|
+
}
|
|
15
|
+
/** Parse a canonical decimal string (`-1234.56`). Returns undefined if invalid. */
|
|
16
|
+
export declare function parseDecimal(value: string): Decimal | undefined;
|
|
17
|
+
/** Render a Decimal back to a canonical string. */
|
|
18
|
+
export declare function formatDecimal({ units, scale }: Decimal): string;
|
|
19
|
+
/** `-1` if a < b, `0` if equal, `1` if a > b. */
|
|
20
|
+
export declare function compareDecimals(a: string, b: string): number;
|
|
21
|
+
/** Exact addition. `addDecimals('0.1', '0.2')` is `'0.3'`, not `'0.30000000000000004'`. */
|
|
22
|
+
export declare function addDecimals(a: string, b: string): string | undefined;
|
|
23
|
+
/** Round half-up to a fixed number of decimal places. */
|
|
24
|
+
export declare function roundDecimal(value: string, precision: number): string | undefined;
|
|
25
|
+
/** Constrain a value to a range, comparing exactly. */
|
|
26
|
+
export declare function clampDecimal(value: string, min?: string, max?: string): string;
|
|
27
|
+
/** Whether a string is a well-formed decimal. */
|
|
28
|
+
export declare function isDecimal(value: string): boolean;
|
|
29
|
+
/** The separators a locale uses, e.g. `.`/`,` for `en`, `,`/`.` for `sl`. */
|
|
30
|
+
export declare function localeSeparators(locale: string): {
|
|
31
|
+
decimal: string;
|
|
32
|
+
group: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Render a canonical decimal for display, e.g. `"1234.56"` as `1.234,56` in
|
|
36
|
+
* `sl`. Formatting goes through the digits, never `Number`, so a value too
|
|
37
|
+
* large for a float still displays exactly.
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatForLocale(value: string, locale: string, precision?: number): string;
|
|
40
|
+
/**
|
|
41
|
+
* Render a canonical decimal for *editing*: the locale's decimal separator,
|
|
42
|
+
* but no grouping.
|
|
43
|
+
*
|
|
44
|
+
* Editing must not show the canonical `1234.56` in a locale where `.` groups
|
|
45
|
+
* digits — parsing that back would read it as `123456`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function toEditable(value: string, locale?: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Turn typed input into a canonical decimal, accepting the locale's
|
|
50
|
+
* separators. `1.234,56` in `sl` becomes `1234.56`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function parseFromLocale(input: string, locale: string): string | undefined;
|
|
53
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type { ButtonProps } from './components/Button.vue';
|
|
|
9
9
|
export type { ButtonGroupProps } from './components/ButtonGroup.vue';
|
|
10
10
|
export type { CardProps } from './components/Card.vue';
|
|
11
11
|
export type { CheckboxProps } from './components/Checkbox.vue';
|
|
12
|
+
export type { ComboboxItemGroup, ComboboxItemOption, ComboboxItems, ComboboxProps } from './components/Combobox.vue';
|
|
12
13
|
export type { ConfirmDialogProps } from './components/ConfirmDialog.vue';
|
|
13
14
|
export type { DialogProps } from './components/Dialog.vue';
|
|
14
15
|
export type { DropdownMenuProps } from './components/DropdownMenu.vue';
|
|
@@ -17,10 +18,11 @@ export type { FormProps } from './components/Form.vue';
|
|
|
17
18
|
export type { FormFieldProps } from './components/FormField.vue';
|
|
18
19
|
export type { InputProps } from './components/Input.vue';
|
|
19
20
|
export type { LabelProps } from './components/Label.vue';
|
|
21
|
+
export type { NumberInputProps } from './components/NumberInput.vue';
|
|
20
22
|
export type { PaginationProps } from './components/Pagination.vue';
|
|
21
23
|
export type { ProgressProps } from './components/Progress.vue';
|
|
22
24
|
export type { RadioGroupItemOption, RadioGroupProps } from './components/RadioGroup.vue';
|
|
23
|
-
export type { SelectItemOption, SelectProps } from './components/Select.vue';
|
|
25
|
+
export type { SelectItemGroup, SelectItemOption, SelectItems, SelectProps } from './components/Select.vue';
|
|
24
26
|
export type { SeparatorProps } from './components/Separator.vue';
|
|
25
27
|
export type { SkeletonProps } from './components/Skeleton.vue';
|
|
26
28
|
export type { StatProps } from './components/Stat.vue';
|
|
@@ -33,6 +35,7 @@ export type { TooltipProps } from './components/Tooltip.vue';
|
|
|
33
35
|
export * from './composables/appearance';
|
|
34
36
|
export * from './composables/button-group';
|
|
35
37
|
export * from './composables/confirm';
|
|
38
|
+
export * from './composables/decimal';
|
|
36
39
|
export * from './composables/dropdown-menu';
|
|
37
40
|
export * from './composables/form';
|
|
38
41
|
export * from './composables/toast';
|