iryx-ui 0.4.0 → 0.6.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 +293 -10
- package/dist/component-names.d.ts +1 -1
- package/dist/components/Alert.vue.d.ts +19 -4
- package/dist/components/App.vue.d.ts +1 -1
- package/dist/components/Badge.vue.d.ts +4 -3
- package/dist/components/Banner.vue.d.ts +81 -0
- package/dist/components/Combobox.vue.d.ts +112 -0
- package/dist/components/Pagination.vue.d.ts +2 -0
- package/dist/components/Select.vue.d.ts +15 -4
- package/dist/components/Table.vue.d.ts +131 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/composables/data-table.d.ts +93 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +65 -58
- package/dist/node_modules/.pnpm/@hugeicons_core-free-icons@4.2.3/node_modules/@hugeicons/core-free-icons/dist/esm/index.js +15 -1
- 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/Alert.vue_vue_type_script_setup_true_lang.js +51 -35
- package/dist/packages/iryx-ui/src/components/Badge.vue_vue_type_script_setup_true_lang.js +1 -2
- package/dist/packages/iryx-ui/src/components/Banner.js +5 -0
- package/dist/packages/iryx-ui/src/components/Banner.vue_vue_type_script_setup_true_lang.js +93 -0
- 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/Pagination.vue_vue_type_script_setup_true_lang.js +5 -1
- 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/Table.js +5 -0
- package/dist/packages/iryx-ui/src/components/Table.vue_vue_type_script_setup_true_lang.js +217 -0
- package/dist/packages/iryx-ui/src/components/index.js +62 -56
- package/dist/packages/iryx-ui/src/composables/data-table.js +106 -0
- package/dist/packages/iryx-ui/src/theme/alert.js +10 -21
- package/dist/packages/iryx-ui/src/theme/badge.js +37 -37
- package/dist/packages/iryx-ui/src/theme/banner.js +62 -0
- 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/input.js +1 -1
- package/dist/packages/iryx-ui/src/theme/number-input.js +1 -1
- package/dist/packages/iryx-ui/src/theme/pagination.js +24 -14
- 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/stat.js +4 -4
- 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/table.js +62 -0
- package/dist/packages/iryx-ui/src/theme/tabs.js +3 -3
- package/dist/packages/iryx-ui/src/theme/toast.js +11 -26
- package/dist/packages/iryx-ui/src/theme/tooltip.js +1 -1
- package/dist/plugin.d.ts +1 -1
- package/dist/theme/alert.d.ts +6 -12
- package/dist/theme/badge.d.ts +15 -12
- package/dist/theme/banner.d.ts +185 -0
- package/dist/theme/button.d.ts +2 -2
- package/dist/theme/combobox.d.ts +101 -0
- package/dist/theme/index.d.ts +3 -0
- package/dist/theme/input.d.ts +5 -5
- package/dist/theme/pagination.d.ts +33 -0
- package/dist/theme/presets.d.ts +0 -42
- package/dist/theme/select.d.ts +6 -0
- package/dist/theme/stat.d.ts +0 -3
- package/dist/theme/table.d.ts +230 -0
- package/dist/theme/toast.d.ts +0 -15
- package/package.json +1 -1
- package/theme.css +24 -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
|
```
|
|
@@ -197,15 +197,42 @@ For a static re-brand, plain CSS works too — tokens are just variables:
|
|
|
197
197
|
}
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
+
### Typeface
|
|
201
|
+
|
|
202
|
+
The library ships **no webfont** — that would put font files, a licence and a
|
|
203
|
+
network request into every app whether it wanted them or not. It reads one
|
|
204
|
+
variable, defaulting to the system stack. Point it at your own family and
|
|
205
|
+
everything follows, including Tailwind's `font-sans`:
|
|
206
|
+
|
|
207
|
+
```css
|
|
208
|
+
@import "@fontsource-variable/instrument-sans"; /* or a <link>, or self-hosted */
|
|
209
|
+
|
|
210
|
+
:root {
|
|
211
|
+
--iryx-font-sans: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Unlike the colour tokens, this one is not mode-specific — there is no `.dark`
|
|
216
|
+
counterpart to keep in sync.
|
|
217
|
+
|
|
200
218
|
Available tokens, each usable as a Tailwind color (`bg-primary`,
|
|
201
219
|
`text-muted-foreground`, …):
|
|
202
220
|
|
|
203
221
|
| Group | Tokens |
|
|
204
222
|
| --- | --- |
|
|
205
|
-
| Surfaces | `background`, `foreground`, `accent`, `accent-foreground`, `muted`, `muted-foreground`, `border` |
|
|
223
|
+
| Surfaces | `background`, `foreground`, `accent`, `accent-foreground`, `muted`, `muted-foreground`, `border`, `input` |
|
|
206
224
|
| Brand | `primary`, `primary-foreground`, `primary-from`, `primary-to` |
|
|
207
225
|
| Status | `success`, `warning`, `danger`, `info` — each with `-foreground`, `-muted`, `-muted-foreground` and `-border` |
|
|
208
226
|
|
|
227
|
+
`input` is the fill behind the fields — `IInput`, `ITextarea`, `INumberInput`,
|
|
228
|
+
the `ISelect` trigger, the `ICombobox` anchor — and behind `IButton`'s
|
|
229
|
+
`outline` variant, so the two read as the same kind of surface. It's a token
|
|
230
|
+
of its own rather than a reused `muted`, so how raised a control looks can be
|
|
231
|
+
tuned without moving every muted surface with it. It matches the page
|
|
232
|
+
background in light mode and lifts off it in dark, where the difference is
|
|
233
|
+
legible; that split lives in the token values, so no component needs a `dark:`
|
|
234
|
+
class for it.
|
|
235
|
+
|
|
209
236
|
`primary-from` / `primary-to` are the stops of the solid button's vertical
|
|
210
237
|
gradient. The status tokens carry their own dark-mode values, so components
|
|
211
238
|
never need a `dark:` class for them: `success` is the solid fill,
|
|
@@ -266,7 +293,8 @@ app.use(createIryxUi({ unstyled: true }))
|
|
|
266
293
|
| `ITextarea` | Multi-line field with matching sizes and `invalid` state |
|
|
267
294
|
| `ILabel` | Field label with optional `required` asterisk |
|
|
268
295
|
| `ICheckbox` | Tri-state checkbox (`true` / `false` / `'indeterminate'`), optional `label` + `description` |
|
|
269
|
-
| `ISelect` | Listbox with keyboard nav and typeahead, driven by an `items` array |
|
|
296
|
+
| `ISelect` | Listbox with keyboard nav and typeahead, driven by an `items` array, with optional groups |
|
|
297
|
+
| `ICombobox` | Searchable select — filters as you type, with optional groups, virtualized rows and a "create from query" row |
|
|
270
298
|
| `IRadioGroup` | Radio list with labels wired up automatically; items take a `description` |
|
|
271
299
|
| `ISwitch` | Accessible toggle, optional `label` + `description` |
|
|
272
300
|
|
|
@@ -282,8 +310,9 @@ app.use(createIryxUi({ unstyled: true }))
|
|
|
282
310
|
|
|
283
311
|
| Component | Description |
|
|
284
312
|
| --- | --- |
|
|
285
|
-
| `IAlert` | Inline
|
|
286
|
-
| `
|
|
313
|
+
| `IAlert` | Inline, contextual message in four variants — variant icon, `actions` slot, optional dismiss |
|
|
314
|
+
| `IBanner` | Page-level announcement — full-bleed, six variants, sticky top or fixed bottom |
|
|
315
|
+
| `IBadge` | Status pill — five variants × three sizes; `dot` moves the colour onto a leading dot |
|
|
287
316
|
| `IToaster` | Host for `useToast()`; six viewport positions, stacking, action buttons |
|
|
288
317
|
| `IDialog` | Modal with header/body/footer slots, `dismissible` and `showClose` |
|
|
289
318
|
| `IConfirmDialog` | Host for `useConfirm()` — renders the promise-based confirmation |
|
|
@@ -298,9 +327,10 @@ app.use(createIryxUi({ unstyled: true }))
|
|
|
298
327
|
| --- | --- |
|
|
299
328
|
| `ITabs` | `solid` or `line` variants with an animated indicator, horizontal or vertical |
|
|
300
329
|
| `IBreadcrumb` | Trail from an `items` array; the last crumb is marked as the current page |
|
|
301
|
-
| `IPagination` | Page list with ellipsis, edge pages and prev/next controls |
|
|
330
|
+
| `IPagination` | Page list with ellipsis, edge pages and prev/next controls; `align` places it, `size` sets the button scale |
|
|
302
331
|
| `IStepper` | Multi-step progress, horizontal or vertical, optional `linear` ordering |
|
|
303
332
|
| `IStat` | KPI tile — label, value, signed delta with trend colour, and a hint |
|
|
333
|
+
| `ITable` | Data table — sorting, selection, expansion and per-cell slots, client- or server-driven |
|
|
304
334
|
|
|
305
335
|
Every component supports `unstyled` and a `class` override; multi-part ones take a `ui` prop for per-slot classes.
|
|
306
336
|
|
|
@@ -406,6 +436,77 @@ const framework = ref('vue')
|
|
|
406
436
|
|
|
407
437
|
`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.
|
|
408
438
|
|
|
439
|
+
`ISelect` also takes groups — an entry with its own `items` becomes a labelled
|
|
440
|
+
heading, the same shape `ICombobox` uses:
|
|
441
|
+
|
|
442
|
+
```vue
|
|
443
|
+
<ISelect
|
|
444
|
+
v-model="framework"
|
|
445
|
+
:items="[
|
|
446
|
+
{ label: 'Virtual DOM', items: ['Vue', 'React'] },
|
|
447
|
+
{ label: 'Compiled', items: [{ label: 'Svelte', value: 'svelte' }] },
|
|
448
|
+
]"
|
|
449
|
+
/>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Searchable selects
|
|
453
|
+
|
|
454
|
+
`ICombobox` takes the same `items` as `ISelect` and filters them against what
|
|
455
|
+
the user types, which is what you want once a list runs to hundreds of entries.
|
|
456
|
+
The field shows the selected option's **label** while the model holds its value.
|
|
457
|
+
|
|
458
|
+
```vue
|
|
459
|
+
<ICombobox
|
|
460
|
+
v-model="clientId"
|
|
461
|
+
:items="clients"
|
|
462
|
+
placeholder="Search clients"
|
|
463
|
+
empty-text="No clients found."
|
|
464
|
+
/>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Set `create` to offer a row for whatever the user typed when nothing matches.
|
|
468
|
+
Choosing it emits `create` with the query and does **not** change the model —
|
|
469
|
+
the option doesn't exist yet, so you add it and select it yourself:
|
|
470
|
+
|
|
471
|
+
```vue
|
|
472
|
+
<ICombobox
|
|
473
|
+
v-model="clientId"
|
|
474
|
+
:items="clients"
|
|
475
|
+
create
|
|
476
|
+
:create-label="query => `Add ${query}`"
|
|
477
|
+
@create="query => clients.push({ label: query, value: addClient(query) })"
|
|
478
|
+
/>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Both `empty-text` and `create-label` are props precisely so a non-English app
|
|
482
|
+
never inherits an English string; `empty` and `create` slots take over the
|
|
483
|
+
markup entirely if you need more than text.
|
|
484
|
+
|
|
485
|
+
An entry with its own `items` becomes a labelled group. A group disappears on
|
|
486
|
+
its own once nothing inside it matches:
|
|
487
|
+
|
|
488
|
+
```vue
|
|
489
|
+
<ICombobox
|
|
490
|
+
v-model="clientId"
|
|
491
|
+
:items="[
|
|
492
|
+
{ label: 'Recent', items: ['Acme Industries', 'Bolt Logistics'] },
|
|
493
|
+
{ label: 'Archived', items: [{ label: 'Cirrus Systems', value: 'cirrus' }] },
|
|
494
|
+
]"
|
|
495
|
+
/>
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
For lists in the thousands, `virtual` renders only the rows on screen:
|
|
499
|
+
|
|
500
|
+
```vue
|
|
501
|
+
<ICombobox v-model="sku" virtual :items="fiveThousandItems" :estimate-size="32" />
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
`virtual` and grouped items are **mutually exclusive** — the underlying
|
|
505
|
+
virtualizer is a flat window with no notion of group headings, so groups are
|
|
506
|
+
flattened and their labels dropped (with a warning in dev). `estimate-size` is
|
|
507
|
+
the assumed row height in px, used to size the scrollbar before rows are
|
|
508
|
+
measured; set it if you have restyled rows to a different height.
|
|
509
|
+
|
|
409
510
|
### Numbers and money
|
|
410
511
|
|
|
411
512
|
`INumberInput` never turns your value into a `number`. The model is a decimal
|
|
@@ -526,6 +627,188 @@ The control inside a field automatically inherits its `id`, invalid styling and
|
|
|
526
627
|
|
|
527
628
|
Use the `#label` / `#description` slots instead of the props when you need markup (a link, a badge) inside the text.
|
|
528
629
|
|
|
630
|
+
### Alerts and banners
|
|
631
|
+
|
|
632
|
+
They look similar and do different jobs, so they're separate components.
|
|
633
|
+
|
|
634
|
+
**`IAlert` is contextual.** It sits in the flow next to the thing it's about —
|
|
635
|
+
a failed upload, a form that won't submit — boxed, with an icon and a title.
|
|
636
|
+
Danger and warning variants take `role="alert"` so a screen reader interrupts,
|
|
637
|
+
because the user caused it and needs to know now.
|
|
638
|
+
|
|
639
|
+
**`IBanner` is page-level.** It spans the full width and announces something
|
|
640
|
+
that isn't about any one element: a trial ending, scheduled maintenance. It's a
|
|
641
|
+
labelled `role="region"`, never an alert — it's ambient, so interrupting
|
|
642
|
+
someone mid-task would be wrong.
|
|
643
|
+
|
|
644
|
+
```vue
|
|
645
|
+
<IBanner
|
|
646
|
+
v-model:open="showTrial"
|
|
647
|
+
variant="primary"
|
|
648
|
+
position="top"
|
|
649
|
+
title="Trial ends in 3 days."
|
|
650
|
+
description="Upgrade to keep your data."
|
|
651
|
+
closable
|
|
652
|
+
label="Trial notice"
|
|
653
|
+
>
|
|
654
|
+
<template #actions>
|
|
655
|
+
<IButton size="sm" variant="outline">Upgrade</IButton>
|
|
656
|
+
</template>
|
|
657
|
+
</IBanner>
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
`position` is `static` (in flow), `top` (sticky) or `bottom` (fixed to the
|
|
661
|
+
viewport, with the rule moved to its top edge). `contained` keeps the text at a
|
|
662
|
+
readable measure while the fill still spans the window.
|
|
663
|
+
|
|
664
|
+
Both dismiss through `v-model:open`, so the usual case is one binding instead
|
|
665
|
+
of a `close` handler plus a `v-if`. `close` still fires if you need to persist
|
|
666
|
+
or confirm first:
|
|
667
|
+
|
|
668
|
+
```vue
|
|
669
|
+
<IAlert v-model:open="visible" variant="danger" title="Upload failed" closable>
|
|
670
|
+
The file was larger than 10 MB.
|
|
671
|
+
<template #actions>
|
|
672
|
+
<IButton size="sm" @click="retry()">Retry</IButton>
|
|
673
|
+
</template>
|
|
674
|
+
</IAlert>
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Tables
|
|
678
|
+
|
|
679
|
+
`ITable` takes `rows` and `columns` and renders a real `<table>`. Columns are
|
|
680
|
+
plain objects — no render functions — and anything beyond the raw value is a
|
|
681
|
+
slot:
|
|
682
|
+
|
|
683
|
+
```vue
|
|
684
|
+
<script setup>
|
|
685
|
+
const columns = [
|
|
686
|
+
{ key: 'number', label: 'Invoice', sortable: true },
|
|
687
|
+
{ key: 'customer.name', label: 'Customer', sortable: true, sortKey: 'customer_name' },
|
|
688
|
+
{ key: 'total', label: 'Total', sortable: true, numeric: true },
|
|
689
|
+
{ key: 'status', label: 'Status' },
|
|
690
|
+
]
|
|
691
|
+
</script>
|
|
692
|
+
|
|
693
|
+
<template>
|
|
694
|
+
<ITable :rows="invoices" :columns="columns" label="Invoices">
|
|
695
|
+
<template #cell-total="{ value }">
|
|
696
|
+
{{ formatMoney(value) }}
|
|
697
|
+
</template>
|
|
698
|
+
<template #cell-status="{ row }">
|
|
699
|
+
<IBadge :variant="row.paid ? 'success' : 'warning'">
|
|
700
|
+
{{ row.paid ? 'Paid' : 'Due' }}
|
|
701
|
+
</IBadge>
|
|
702
|
+
</template>
|
|
703
|
+
</ITable>
|
|
704
|
+
</template>
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
`key` doubles as the accessor (dot-notation reaches nested values) and the slot
|
|
708
|
+
suffix, so `#cell-customer.name` targets that column. `#header-<key>` replaces a
|
|
709
|
+
header the same way.
|
|
710
|
+
|
|
711
|
+
`numeric` gives a column tabular figures and end alignment, so amounts line up
|
|
712
|
+
digit-for-digit down the column instead of wandering with each glyph's width.
|
|
713
|
+
Set `align` alongside it to keep the figures but place the column differently.
|
|
714
|
+
|
|
715
|
+
#### Client or server
|
|
716
|
+
|
|
717
|
+
**The table never fetches.** It renders what you give it and emits what the
|
|
718
|
+
user did, so caching, cancellation and auth stay in your data layer.
|
|
719
|
+
|
|
720
|
+
Which mode it runs in is decided by one prop. Omit `total` and it sorts and
|
|
721
|
+
paginates `rows` itself. Provide `total` and it does neither — the rows you
|
|
722
|
+
passed are already the page the server returned, so it only reflects state and
|
|
723
|
+
emits changes:
|
|
724
|
+
|
|
725
|
+
```vue
|
|
726
|
+
<ITable
|
|
727
|
+
v-model:sort="sort"
|
|
728
|
+
v-model:page="page"
|
|
729
|
+
:rows="data.rows"
|
|
730
|
+
:columns="columns"
|
|
731
|
+
:total="data.total"
|
|
732
|
+
:loading="pending"
|
|
733
|
+
/>
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Watch those models and refetch. Sorting a column resets `page` to 1, since the
|
|
737
|
+
old page number means nothing against a reordered list.
|
|
738
|
+
|
|
739
|
+
#### State ownership
|
|
740
|
+
|
|
741
|
+
Every model is optional. Bind one and you own that state — put it in the URL, a
|
|
742
|
+
store, or `useState`. Leave it unbound and the table keeps it internally, so the
|
|
743
|
+
simple case needs no wiring:
|
|
744
|
+
|
|
745
|
+
| Model | Type |
|
|
746
|
+
| --- | --- |
|
|
747
|
+
| `v-model:sort` | `{ key, order } \| null` — `null` is "unsorted", distinct from never-sorted |
|
|
748
|
+
| `v-model:page` | `number`, 1-indexed |
|
|
749
|
+
| `v-model:perPage` | `number` |
|
|
750
|
+
| `v-model:selection` | `(string \| number)[]` of row keys |
|
|
751
|
+
| `v-model:expanded` | `(string \| number)[]` of row keys |
|
|
752
|
+
|
|
753
|
+
#### Selection and expansion
|
|
754
|
+
|
|
755
|
+
`selectable` adds a checkbox column whose header is tri-state over the rows on
|
|
756
|
+
screen. Selection is held as **row keys** (`rowKey`, default `'id'`), and
|
|
757
|
+
select-all only touches the current page, so selections made on other pages
|
|
758
|
+
survive. `isRowSelectable` vetoes a row — its checkbox is hidden and the header
|
|
759
|
+
skips it.
|
|
760
|
+
|
|
761
|
+
`expandable` adds a disclosure column and renders the `#expanded` slot beneath
|
|
762
|
+
an open row; `canExpandRow` vetoes it per row.
|
|
763
|
+
|
|
764
|
+
```vue
|
|
765
|
+
<ITable
|
|
766
|
+
v-model:selection="selected"
|
|
767
|
+
:rows="rows"
|
|
768
|
+
:columns="columns"
|
|
769
|
+
selectable
|
|
770
|
+
expandable
|
|
771
|
+
:is-row-selectable="row => !row.locked"
|
|
772
|
+
>
|
|
773
|
+
<template #expanded="{ row }">
|
|
774
|
+
<div class="p-4">{{ row.notes }}</div>
|
|
775
|
+
</template>
|
|
776
|
+
</ITable>
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### States and props
|
|
780
|
+
|
|
781
|
+
`loading` sets `aria-busy` and, when there is nothing to show yet, renders
|
|
782
|
+
`loadingRows` skeleton rows instead of the empty message. With rows already on
|
|
783
|
+
screen it leaves them in place, so a refetch doesn't blank the table. Otherwise
|
|
784
|
+
`emptyText` — or the `#empty` slot — takes over.
|
|
785
|
+
|
|
786
|
+
| Prop | What it does |
|
|
787
|
+
| --- | --- |
|
|
788
|
+
| `rowKey` | Field identifying a row. Default `'id'` |
|
|
789
|
+
| `clickableRows` | Emits `rowClick` and shows a pointer cursor |
|
|
790
|
+
| `striped` / `hoverable` / `stickyHeader` | Row and header treatment |
|
|
791
|
+
| `size` | `sm` / `md` / `lg` row density |
|
|
792
|
+
| `label` / `caption` | Accessible name, and an optional visible caption |
|
|
793
|
+
|
|
794
|
+
Headers are real `<button>`s, sorted columns carry `aria-sort`, and the table is
|
|
795
|
+
a plain `<table>`, so screen readers and keyboard users get the semantics for
|
|
796
|
+
free. Give it a `label` (or a `caption`) — a table with no accessible name is
|
|
797
|
+
hard to place when tabbing through a page.
|
|
798
|
+
|
|
799
|
+
#### Without the markup
|
|
800
|
+
|
|
801
|
+
`useDataTable()` holds the whole state machine — sorting, paging, selection,
|
|
802
|
+
expansion — and renders nothing, if you want the logic under your own markup:
|
|
803
|
+
|
|
804
|
+
```ts
|
|
805
|
+
const table = useDataTable({
|
|
806
|
+
rows: () => rows.value,
|
|
807
|
+
columns: () => columns,
|
|
808
|
+
})
|
|
809
|
+
// table.pageRows, table.toggleSort, table.headerSelection, …
|
|
810
|
+
```
|
|
811
|
+
|
|
529
812
|
## License
|
|
530
813
|
|
|
531
814
|
[MIT](https://github.com/therok1/iryx-ui/blob/main/LICENSE)
|
|
@@ -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", "NumberInput", "Pagination", "Progress", "RadioGroup", "Select", "Separator", "Skeleton", "Stat", "Stepper", "Switch", "Tabs", "Textarea", "Toaster", "Tooltip"];
|
|
6
|
+
export declare const componentNames: readonly ["Alert", "App", "Badge", "Banner", "Breadcrumb", "Button", "ButtonGroup", "Card", "Checkbox", "Combobox", "ConfirmDialog", "Dialog", "DropdownMenu", "EmptyState", "Form", "FormField", "Input", "Label", "NumberInput", "Pagination", "Progress", "RadioGroup", "Select", "Separator", "Skeleton", "Stat", "Stepper", "Switch", "Table", "Tabs", "Textarea", "Toaster", "Tooltip"];
|
|
7
7
|
export type ComponentName = (typeof componentNames)[number];
|
|
@@ -26,10 +26,21 @@ export interface AlertProps {
|
|
|
26
26
|
content?: string;
|
|
27
27
|
title?: string;
|
|
28
28
|
description?: string;
|
|
29
|
+
actions?: string;
|
|
29
30
|
close?: string;
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
type __VLS_Props = AlertProps;
|
|
34
|
+
type __VLS_ModelProps = {
|
|
35
|
+
/**
|
|
36
|
+
* Dismissal as a model, so the common case is one binding rather than a
|
|
37
|
+
* `close` handler plus a `v-if` in every consumer. `close` still fires, for
|
|
38
|
+
* callers that want to confirm or persist before it goes.
|
|
39
|
+
*/
|
|
40
|
+
'open'?: boolean;
|
|
41
|
+
};
|
|
42
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
43
|
+
declare var __VLS_8: {}, __VLS_15: {}, __VLS_17: {}, __VLS_19: {}, __VLS_21: {};
|
|
33
44
|
type __VLS_Slots = {} & {
|
|
34
45
|
icon?: (props: typeof __VLS_8) => any;
|
|
35
46
|
} & {
|
|
@@ -37,12 +48,16 @@ type __VLS_Slots = {} & {
|
|
|
37
48
|
} & {
|
|
38
49
|
default?: (props: typeof __VLS_17) => any;
|
|
39
50
|
} & {
|
|
40
|
-
|
|
51
|
+
actions?: (props: typeof __VLS_19) => any;
|
|
52
|
+
} & {
|
|
53
|
+
close?: (props: typeof __VLS_21) => any;
|
|
41
54
|
};
|
|
42
|
-
declare const __VLS_base: import("vue").DefineComponent<
|
|
55
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
43
56
|
close: () => any;
|
|
44
|
-
|
|
57
|
+
"update:open": (value: boolean) => any;
|
|
58
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
45
59
|
onClose?: (() => any) | undefined;
|
|
60
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
46
61
|
}>, {
|
|
47
62
|
icon: IconLike | false;
|
|
48
63
|
as: string;
|
|
@@ -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;
|
|
@@ -4,10 +4,11 @@ export interface BadgeProps {
|
|
|
4
4
|
/** Merge props onto the immediate child instead of rendering an element. */
|
|
5
5
|
asChild?: boolean;
|
|
6
6
|
variant?: 'neutral' | 'success' | 'warning' | 'danger' | 'info';
|
|
7
|
-
/** `soft` is a tinted background; `solid` is a filled block of colour. */
|
|
8
|
-
tone?: 'soft' | 'solid';
|
|
9
7
|
size?: 'sm' | 'md' | 'lg';
|
|
10
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Show a leading status dot. This also switches the look: the badge goes
|
|
10
|
+
* neutral and the dot alone carries the variant's colour.
|
|
11
|
+
*/
|
|
11
12
|
dot?: boolean;
|
|
12
13
|
/** Text content. Ignored when the default slot is used. */
|
|
13
14
|
label?: string;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { IconLike } from '../composables/icon';
|
|
2
|
+
export interface BannerProps {
|
|
3
|
+
/** Render as a different element or component. */
|
|
4
|
+
as?: string;
|
|
5
|
+
variant?: 'neutral' | 'info' | 'success' | 'warning' | 'danger' | 'primary';
|
|
6
|
+
/**
|
|
7
|
+
* `static` sits in the flow, `top` sticks to the top of the scroll
|
|
8
|
+
* container, `bottom` pins to the bottom of the viewport.
|
|
9
|
+
*/
|
|
10
|
+
position?: 'static' | 'top' | 'bottom';
|
|
11
|
+
/** Bold lead-in before the message. */
|
|
12
|
+
title?: string;
|
|
13
|
+
/** Message text. Ignored when the default slot is used. */
|
|
14
|
+
description?: string;
|
|
15
|
+
/** Leading icon. None by default — a banner is ambient, not a status report. */
|
|
16
|
+
icon?: IconLike;
|
|
17
|
+
/** Constrain the text to a readable measure while the fill still spans full width. */
|
|
18
|
+
contained?: boolean;
|
|
19
|
+
align?: 'start' | 'center';
|
|
20
|
+
/** Render a dismiss button. */
|
|
21
|
+
closable?: boolean;
|
|
22
|
+
/** Accessible name for the dismiss button — override for non-English apps. */
|
|
23
|
+
closeLabel?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Accessible name for the landmark. A banner is a region rather than an
|
|
26
|
+
* alert: it is ambient, so it must not interrupt a screen reader mid-task.
|
|
27
|
+
*/
|
|
28
|
+
label?: string;
|
|
29
|
+
/** Skip built-in classes; you take over styling entirely. */
|
|
30
|
+
unstyled?: boolean;
|
|
31
|
+
class?: string;
|
|
32
|
+
/** Override classes per slot, e.g. `{ container: 'max-w-3xl' }`. */
|
|
33
|
+
ui?: {
|
|
34
|
+
root?: string;
|
|
35
|
+
container?: string;
|
|
36
|
+
icon?: string;
|
|
37
|
+
content?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
actions?: string;
|
|
40
|
+
close?: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
type __VLS_Props = BannerProps;
|
|
44
|
+
type __VLS_ModelProps = {
|
|
45
|
+
/** Dismissal as a model, matching IAlert — bind it, or let the banner hide itself. */
|
|
46
|
+
'open'?: boolean;
|
|
47
|
+
};
|
|
48
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
49
|
+
declare var __VLS_8: {}, __VLS_15: {}, __VLS_17: {}, __VLS_19: {}, __VLS_21: {};
|
|
50
|
+
type __VLS_Slots = {} & {
|
|
51
|
+
icon?: (props: typeof __VLS_8) => any;
|
|
52
|
+
} & {
|
|
53
|
+
title?: (props: typeof __VLS_15) => any;
|
|
54
|
+
} & {
|
|
55
|
+
default?: (props: typeof __VLS_17) => any;
|
|
56
|
+
} & {
|
|
57
|
+
actions?: (props: typeof __VLS_19) => any;
|
|
58
|
+
} & {
|
|
59
|
+
close?: (props: typeof __VLS_21) => any;
|
|
60
|
+
};
|
|
61
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
62
|
+
close: () => any;
|
|
63
|
+
"update:open": (value: boolean) => any;
|
|
64
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
65
|
+
onClose?: (() => any) | undefined;
|
|
66
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
67
|
+
}>, {
|
|
68
|
+
as: string;
|
|
69
|
+
closeLabel: string;
|
|
70
|
+
unstyled: boolean;
|
|
71
|
+
label: string;
|
|
72
|
+
contained: boolean;
|
|
73
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
74
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
75
|
+
declare const _default: typeof __VLS_export;
|
|
76
|
+
export default _default;
|
|
77
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
78
|
+
new (): {
|
|
79
|
+
$slots: S;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -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:open": (value: boolean) => any;
|
|
78
|
+
"update:modelValue": (value: AcceptableValue) => any;
|
|
79
|
+
create: (query: string) => any;
|
|
80
|
+
highlight: (payload: {
|
|
81
|
+
ref: HTMLElement;
|
|
82
|
+
value: AcceptableValue;
|
|
83
|
+
} | undefined) => any;
|
|
84
|
+
}, string, import("vue").PublicProps, Readonly<ComboboxProps> & Readonly<{
|
|
85
|
+
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
86
|
+
"onUpdate:modelValue"?: ((value: AcceptableValue) => any) | undefined;
|
|
87
|
+
onCreate?: ((query: string) => any) | undefined;
|
|
88
|
+
onHighlight?: ((payload: {
|
|
89
|
+
ref: HTMLElement;
|
|
90
|
+
value: AcceptableValue;
|
|
91
|
+
} | undefined) => any) | undefined;
|
|
92
|
+
}>, {
|
|
93
|
+
unstyled: boolean;
|
|
94
|
+
open: boolean;
|
|
95
|
+
disabled: boolean;
|
|
96
|
+
invalid: boolean;
|
|
97
|
+
emptyText: string;
|
|
98
|
+
create: boolean;
|
|
99
|
+
createLabel: (query: string) => string;
|
|
100
|
+
virtual: boolean;
|
|
101
|
+
estimateSize: number;
|
|
102
|
+
overscan: number;
|
|
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
|
+
};
|
|
@@ -7,6 +7,8 @@ export interface PaginationProps {
|
|
|
7
7
|
/** Keep the first and last page visible however far away they are. */
|
|
8
8
|
showEdges?: boolean;
|
|
9
9
|
size?: 'sm' | 'md' | 'lg';
|
|
10
|
+
/** Where the control sits in its container. Centred by default. */
|
|
11
|
+
align?: 'start' | 'center' | 'end';
|
|
10
12
|
/** Accessible names — override for non-English apps. */
|
|
11
13
|
prevLabel?: string;
|
|
12
14
|
nextLabel?: string;
|