rei-kit 0.4.4 → 0.5.1
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 +110 -38
- package/dist/components/BaseButton.vue.d.ts +35 -3
- package/dist/components/BaseCheckbox.vue.d.ts +39 -0
- package/dist/components/BaseInput.vue.d.ts +2 -0
- package/dist/components/BaseRadioGroup.vue.d.ts +33 -0
- package/dist/components/BaseSelect.vue.d.ts +46 -0
- package/dist/components/BaseTextarea.vue.d.ts +20 -0
- package/dist/components/FormField.vue.d.ts +61 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +407 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
# rei-kit
|
|
2
2
|
|
|
3
|
-
Vue 3 and Tailwind 4 design system and shared runtime.
|
|
4
|
-
[Hibi](https://github.com/ramazandogna/hibi).
|
|
3
|
+
Vue 3 and Tailwind 4 design system and shared runtime.
|
|
5
4
|
|
|
6
5
|
零 — the layer everything else starts from.
|
|
7
6
|
|
|
8
7
|
## Why it exists
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
**The kit distributes decisions, not a look.** Three apps run on it and no two
|
|
10
|
+
resemble each other: a phone journal, a phone ledger, and a wide Japanese
|
|
11
|
+
course site. None of them forked it, because there is not a single hex value
|
|
12
|
+
inside a component. Colours are named for the role they play, and an app
|
|
13
|
+
rebrands by redefining eleven values.
|
|
14
|
+
|
|
15
|
+
That is the whole trick, and everything else follows from it.
|
|
16
|
+
|
|
17
|
+
### What gets to be in here
|
|
18
|
+
|
|
19
|
+
Three things have to be true at once:
|
|
20
|
+
|
|
21
|
+
1. **Two apps genuinely need it** — measured, not predicted. A component one
|
|
22
|
+
app needs stays in that app however general it looks.
|
|
23
|
+
2. **It adds no required peer.** Anything that needs a new library goes behind
|
|
24
|
+
its own entry point, or stays in the app.
|
|
25
|
+
3. **It contains no product decision** — no colour, no copy, no icon. Those
|
|
26
|
+
arrive as props and slots.
|
|
27
|
+
|
|
28
|
+
The corollary is the useful one: **a file that is 90% identical in two apps is
|
|
29
|
+
a kit candidate.** The measure is `diff`, not taste.
|
|
14
30
|
|
|
15
31
|
## Status
|
|
16
32
|
|
|
17
|
-
**v0.
|
|
18
|
-
|
|
19
|
-
|
|
33
|
+
**v0.4.4 — three consumers.**
|
|
34
|
+
|
|
35
|
+
| | |
|
|
36
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
37
|
+
| Components | 22 |
|
|
38
|
+
| Composables | 11 |
|
|
39
|
+
| Utilities | 20 |
|
|
40
|
+
| Also | a generic i18n runtime, an optional Supabase entry |
|
|
41
|
+
| Consumers | [Hibi](https://github.com/ramazandogna/hibi) · [Kakei](https://github.com/ramazandogna/kakei) · [Kakehashi](https://github.com/ramazandogna/kakehashi) |
|
|
42
|
+
|
|
43
|
+
Every export is listed by name in `src/__tests__/public-api.spec.ts`, which is
|
|
44
|
+
the package's promise written down.
|
|
20
45
|
|
|
21
46
|
## Install
|
|
22
47
|
|
|
@@ -37,7 +62,7 @@ is the only copy:
|
|
|
37
62
|
| `@supabase/supabase-js` | the `rei-kit/supabase` entry only |
|
|
38
63
|
|
|
39
64
|
This is not a formality. A second copy of a library that works through
|
|
40
|
-
provide/inject is not a spare copy
|
|
65
|
+
provide/inject is not a spare copy — it is a different injection key, so the
|
|
41
66
|
app's own provider becomes invisible and the component throws on mount.
|
|
42
67
|
|
|
43
68
|
## Use
|
|
@@ -59,12 +84,12 @@ import { createSupabaseClient } from 'rei-kit/supabase'
|
|
|
59
84
|
|
|
60
85
|
### Wiring the styles
|
|
61
86
|
|
|
62
|
-
|
|
87
|
+
Four lines, and all four are load-bearing:
|
|
63
88
|
|
|
64
89
|
```css
|
|
65
90
|
/* your app's main.css */
|
|
66
91
|
@import 'tailwindcss';
|
|
67
|
-
@import 'rei-kit/tokens.css'; /* colour roles, the dark variant, utilities */
|
|
92
|
+
@import 'rei-kit/tokens.css'; /* colour roles, the dark variant, measures, utilities */
|
|
68
93
|
@import 'rei-kit/styles.css'; /* compiled component styles */
|
|
69
94
|
|
|
70
95
|
/* Tailwind generates a utility only where it has seen the class, and it does
|
|
@@ -76,27 +101,19 @@ Three lines, and all three are load-bearing:
|
|
|
76
101
|
The path is relative to the CSS file, so adjust the `../` depth to where your
|
|
77
102
|
`main.css` sits.
|
|
78
103
|
|
|
79
|
-
Leaving any of the
|
|
80
|
-
mount, and they come out unstyled. Nothing type-checks this, so it
|
|
81
|
-
test
|
|
82
|
-
|
|
83
|
-
### Prerendering
|
|
104
|
+
**Leaving any of the four out fails quietly:** the build succeeds, the
|
|
105
|
+
components mount, and they come out unstyled. Nothing type-checks this, so it
|
|
106
|
+
is worth a test — Hibi's `kit-styling.spec.ts` reads its own stylesheet and
|
|
107
|
+
asserts all four, at unit-test speed. Copy it.
|
|
84
108
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- **The theme.** `applyTheme` does nothing without a document, so prerendered
|
|
90
|
-
HTML carries no `.dark`. Set it before hydration with a small synchronous
|
|
91
|
-
script in `index.html`, or the first paint flashes light.
|
|
92
|
-
- **Today's date.** `useToday()` on a server is the *server's* today, a
|
|
93
|
-
different day from the visitor's either side of midnight. Render anything
|
|
94
|
-
derived from it on the client.
|
|
109
|
+
That test exists because the failure is real: Hibi shipped with the tab bar
|
|
110
|
+
invisible once, and separately spent three versions restating `tokens.css`
|
|
111
|
+
locally instead of importing it, which nothing noticed.
|
|
95
112
|
|
|
96
113
|
### Colours
|
|
97
114
|
|
|
98
|
-
`tokens.css` defines all eleven roles, a `.dark` block for each surface, and
|
|
99
|
-
`dark` variant. A new app rebrands by overriding values, never by renaming:
|
|
115
|
+
`tokens.css` defines all eleven roles, a `.dark` block for each surface, and
|
|
116
|
+
the `dark` variant. A new app rebrands by overriding values, never by renaming:
|
|
100
117
|
|
|
101
118
|
```css
|
|
102
119
|
@theme {
|
|
@@ -125,8 +142,52 @@ the app's own dark-mode overrides carry into the kit's components:
|
|
|
125
142
|
}
|
|
126
143
|
```
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
|
|
145
|
+
Put your own `.dark` block _after_ the import. Both blocks match at the same
|
|
146
|
+
specificity, so the later one wins — which is what makes the override work.
|
|
147
|
+
|
|
148
|
+
### Measures
|
|
149
|
+
|
|
150
|
+
Widths are roles too. `PageContainer` shipped with `75rem` baked in, and the
|
|
151
|
+
first app that wanted it had deliberately measured its page at 1120px, so the
|
|
152
|
+
component written to remove that app's hand-rolled container could not replace
|
|
153
|
+
it — the same mistake as a hex inside a component, one axis over.
|
|
154
|
+
|
|
155
|
+
```css
|
|
156
|
+
@theme {
|
|
157
|
+
--measure-page: 1120px; /* a page */
|
|
158
|
+
--measure-reading: 68ch; /* a column of prose */
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### The phone shell
|
|
163
|
+
|
|
164
|
+
`tokens.css` also carries the geometry of a phone shell — `shell-frame`,
|
|
165
|
+
`page-slide`, `page-auth` and the slide transitions between screens. All of it
|
|
166
|
+
is opt-in: nothing applies unless you put the class on an element, so a wide
|
|
167
|
+
app can ignore it. Two of the three consumers use it.
|
|
168
|
+
|
|
169
|
+
### Prerendering
|
|
170
|
+
|
|
171
|
+
The kit imports and renders on a server, so an app can prerender with
|
|
172
|
+
`vite-ssg` or any other SSR build. Two things stay the app's job, because only
|
|
173
|
+
the app knows the answer:
|
|
174
|
+
|
|
175
|
+
- **The theme.** `applyTheme` does nothing without a document, so prerendered
|
|
176
|
+
HTML carries no `.dark`. Set it before hydration with a small synchronous
|
|
177
|
+
script in `index.html`, or the first paint flashes light.
|
|
178
|
+
- **Today's date.** `useToday()` on a server is the _server's_ today, a
|
|
179
|
+
different day from the visitor's either side of midnight. Render anything
|
|
180
|
+
derived from it on the client.
|
|
181
|
+
|
|
182
|
+
## Seeing what is in it
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
pnpm showcase
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
A page that wires the kit exactly the way this README says to, so a broken
|
|
189
|
+
install shows up there before it ships. A component nobody can see is a
|
|
190
|
+
component nobody uses.
|
|
130
191
|
|
|
131
192
|
## Commands
|
|
132
193
|
|
|
@@ -135,26 +196,34 @@ product changes values rather than renaming anything.
|
|
|
135
196
|
| `pnpm dev` | Rebuild on change, for use with a linked app |
|
|
136
197
|
| `pnpm build` | Type-check, then build |
|
|
137
198
|
| `pnpm check` | Everything CI runs: format, lint, types, tests, build |
|
|
199
|
+
| `pnpm showcase` | The showcase, in dev mode |
|
|
138
200
|
| `pnpm test:unit` | Vitest, watch mode |
|
|
139
201
|
| `pnpm lint` | oxlint + ESLint, with `--fix` |
|
|
140
202
|
|
|
141
203
|
## Not breaking the apps that use it
|
|
142
204
|
|
|
143
|
-
|
|
205
|
+
Four layers, cheapest first.
|
|
144
206
|
|
|
145
|
-
**Pinned ranges.** A consumer depends on `^0.
|
|
146
|
-
`>=0.
|
|
147
|
-
schedule, and a release can never reach an app that has not asked for it.
|
|
207
|
+
**Pinned ranges.** A consumer depends on `^0.4.0`, which at 0.x means
|
|
208
|
+
`>=0.4.0 <0.5.0` — publishing 0.5.0 upgrades nobody. Apps move on their own
|
|
209
|
+
schedule, and a release can never reach an app that has not asked for it. The
|
|
210
|
+
cost is the mirror image: an app that never asks never moves. Two of these
|
|
211
|
+
three sat two minors behind, so `PATCHNOTES.md` exists to make taking one a
|
|
212
|
+
short read.
|
|
148
213
|
|
|
149
214
|
**The public API test.** `src/__tests__/public-api.spec.ts` lists every export
|
|
150
215
|
by name. The kit compiles perfectly well without an export nothing here calls,
|
|
151
216
|
so removing one is invisible to every other test; this one fails loudly and
|
|
152
217
|
asks whether the version should be a major.
|
|
153
218
|
|
|
219
|
+
**The SSR test.** `src/__tests__/ssr.spec.ts` renders in the **node**
|
|
220
|
+
environment, not jsdom — jsdom supplies the very `document` a server lacks, and
|
|
221
|
+
passed all four of the SSR bugs 0.2.2 fixed.
|
|
222
|
+
|
|
154
223
|
**The consumer check.** `.github/workflows/consumer.yml` packs the tarball npm
|
|
155
|
-
would serve
|
|
156
|
-
types,
|
|
157
|
-
|
|
224
|
+
would serve and installs it into **all three** apps, running each one's full
|
|
225
|
+
gate — format, lint, types, tests, production build. Kakehashi's build is
|
|
226
|
+
`vite-ssg build`, so that job is also the real prerender.
|
|
158
227
|
|
|
159
228
|
That last one is the important one: the kit's own tests never import it the way
|
|
160
229
|
an app does.
|
|
@@ -169,6 +238,9 @@ pnpm version minor
|
|
|
169
238
|
git push --follow-tags
|
|
170
239
|
```
|
|
171
240
|
|
|
241
|
+
Then write the release into `PATCHNOTES.md` — what a consumer gains, and what
|
|
242
|
+
they have to do to take it.
|
|
243
|
+
|
|
172
244
|
## License
|
|
173
245
|
|
|
174
246
|
MIT
|
|
@@ -1,13 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kit's button, and — when asked — its link.
|
|
3
|
+
*
|
|
4
|
+
* `as` exists because a button and a link are the same shape and a different
|
|
5
|
+
* element, and the app was resolving that by nesting them: a consumer had
|
|
6
|
+
* `<RouterLink><BaseButton>` in every call to action, which is an `<a>` around
|
|
7
|
+
* a `<button>`. That is invalid HTML, two stops in the tab order and two
|
|
8
|
+
* controls to a screen reader, for one thing on the screen. Whether something
|
|
9
|
+
* navigates is the app's decision; carrying it is this component's job.
|
|
10
|
+
*
|
|
11
|
+
* `router-link` is resolved by name rather than imported, so `vue-router` stays
|
|
12
|
+
* the optional peer it is. Only an app that passes `as="router-link"` needs it,
|
|
13
|
+
* and an app that passes it has it.
|
|
14
|
+
*/
|
|
1
15
|
type __VLS_Props = {
|
|
16
|
+
/** What to render. `button` unless this navigates. */
|
|
17
|
+
as?: 'button' | 'a' | 'router-link' | undefined;
|
|
2
18
|
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | undefined;
|
|
3
|
-
size?: 'sm' | 'md' | undefined;
|
|
19
|
+
size?: 'sm' | 'md' | 'lg' | undefined;
|
|
4
20
|
loading?: boolean | undefined;
|
|
5
21
|
disabled?: boolean | undefined;
|
|
22
|
+
/** Ignored unless `as` is `button`. */
|
|
6
23
|
type?: 'button' | 'submit' | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Square, sized to its icon, with no label beside it.
|
|
26
|
+
*
|
|
27
|
+
* **Pass `aria-label`.** An icon on its own has no accessible name, and a
|
|
28
|
+
* control a screen reader announces as "button" is not usable. Attributes
|
|
29
|
+
* fall through, so `aria-label` lands where it should — nothing here can
|
|
30
|
+
* check that you passed one, which is why it is said this loudly.
|
|
31
|
+
*/
|
|
32
|
+
icon?: boolean | undefined;
|
|
33
|
+
/** Fills its container. The ordinary case under a form. */
|
|
34
|
+
block?: boolean | undefined;
|
|
35
|
+
/** For `as="router-link"`. */
|
|
36
|
+
to?: string | Record<string, unknown> | undefined;
|
|
37
|
+
/** For `as="a"`. */
|
|
38
|
+
href?: string | undefined;
|
|
7
39
|
};
|
|
8
|
-
declare var
|
|
40
|
+
declare var __VLS_8: {};
|
|
9
41
|
type __VLS_Slots = {} & {
|
|
10
|
-
default?: (props: typeof
|
|
42
|
+
default?: (props: typeof __VLS_8) => any;
|
|
11
43
|
};
|
|
12
44
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
13
45
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single checkbox, with its label beside it.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately not built on `FormField`. That component stacks a label above
|
|
5
|
+
* its control, which is right for every field where the control is a box you
|
|
6
|
+
* type into and wrong here: a checkbox is read as one sentence with a mark in
|
|
7
|
+
* front of it, and putting the words above the box breaks the association a
|
|
8
|
+
* sighted reader makes before they get to the accessible name.
|
|
9
|
+
*
|
|
10
|
+
* The whole row is the label, so the words are part of the hit target. On a
|
|
11
|
+
* phone that is the difference between a control and a coin toss.
|
|
12
|
+
*/
|
|
13
|
+
type __VLS_Props = {
|
|
14
|
+
label: string;
|
|
15
|
+
error?: string | undefined;
|
|
16
|
+
hint?: string | undefined;
|
|
17
|
+
disabled?: boolean | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* `md` is a setting: a line the reader came here to change, in ink.
|
|
20
|
+
* `sm` is an aside — "remember me" under a sign-in form, "show the ones I
|
|
21
|
+
* have learned" above a list — quieter and tighter.
|
|
22
|
+
*
|
|
23
|
+
* The two are not a guess. Of the five hand-written checkboxes across the
|
|
24
|
+
* three consuming apps, four were the aside and one was the setting, and
|
|
25
|
+
* they differed in exactly these two ways.
|
|
26
|
+
*/
|
|
27
|
+
size?: 'sm' | 'md' | undefined;
|
|
28
|
+
};
|
|
29
|
+
type __VLS_ModelProps = {
|
|
30
|
+
modelValue?: boolean;
|
|
31
|
+
};
|
|
32
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
33
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
34
|
+
"update:modelValue": (value: boolean) => any;
|
|
35
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
36
|
+
"onUpdate:modelValue"?: (value: boolean) => any;
|
|
37
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
38
|
+
declare const _default: typeof __VLS_export;
|
|
39
|
+
export default _default;
|
|
@@ -9,6 +9,8 @@ type __VLS_Props = {
|
|
|
9
9
|
*/
|
|
10
10
|
labelHidden?: boolean | undefined;
|
|
11
11
|
type?: 'text' | 'email' | 'password' | 'number' | undefined;
|
|
12
|
+
/** `sm` for a field inside a row rather than in a form of its own. */
|
|
13
|
+
size?: 'sm' | 'md' | undefined;
|
|
12
14
|
};
|
|
13
15
|
type __VLS_ModelProps = {
|
|
14
16
|
modelValue?: string | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A set of radios, and the reason there is no `BaseRadio`.
|
|
3
|
+
*
|
|
4
|
+
* One radio on its own is not a control — it is half of a choice that cannot
|
|
5
|
+
* be unmade, and every real use is a group. So the group is the component.
|
|
6
|
+
*
|
|
7
|
+
* `fieldset` and `legend` rather than a label: a label points at one element,
|
|
8
|
+
* and the thing being named here is the question, not any single answer. Left
|
|
9
|
+
* as a plain label, a screen reader reads the options with no idea what they
|
|
10
|
+
* are options for.
|
|
11
|
+
*/
|
|
12
|
+
type __VLS_Props = {
|
|
13
|
+
legend: string;
|
|
14
|
+
options: readonly {
|
|
15
|
+
value: string;
|
|
16
|
+
label: string;
|
|
17
|
+
disabled?: boolean | undefined;
|
|
18
|
+
}[];
|
|
19
|
+
error?: string | undefined;
|
|
20
|
+
hint?: string | undefined;
|
|
21
|
+
legendHidden?: boolean | undefined;
|
|
22
|
+
};
|
|
23
|
+
type __VLS_ModelProps = {
|
|
24
|
+
modelValue?: string | undefined;
|
|
25
|
+
};
|
|
26
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
27
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
28
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
29
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
30
|
+
"onUpdate:modelValue"?: (value: string | undefined) => any;
|
|
31
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
32
|
+
declare const _default: typeof __VLS_export;
|
|
33
|
+
export default _default;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A native `<select>`, wearing the kit's field.
|
|
3
|
+
*
|
|
4
|
+
* Native on purpose. A custom listbox has to reimplement typeahead, the
|
|
5
|
+
* keyboard, and the way a phone lifts the options into its own picker — and it
|
|
6
|
+
* gets one of them wrong. What is worth replacing is the chrome, so the arrow
|
|
7
|
+
* is drawn and the browser's own is removed.
|
|
8
|
+
*
|
|
9
|
+
* Options are passed rather than slotted so the value can be anything and the
|
|
10
|
+
* label can be a translated string the kit never sees.
|
|
11
|
+
*/
|
|
12
|
+
type __VLS_Props = {
|
|
13
|
+
label: string;
|
|
14
|
+
options: readonly {
|
|
15
|
+
value: string;
|
|
16
|
+
label: string;
|
|
17
|
+
disabled?: boolean | undefined;
|
|
18
|
+
}[];
|
|
19
|
+
error?: string | undefined;
|
|
20
|
+
hint?: string | undefined;
|
|
21
|
+
labelHidden?: boolean | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* An unselectable first row, for a field with no sensible default.
|
|
24
|
+
*
|
|
25
|
+
* Disabled rather than merely empty: an empty option that can be chosen lets
|
|
26
|
+
* someone go back to having answered nothing, which no form wants.
|
|
27
|
+
*/
|
|
28
|
+
placeholder?: string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* `sm` for a select that filters or sorts rather than answers a form.
|
|
31
|
+
*
|
|
32
|
+
* Every hand-written select across the three consuming apps was this one.
|
|
33
|
+
*/
|
|
34
|
+
size?: 'sm' | 'md' | undefined;
|
|
35
|
+
};
|
|
36
|
+
type __VLS_ModelProps = {
|
|
37
|
+
modelValue?: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
40
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
41
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
42
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
43
|
+
"onUpdate:modelValue"?: (value: string | undefined) => any;
|
|
44
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
45
|
+
declare const _default: typeof __VLS_export;
|
|
46
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
label: string;
|
|
3
|
+
error?: string | undefined;
|
|
4
|
+
hint?: string | undefined;
|
|
5
|
+
labelHidden?: boolean | undefined;
|
|
6
|
+
rows?: number | undefined;
|
|
7
|
+
/** `sm` for editing in place, inside a row that is already small. */
|
|
8
|
+
size?: 'sm' | 'md' | undefined;
|
|
9
|
+
};
|
|
10
|
+
type __VLS_ModelProps = {
|
|
11
|
+
modelValue?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
14
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
15
|
+
"update:modelValue": (value: string | undefined) => any;
|
|
16
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
17
|
+
"onUpdate:modelValue"?: (value: string | undefined) => any;
|
|
18
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const _default: typeof __VLS_export;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A label, a hint, an error, and the wiring between them.
|
|
3
|
+
*
|
|
4
|
+
* This was inside `BaseInput`, which is why the kit had one form control
|
|
5
|
+
* instead of five. The hard part of a field is not the `<input>` — it is
|
|
6
|
+
* generating an id, pointing the label at it, deciding whether the description
|
|
7
|
+
* is the hint or the error, and telling assistive tech which one to read. That
|
|
8
|
+
* is identical for a select, a textarea and an input, and every app that
|
|
9
|
+
* needed one of the other two wrote the whole thing again.
|
|
10
|
+
*
|
|
11
|
+
* The control comes in through the slot and is handed what it needs to be
|
|
12
|
+
* described. It is a slot rather than a prop so the field never has to know
|
|
13
|
+
* what it is wrapping.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```vue
|
|
17
|
+
* <FormField :label="t('profile.name')" :error="errors.name">
|
|
18
|
+
* <template #default="{ id, describedBy, invalid }">
|
|
19
|
+
* <input :id="id" :aria-describedby="describedBy" :aria-invalid="invalid" />
|
|
20
|
+
* </template>
|
|
21
|
+
* </FormField>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
type __VLS_Props = {
|
|
25
|
+
label: string;
|
|
26
|
+
error?: string | undefined;
|
|
27
|
+
hint?: string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* `sm` for a control that sits inside something else — a toolbar, a filter
|
|
30
|
+
* row, a settings line — rather than in a form of its own.
|
|
31
|
+
*
|
|
32
|
+
* It exists because every hand-written select in all three apps was the
|
|
33
|
+
* small one, and the kit only had the large one. A part is not reusable if
|
|
34
|
+
* reaching for it costs a size somebody chose on purpose.
|
|
35
|
+
*/
|
|
36
|
+
size?: 'sm' | 'md' | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Hides the label visually but keeps it for assistive tech. For fields whose
|
|
39
|
+
* surrounding row already names them — dropping the label entirely would
|
|
40
|
+
* leave the control with no accessible name at all.
|
|
41
|
+
*/
|
|
42
|
+
labelHidden?: boolean | undefined;
|
|
43
|
+
};
|
|
44
|
+
declare var __VLS_1: {
|
|
45
|
+
id: string;
|
|
46
|
+
describedBy: string | undefined;
|
|
47
|
+
invalid: boolean;
|
|
48
|
+
size: "sm" | "md";
|
|
49
|
+
};
|
|
50
|
+
type __VLS_Slots = {} & {
|
|
51
|
+
default?: (props: typeof __VLS_1) => any;
|
|
52
|
+
};
|
|
53
|
+
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
54
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
55
|
+
declare const _default: typeof __VLS_export;
|
|
56
|
+
export default _default;
|
|
57
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
58
|
+
new (): {
|
|
59
|
+
$slots: S;
|
|
60
|
+
};
|
|
61
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -47,7 +47,12 @@ export { default as BaseButton } from './components/BaseButton.vue';
|
|
|
47
47
|
export { default as BaseInput } from './components/BaseInput.vue';
|
|
48
48
|
export { default as BaseSheet } from './components/BaseSheet.vue';
|
|
49
49
|
export { default as BaseCard } from './components/BaseCard.vue';
|
|
50
|
+
export { default as BaseCheckbox } from './components/BaseCheckbox.vue';
|
|
51
|
+
export { default as BaseRadioGroup } from './components/BaseRadioGroup.vue';
|
|
52
|
+
export { default as BaseSelect } from './components/BaseSelect.vue';
|
|
53
|
+
export { default as BaseTextarea } from './components/BaseTextarea.vue';
|
|
50
54
|
export { default as EmptyState } from './components/EmptyState.vue';
|
|
55
|
+
export { default as FormField } from './components/FormField.vue';
|
|
51
56
|
export { default as ErrorBoundary } from './components/ErrorBoundary.vue';
|
|
52
57
|
export { default as PageContainer } from './components/PageContainer.vue';
|
|
53
58
|
export { default as PageHeader } from './components/PageHeader.vue';
|