rei-kit 4.1.1 → 4.3.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 +7 -7
- package/dist/check.js +94 -0
- package/dist/check.js.map +1 -1
- package/dist/components/BaseCheckboxGroup.vue.d.ts +59 -0
- package/dist/components/SectionHeading.vue.d.ts +15 -5
- package/dist/index.d.ts +20 -2
- package/dist/index.js +104 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rei-kit
|
|
2
2
|
|
|
3
|
-
**One kit. Every look.**
|
|
3
|
+
**One kit. Every look.** 106 accessible components for Vue 3 and Tailwind 4.
|
|
4
4
|
Change the **material** with one attribute and the **palette** with another —
|
|
5
5
|
every component follows, and none of them knows your brand.
|
|
6
6
|
|
|
@@ -83,7 +83,7 @@ go red, and several of them exist nowhere else:
|
|
|
83
83
|
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
84
|
| **`direction-styles.spec.ts`** | One case per file, reading each stylesheet for a property that picks a side — `padding-left` where `padding-inline-start` was meant — and for a one-sided gradient with no right-to-left counterpart. It found 61 on its first run. |
|
|
85
85
|
| **`direction-keys.spec.ts`** | One case per control, pressing **ArrowLeft in a right-to-left document** and asserting it went *forward*. Ten components were walking backwards through themselves. It type-checks, it renders, it passes axe, and only an Arabic reader sees it. |
|
|
86
|
-
| **`examples-axe.spec.ts`** | axe on all
|
|
86
|
+
| **`examples-axe.spec.ts`** | axe on all 106 usage examples, **and again with each one opened** — a menu, a dialog, a combobox list is exactly where the ARIA lives and where a closed-state audit says nothing. |
|
|
87
87
|
| **`focus.spec.ts`** | Reads every component for a focus ring. A missing one compiles, renders and passes every other check. |
|
|
88
88
|
| **`public-api.spec.ts`** | Names every runtime export of all six entries. A kit compiles fine without an export nothing inside it calls. |
|
|
89
89
|
| **`type-surface.ts`** | Re-exports every published type and is type-checked twice, because a test cannot see types — they are gone by the time one runs. |
|
|
@@ -165,7 +165,7 @@ app, and the case *least* favourable to this kit:
|
|
|
165
165
|
|
|
166
166
|
| Kit | JS | CSS | Total |
|
|
167
167
|
| -------------------- | ---------: | ----------: | ----------: |
|
|
168
|
-
| **rei-kit 4.
|
|
168
|
+
| **rei-kit 4.3.0** | **3.7 KB** | **23.2 KB** | **26.8 KB** |
|
|
169
169
|
| element-plus 2.14.6 | 27.3 KB | 6.0 KB | 33.3 KB |
|
|
170
170
|
| naive-ui 2.45.3 | 51.2 KB | — | 51.2 KB |
|
|
171
171
|
| primevue 5.0.1 | 53.6 KB | — | 53.6 KB |
|
|
@@ -177,7 +177,7 @@ a data table, a tooltip and a card. A screen rather than a demo:
|
|
|
177
177
|
|
|
178
178
|
| Kit | JS | CSS | Total |
|
|
179
179
|
| -------------------- | ---------: | ----------: | -----------: |
|
|
180
|
-
| **rei-kit 4.
|
|
180
|
+
| **rei-kit 4.3.0** | **8.6 KB** | **23.2 KB** | **31.8 KB** |
|
|
181
181
|
| element-plus 2.14.6 | 90.1 KB | 14.0 KB | 104.1 KB |
|
|
182
182
|
| naive-ui 2.45.3 | 132.4 KB | — | 132.4 KB |
|
|
183
183
|
| primevue 5.0.1 | 135.8 KB | — | 135.8 KB |
|
|
@@ -191,7 +191,7 @@ the same seven cost them **60 to 125 KB**. A quarter smaller at three
|
|
|
191
191
|
components; **3.3× smaller at ten**.
|
|
192
192
|
|
|
193
193
|
That is the trade, stated rather than left to be inferred: rei-kit's CSS
|
|
194
|
-
column is the entire `mobile.css` preset — all
|
|
194
|
+
column is the entire `mobile.css` preset — all 106 components, four materials,
|
|
195
195
|
ten palettes — and it is the same 23.1 KB whether an app imports three
|
|
196
196
|
components or every one of them. An app using very little of the kit carries
|
|
197
197
|
stylesheet it does not need; an app using a screenful stops paying anything
|
|
@@ -222,7 +222,7 @@ Each claim here is enforced by something that fails, not by a promise.
|
|
|
222
222
|
|
|
223
223
|
| Standard | How it is held |
|
|
224
224
|
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
225
|
-
| **Open for extension, closed for edits** | A new look is a token, a material or a palette — never an edit to a component. Materials and palettes restyle all
|
|
225
|
+
| **Open for extension, closed for edits** | A new look is a token, a material or a palette — never an edit to a component. Materials and palettes restyle all 106 without touching one, and a test fails if a component holds a colour. |
|
|
226
226
|
| **Single responsibility** | One part, one job. Label-and-error wiring lives in `FormField`, not in five inputs; taking the page behind a layer out of reach lives in one helper the modal, the sheet and the guide share. |
|
|
227
227
|
| **Depend on roles, not values** | Components read `primary`, `surface`, `--shadow-card` — never a hex or a pixel shadow. An app's brand wins in both modes, tested. |
|
|
228
228
|
| **WAI-ARIA Authoring Practices** | Menus, comboboxes, tabs, sliders, dialogs and accordions follow their APG pattern: arrows move, Tab leaves, Escape closes, focus returns. Behaviour tests drive each with the keyboard. |
|
|
@@ -238,7 +238,7 @@ Each claim here is enforced by something that fails, not by a promise.
|
|
|
238
238
|
|
|
239
239
|
| | |
|
|
240
240
|
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
241
|
-
| Components |
|
|
241
|
+
| Components | 106 (`AuthForm`, `BaseTable`, `BaseCombobox`, `BaseSlider`, `TabShell`, `BaseModal`, `BaseTabs`, `BaseTooltip`, `BasePagination`, `BaseBreadcrumb`, `BaseDisclosure`, `BaseAccordion`, `NavLinks`, `OfflineBanner`, `FabButton`, `BaseButton`, `BaseCard`, `BaseInput`, `BaseSelect`, `BaseTextarea`, `BaseCheckbox`, `BaseSwitch`, `BaseRadioGroup`, `BaseMenu`, `BaseAvatar`, `BaseSpinner`, `BaseAlert`, `BaseBadge`, `BaseSheet`, `ProgressBar`, `PriceCard`, `ToastHost`, `TabBar`, `GoogleButton`, `LocaleLinks`, `LocaleSheet`, `AuthShell`, `TourShell`, `InstallPrompt`, `UpdatePrompt`, `InstallSettings`, `SkeletonList`, `PageContainer`, `ErrorBoundary`, etc.) |
|
|
242
242
|
| Composables | 14 (`useToast`, `useTheme`, `useMaterial`, `usePalette`, `useToday`, `useMediaQuery`, `useOnline`, `useInstall`, `useSnooze`, `useThemeSync`, `useVisualViewport`, `useDragScroll`, `useDebouncedCallback`, `useAnnounce`) |
|
|
243
243
|
| Utilities | 48 functions and 5 constants (`applyTheme`, `applyMaterial`, `applyPalette`, `MATERIALS`, `PALETTES`, `formatDate`, `fieldErrors`, `toAuthMessageKey`, `createAuthGuard`, `createQueryDefaults`, `createWriteReport`, `toRedirectPath`, `Supabase error mapper`, i18n runtime, etc.) |
|
|
244
244
|
| Entry Points | `rei-kit`, `rei-kit/app`, `rei-kit/web`, `rei-kit/pwa`, `rei-kit/motion`, `rei-kit/supabase`, `rei-kit/mobile.css`, `rei-kit/web.css`, and each stylesheet on its own |
|
package/dist/check.js
CHANGED
|
@@ -54,6 +54,87 @@ function darkRoles(css) {
|
|
|
54
54
|
}
|
|
55
55
|
return colourRoles(bodies.join("\n"));
|
|
56
56
|
}
|
|
57
|
+
/** The five roles a component fills and then writes on. */
|
|
58
|
+
var FILLED_ROLES = [
|
|
59
|
+
"primary",
|
|
60
|
+
"accent",
|
|
61
|
+
"positive",
|
|
62
|
+
"negative",
|
|
63
|
+
"warning"
|
|
64
|
+
];
|
|
65
|
+
/** Every `--color-<name>: <value>` of a block, as declared. */
|
|
66
|
+
function colourValues(css) {
|
|
67
|
+
return new Map([...css.matchAll(/--color-([a-z0-9-]+)\s*:\s*([^;]+);/g)].map(([, name, value]) => [name, value.trim()]));
|
|
68
|
+
}
|
|
69
|
+
/** The body of a rule, from its opening brace to the matching one. */
|
|
70
|
+
function bodies(css, opening) {
|
|
71
|
+
const found = [];
|
|
72
|
+
for (const match of css.matchAll(opening)) {
|
|
73
|
+
let depth = 1;
|
|
74
|
+
let at = match.index + match[0].length;
|
|
75
|
+
while (at < css.length && depth > 0) {
|
|
76
|
+
if (css[at] === "{") depth += 1;
|
|
77
|
+
else if (css[at] === "}") depth -= 1;
|
|
78
|
+
at += 1;
|
|
79
|
+
}
|
|
80
|
+
found.push(css.slice(match.index + match[0].length, at - 1));
|
|
81
|
+
}
|
|
82
|
+
return found.join("\n");
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A role's value as six hex digits, following aliases.
|
|
86
|
+
*
|
|
87
|
+
* An app that names its colours by pigment reaches the roles through
|
|
88
|
+
* `var(--color-sea)`, so a reader that only accepts a literal sees nothing to
|
|
89
|
+
* measure in the one stylesheet in this family written that way. Anything
|
|
90
|
+
* else — `oklch()`, `color-mix()`, a value from outside this stylesheet —
|
|
91
|
+
* returns null and is skipped rather than guessed at.
|
|
92
|
+
*/
|
|
93
|
+
function hex(values, role, seen = /* @__PURE__ */ new Set()) {
|
|
94
|
+
const value = values.get(role);
|
|
95
|
+
if (!value || seen.has(role)) return null;
|
|
96
|
+
seen.add(role);
|
|
97
|
+
const alias = /^var\(--color-([a-z0-9-]+)\)$/.exec(value);
|
|
98
|
+
if (alias) return hex(values, alias[1], seen);
|
|
99
|
+
const literal = /^#([0-9a-f]{6})$/i.exec(value);
|
|
100
|
+
return literal ? `#${literal[1].toLowerCase()}` : null;
|
|
101
|
+
}
|
|
102
|
+
function channels(colour) {
|
|
103
|
+
const n = Number.parseInt(colour.slice(1), 16);
|
|
104
|
+
return [
|
|
105
|
+
n >> 16 & 255,
|
|
106
|
+
n >> 8 & 255,
|
|
107
|
+
n & 255
|
|
108
|
+
].map((value) => {
|
|
109
|
+
const c = value / 255;
|
|
110
|
+
return c <= .04045 ? c / 12.92 : ((c + .055) / 1.055) ** 2.4;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** WCAG contrast, the ratio the kit's own palettes are measured against. */
|
|
114
|
+
function contrast(a, b) {
|
|
115
|
+
const luminance = (colour) => {
|
|
116
|
+
const [r, g, b] = channels(colour);
|
|
117
|
+
return .2126 * r + .7152 * g + .0722 * b;
|
|
118
|
+
};
|
|
119
|
+
const [x, y] = [luminance(a), luminance(b)];
|
|
120
|
+
return (Math.max(x, y) + .05) / (Math.min(x, y) + .05);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* What `tokens.css` derives for an `on-` role the app leaves alone.
|
|
124
|
+
*
|
|
125
|
+
* The same arithmetic as the `oklch(from … clamp(0, (0.6 - l) * 1000, 1) 0 0)`
|
|
126
|
+
* net there: black or white, whichever is further from the fill's lightness.
|
|
127
|
+
* Repeated rather than read out of the stylesheet, because a check that
|
|
128
|
+
* evaluates a `calc()` is a CSS engine; this is one threshold, and
|
|
129
|
+
* `check.spec.ts` holds the two to the same answer.
|
|
130
|
+
*/
|
|
131
|
+
function derivedInk(fill) {
|
|
132
|
+
const [r, g, b] = channels(fill);
|
|
133
|
+
const l = Math.cbrt(.4122214708 * r + .5363325363 * g + .0514459929 * b);
|
|
134
|
+
const m = Math.cbrt(.2119034982 * r + .6806995451 * g + .1073969566 * b);
|
|
135
|
+
const s = Math.cbrt(.0883024619 * r + .2817188376 * g + .6299787005 * b);
|
|
136
|
+
return .2104542553 * l + .793617785 * m - .0040720468 * s < .6 ? "#ffffff" : "#000000";
|
|
137
|
+
}
|
|
57
138
|
/**
|
|
58
139
|
* What is missing from an app's stylesheet, as a list to assert is empty.
|
|
59
140
|
*
|
|
@@ -117,6 +198,19 @@ function checkStyling({ css, tokens }) {
|
|
|
117
198
|
message: `These roles are redefined for the day and left at the kit's own values at night, because \`@theme\` lands in \`:root\` and the kit's \`.dark\` comes after it: ${redefined.join(", ")}.`,
|
|
118
199
|
fix: ".dark { --color-primary: …; } — restate each one under `.dark` as well"
|
|
119
200
|
});
|
|
201
|
+
const theme = colourValues(bodies(css, /@theme[^{]*\{/g));
|
|
202
|
+
const night = new Map([...theme, ...colourValues(bodies(css, /(?<![\w):])\.dark\s*\{/g))]);
|
|
203
|
+
const unreadable = FILLED_ROLES.flatMap((role) => [["day", theme], ["night", night]].flatMap(([mode, values]) => {
|
|
204
|
+
const fill = hex(values, role);
|
|
205
|
+
if (!fill) return [];
|
|
206
|
+
const ink = hex(values, `on-${role}`) ?? derivedInk(fill);
|
|
207
|
+
const ratio = contrast(fill, ink);
|
|
208
|
+
return ratio >= 4.5 ? [] : [`${role} by ${mode} (${fill} under ${ink}, ${ratio.toFixed(2)}:1)`];
|
|
209
|
+
}));
|
|
210
|
+
if (unreadable.length) problems.push({
|
|
211
|
+
message: `Text on these filled roles is below WCAG AA, so a label on one of them is unreadable at normal size: ${unreadable.join("; ")}.`,
|
|
212
|
+
fix: "move the role, or declare its `--color-on-<role>` as a colour measured against it"
|
|
213
|
+
});
|
|
120
214
|
return problems;
|
|
121
215
|
}
|
|
122
216
|
//#endregion
|
package/dist/check.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","names":[],"sources":["../src/check/index.ts"],"sourcesContent":["/**\n * The wiring that fails silently, checked at unit-test speed.\n *\n * Every line of this package's install can be left out and leave a build that\n * is green: a missing `@source` means Tailwind never emits the utilities the\n * components ask for, a missing stylesheet import means the components keep\n * their markup and lose their layout, and a colour role the app never defines\n * makes `bg-primary` emit no declaration at all. None of it is visible to\n * `vue-tsc`, to a component test, or to a reader of the diff. One of the apps\n * in this family shipped a release with its tab bar invisible exactly this\n * way.\n *\n * Both apps then wrote the same check, independently, with three of the four\n * assertions carrying the same names — which is this kit's own test for a\n * missing part. So the check moved here, and it knows one thing their copies\n * could not: that the preset exists and carries the `@source` and the\n * component styles with it.\n *\n * Nothing here reads a file or touches the DOM. The caller supplies the two\n * stylesheets as text, because that is the only part that differs between a\n * Vitest run, a Node script and a CI step.\n */\n\n/** A problem found in an app's stylesheet, written to be read in a failure. */\nexport type StylingProblem = {\n /** What is wrong, in one sentence. */\n message: string\n /** The line to add, where there is a single obvious one. */\n fix?: string\n}\n\nexport type StylingInput = {\n /** The app's own stylesheet — the file that imports Tailwind. */\n css: string\n /** `node_modules/rei-kit/dist/tokens.css`, for the roles it declares. */\n tokens: string\n}\n\n/** Every `--color-<name>` a stylesheet declares. */\nfunction colourRoles(css: string): Set<string> {\n return new Set([...css.matchAll(/--color-([a-z0-9-]+)\\s*:/g)].map(([, name]) => name!))\n}\n\n/**\n * Whether the app imports one of the kit's stylesheets, however it resolves it.\n *\n * Matched on the file rather than on `rei-kit/…`, because a workspace that\n * builds the kit alongside the app reaches it by path — the kit's own\n * showcase does, and the first version of this told it three times that it\n * had not imported what it plainly had. A check that cries wolf is one\n * nobody reads.\n */\nfunction imports(css: string, file: string): boolean {\n return new RegExp(`@import\\\\s+['\"][^'\"]*${file.replace('.', '\\\\.')}['\"]`).test(css)\n}\n\n/** Whether it is the published package rather than a path into a workspace. */\nfunction byPackage(css: string): boolean {\n return /@import\\s+['\"]rei-kit\\//.test(css)\n}\n\n/**\n * The roles a rule in the dark sets, where that rule can beat `:root`.\n *\n * The kit writes two kinds, and the difference is the whole point. A plain\n * `.dark { … }` has the same specificity as `:root` and is emitted after it,\n * so it wins — an app that restates one of those roles in `@theme` and not\n * under `.dark` keeps the kit's colour at night. A `:where(.dark) { … }`\n * carries no specificity at all, which is exactly why the kit puts the\n * filled roles there: an app's own brand has to survive after dark, and\n * before that block existed the kit's lightening beat it.\n *\n * So only the first kind is something an app has to answer, and a reader\n * that lumps them together reports nine roles that are already correct. It\n * did, once, against a real app — which is how this comment came to be here\n * rather than in the version that shipped.\n *\n * The body runs to its matching brace, not the next one: these blocks hold\n * comments and nested rules. And it is found from a rule rather than from\n * the word, or a comment mentioning `.dark` starts the slice and swallows\n * the stylesheet.\n */\nfunction darkRoles(css: string): Set<string> {\n const bodies: string[] = []\n\n /* Not preceded by `)` or a word character, so `:where(.dark)` and\n `.dark [data-palette]` are both left out — the first for specificity,\n the second because it answers a palette rather than the app. */\n for (const match of css.matchAll(/(?<![\\w):])\\.dark\\s*\\{/g)) {\n let depth = 1\n let at = match.index! + match[0].length\n\n while (at < css.length && depth > 0) {\n if (css[at] === '{') depth += 1\n else if (css[at] === '}') depth -= 1\n at += 1\n }\n\n bodies.push(css.slice(match.index! + match[0].length, at - 1))\n }\n\n return colourRoles(bodies.join('\\n'))\n}\n\n/**\n * What is missing from an app's stylesheet, as a list to assert is empty.\n *\n * ```ts\n * import { checkStyling } from 'rei-kit/check'\n *\n * it('is wired to the kit', () => {\n * expect(\n * checkStyling({\n * css: readFileSync('src/assets/main.css', 'utf8'),\n * tokens: readFileSync('node_modules/rei-kit/dist/tokens.css', 'utf8'),\n * }),\n * ).toEqual([])\n * })\n * ```\n */\nexport function checkStyling({ css, tokens }: StylingInput): StylingProblem[] {\n const problems: StylingProblem[] = []\n\n /* The preset is tokens, shell, materials, palettes, the compiled component\n styles and the `@source` — so an app that imports one has answered every\n question below at once, and asking them separately would report faults it\n does not have. */\n const preset = imports(css, 'mobile.css') || imports(css, 'web.css')\n\n if (preset && imports(css, 'mobile.css') && imports(css, 'web.css')) {\n problems.push({\n message:\n 'Both presets are imported. They are two answers to the same question, and the second one wins wherever they disagree.',\n fix: \"keep one of @import 'rei-kit/mobile.css' or @import 'rei-kit/web.css'\",\n })\n }\n\n if (!preset) {\n if (!imports(css, 'styles.css')) {\n problems.push({\n message:\n 'The compiled component styles are not imported, so every component keeps its markup and loses its layout. Nothing else reports this: the build is green and the page renders.',\n fix: \"@import 'rei-kit/mobile.css'; /* or web.css — the preset carries the rest too */\",\n })\n }\n\n /* Only asked of an app that installed the package. A workspace reaching\n the kit by path points Tailwind at wherever it keeps the source, and\n guessing that path is not this check's business. */\n if (byPackage(css) && !/@source\\s+['\"][^'\"]*node_modules\\/rei-kit[^'\"]*['\"]/.test(css)) {\n problems.push({\n message:\n 'Tailwind is not told to scan the kit, so it emits no utility the components ask for. It does not walk node_modules unless pointed at it.',\n fix: \"@source '<relative path>/node_modules/rei-kit/dist';\",\n })\n }\n }\n\n /* Checked last and only when the roles cannot be arriving from the kit\n itself, so the list reports what the app has to define rather than\n repeating the import it is already being told about. */\n const declared = colourRoles(css)\n if (!preset && !imports(css, 'tokens.css')) {\n const missing = [...colourRoles(tokens)].filter((role) => !declared.has(role))\n\n if (missing.length) {\n problems.push({\n message: `These colour roles are never defined, so every utility built on them emits nothing: ${missing.join(', ')}.`,\n fix: \"@import 'rei-kit/tokens.css';\",\n })\n }\n }\n\n /* Measured, because the instruction had been written down and never\n enforced: with the material imported first, `--surface-opacity` computes\n to 100% under `data-material=\"glass\"` instead of 56% — the material is\n simply inert. Both files set the same custom properties, `:root` and\n `[data-material]` carry the same specificity, and equal specificity is\n settled by source order. The build is green, the attribute is on the\n element, and nothing happens. */\n if (!preset) {\n const at = (file: string) => {\n const found = new RegExp(`@import\\\\s+['\"][^'\"]*${file.replace('.', '\\\\.')}['\"]`).exec(css)\n return found ? found.index : -1\n }\n\n const tokensAt = at('tokens.css')\n /* Materials and palettes only. Both reset properties `tokens.css` also\n sets — 17 of them for the palettes — under a selector of the same\n specificity, so order settles it. `motion.css` shares no property with\n `tokens.css` at all, so its position cannot matter, and 4.1.0 asked\n about it anyway: a rule that reports a setup nothing is wrong with is\n the kind that gets switched off. */\n const late = ['materials.css', 'palettes.css']\n .map((file) => ({ file, at: at(file) }))\n .filter((entry) => entry.at !== -1 && tokensAt !== -1 && entry.at < tokensAt)\n\n if (late.length) {\n problems.push({\n message: `${late.map((entry) => entry.file).join(' and ')} must be imported after tokens.css, or the values they set are overwritten and the attribute they answer to does nothing.`,\n fix: \"@import 'rei-kit/tokens.css'; then the rest\",\n })\n }\n }\n\n /* The trap an app found before this check existed, and the sharpest one\n here: `@theme` compiles to `:root`, which Tailwind emits near the top of\n the stylesheet, while the kit's `.dark` block arrives after it. A role\n the app restates in `@theme` but not under `.dark` therefore keeps the\n kit's value at night — so the app comes up in somebody else's colours\n after dark, with a green build and every other check passing. */\n const kitDark = darkRoles(tokens)\n const appDark = darkRoles(css)\n const redefined = [...declared].filter((role) => kitDark.has(role) && !appDark.has(role))\n\n if (redefined.length) {\n problems.push({\n message: `These roles are redefined for the day and left at the kit's own values at night, because \\`@theme\\` lands in \\`:root\\` and the kit's \\`.dark\\` comes after it: ${redefined.join(', ')}.`,\n fix: '.dark { --color-primary: …; } — restate each one under `.dark` as well',\n })\n }\n\n return problems\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"check.js","names":[],"sources":["../src/check/index.ts"],"sourcesContent":["/**\n * The wiring that fails silently, checked at unit-test speed.\n *\n * Every line of this package's install can be left out and leave a build that\n * is green: a missing `@source` means Tailwind never emits the utilities the\n * components ask for, a missing stylesheet import means the components keep\n * their markup and lose their layout, and a colour role the app never defines\n * makes `bg-primary` emit no declaration at all. None of it is visible to\n * `vue-tsc`, to a component test, or to a reader of the diff. One of the apps\n * in this family shipped a release with its tab bar invisible exactly this\n * way.\n *\n * Both apps then wrote the same check, independently, with three of the four\n * assertions carrying the same names — which is this kit's own test for a\n * missing part. So the check moved here, and it knows one thing their copies\n * could not: that the preset exists and carries the `@source` and the\n * component styles with it.\n *\n * Nothing here reads a file or touches the DOM. The caller supplies the two\n * stylesheets as text, because that is the only part that differs between a\n * Vitest run, a Node script and a CI step.\n */\n\n/** A problem found in an app's stylesheet, written to be read in a failure. */\nexport type StylingProblem = {\n /** What is wrong, in one sentence. */\n message: string\n /** The line to add, where there is a single obvious one. */\n fix?: string\n}\n\nexport type StylingInput = {\n /** The app's own stylesheet — the file that imports Tailwind. */\n css: string\n /** `node_modules/rei-kit/dist/tokens.css`, for the roles it declares. */\n tokens: string\n}\n\n/** Every `--color-<name>` a stylesheet declares. */\nfunction colourRoles(css: string): Set<string> {\n return new Set([...css.matchAll(/--color-([a-z0-9-]+)\\s*:/g)].map(([, name]) => name!))\n}\n\n/**\n * Whether the app imports one of the kit's stylesheets, however it resolves it.\n *\n * Matched on the file rather than on `rei-kit/…`, because a workspace that\n * builds the kit alongside the app reaches it by path — the kit's own\n * showcase does, and the first version of this told it three times that it\n * had not imported what it plainly had. A check that cries wolf is one\n * nobody reads.\n */\nfunction imports(css: string, file: string): boolean {\n return new RegExp(`@import\\\\s+['\"][^'\"]*${file.replace('.', '\\\\.')}['\"]`).test(css)\n}\n\n/** Whether it is the published package rather than a path into a workspace. */\nfunction byPackage(css: string): boolean {\n return /@import\\s+['\"]rei-kit\\//.test(css)\n}\n\n/**\n * The roles a rule in the dark sets, where that rule can beat `:root`.\n *\n * The kit writes two kinds, and the difference is the whole point. A plain\n * `.dark { … }` has the same specificity as `:root` and is emitted after it,\n * so it wins — an app that restates one of those roles in `@theme` and not\n * under `.dark` keeps the kit's colour at night. A `:where(.dark) { … }`\n * carries no specificity at all, which is exactly why the kit puts the\n * filled roles there: an app's own brand has to survive after dark, and\n * before that block existed the kit's lightening beat it.\n *\n * So only the first kind is something an app has to answer, and a reader\n * that lumps them together reports nine roles that are already correct. It\n * did, once, against a real app — which is how this comment came to be here\n * rather than in the version that shipped.\n *\n * The body runs to its matching brace, not the next one: these blocks hold\n * comments and nested rules. And it is found from a rule rather than from\n * the word, or a comment mentioning `.dark` starts the slice and swallows\n * the stylesheet.\n */\nfunction darkRoles(css: string): Set<string> {\n const bodies: string[] = []\n\n /* Not preceded by `)` or a word character, so `:where(.dark)` and\n `.dark [data-palette]` are both left out — the first for specificity,\n the second because it answers a palette rather than the app. */\n for (const match of css.matchAll(/(?<![\\w):])\\.dark\\s*\\{/g)) {\n let depth = 1\n let at = match.index! + match[0].length\n\n while (at < css.length && depth > 0) {\n if (css[at] === '{') depth += 1\n else if (css[at] === '}') depth -= 1\n at += 1\n }\n\n bodies.push(css.slice(match.index! + match[0].length, at - 1))\n }\n\n return colourRoles(bodies.join('\\n'))\n}\n\n/** The five roles a component fills and then writes on. */\nconst FILLED_ROLES = ['primary', 'accent', 'positive', 'negative', 'warning'] as const\n\n/** Every `--color-<name>: <value>` of a block, as declared. */\nfunction colourValues(css: string): Map<string, string> {\n return new Map(\n [...css.matchAll(/--color-([a-z0-9-]+)\\s*:\\s*([^;]+);/g)].map(([, name, value]) => [\n name!,\n value!.trim(),\n ]),\n )\n}\n\n/** The body of a rule, from its opening brace to the matching one. */\nfunction bodies(css: string, opening: RegExp): string {\n const found: string[] = []\n\n for (const match of css.matchAll(opening)) {\n let depth = 1\n let at = match.index! + match[0].length\n\n while (at < css.length && depth > 0) {\n if (css[at] === '{') depth += 1\n else if (css[at] === '}') depth -= 1\n at += 1\n }\n\n found.push(css.slice(match.index! + match[0].length, at - 1))\n }\n\n return found.join('\\n')\n}\n\n/**\n * A role's value as six hex digits, following aliases.\n *\n * An app that names its colours by pigment reaches the roles through\n * `var(--color-sea)`, so a reader that only accepts a literal sees nothing to\n * measure in the one stylesheet in this family written that way. Anything\n * else — `oklch()`, `color-mix()`, a value from outside this stylesheet —\n * returns null and is skipped rather than guessed at.\n */\nfunction hex(values: Map<string, string>, role: string, seen = new Set<string>()): string | null {\n const value = values.get(role)\n if (!value || seen.has(role)) return null\n seen.add(role)\n\n const alias = /^var\\(--color-([a-z0-9-]+)\\)$/.exec(value)\n if (alias) return hex(values, alias[1]!, seen)\n\n const literal = /^#([0-9a-f]{6})$/i.exec(value)\n return literal ? `#${literal[1]!.toLowerCase()}` : null\n}\n\nfunction channels(colour: string): [number, number, number] {\n const n = Number.parseInt(colour.slice(1), 16)\n\n return [(n >> 16) & 255, (n >> 8) & 255, n & 255].map((value) => {\n const c = value / 255\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4\n }) as [number, number, number]\n}\n\n/** WCAG contrast, the ratio the kit's own palettes are measured against. */\nfunction contrast(a: string, b: string): number {\n const luminance = (colour: string) => {\n const [r, g, b] = channels(colour)\n return 0.2126 * r + 0.7152 * g + 0.0722 * b\n }\n\n const [x, y] = [luminance(a), luminance(b)]\n return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05)\n}\n\n/**\n * What `tokens.css` derives for an `on-` role the app leaves alone.\n *\n * The same arithmetic as the `oklch(from … clamp(0, (0.6 - l) * 1000, 1) 0 0)`\n * net there: black or white, whichever is further from the fill's lightness.\n * Repeated rather than read out of the stylesheet, because a check that\n * evaluates a `calc()` is a CSS engine; this is one threshold, and\n * `check.spec.ts` holds the two to the same answer.\n */\nfunction derivedInk(fill: string): string {\n const [r, g, b] = channels(fill)\n const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)\n const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)\n const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)\n const lightness = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s\n\n return lightness < 0.6 ? '#ffffff' : '#000000'\n}\n\n/**\n * What is missing from an app's stylesheet, as a list to assert is empty.\n *\n * ```ts\n * import { checkStyling } from 'rei-kit/check'\n *\n * it('is wired to the kit', () => {\n * expect(\n * checkStyling({\n * css: readFileSync('src/assets/main.css', 'utf8'),\n * tokens: readFileSync('node_modules/rei-kit/dist/tokens.css', 'utf8'),\n * }),\n * ).toEqual([])\n * })\n * ```\n */\nexport function checkStyling({ css, tokens }: StylingInput): StylingProblem[] {\n const problems: StylingProblem[] = []\n\n /* The preset is tokens, shell, materials, palettes, the compiled component\n styles and the `@source` — so an app that imports one has answered every\n question below at once, and asking them separately would report faults it\n does not have. */\n const preset = imports(css, 'mobile.css') || imports(css, 'web.css')\n\n if (preset && imports(css, 'mobile.css') && imports(css, 'web.css')) {\n problems.push({\n message:\n 'Both presets are imported. They are two answers to the same question, and the second one wins wherever they disagree.',\n fix: \"keep one of @import 'rei-kit/mobile.css' or @import 'rei-kit/web.css'\",\n })\n }\n\n if (!preset) {\n if (!imports(css, 'styles.css')) {\n problems.push({\n message:\n 'The compiled component styles are not imported, so every component keeps its markup and loses its layout. Nothing else reports this: the build is green and the page renders.',\n fix: \"@import 'rei-kit/mobile.css'; /* or web.css — the preset carries the rest too */\",\n })\n }\n\n /* Only asked of an app that installed the package. A workspace reaching\n the kit by path points Tailwind at wherever it keeps the source, and\n guessing that path is not this check's business. */\n if (byPackage(css) && !/@source\\s+['\"][^'\"]*node_modules\\/rei-kit[^'\"]*['\"]/.test(css)) {\n problems.push({\n message:\n 'Tailwind is not told to scan the kit, so it emits no utility the components ask for. It does not walk node_modules unless pointed at it.',\n fix: \"@source '<relative path>/node_modules/rei-kit/dist';\",\n })\n }\n }\n\n /* Checked last and only when the roles cannot be arriving from the kit\n itself, so the list reports what the app has to define rather than\n repeating the import it is already being told about. */\n const declared = colourRoles(css)\n if (!preset && !imports(css, 'tokens.css')) {\n const missing = [...colourRoles(tokens)].filter((role) => !declared.has(role))\n\n if (missing.length) {\n problems.push({\n message: `These colour roles are never defined, so every utility built on them emits nothing: ${missing.join(', ')}.`,\n fix: \"@import 'rei-kit/tokens.css';\",\n })\n }\n }\n\n /* Measured, because the instruction had been written down and never\n enforced: with the material imported first, `--surface-opacity` computes\n to 100% under `data-material=\"glass\"` instead of 56% — the material is\n simply inert. Both files set the same custom properties, `:root` and\n `[data-material]` carry the same specificity, and equal specificity is\n settled by source order. The build is green, the attribute is on the\n element, and nothing happens. */\n if (!preset) {\n const at = (file: string) => {\n const found = new RegExp(`@import\\\\s+['\"][^'\"]*${file.replace('.', '\\\\.')}['\"]`).exec(css)\n return found ? found.index : -1\n }\n\n const tokensAt = at('tokens.css')\n /* Materials and palettes only. Both reset properties `tokens.css` also\n sets — 17 of them for the palettes — under a selector of the same\n specificity, so order settles it. `motion.css` shares no property with\n `tokens.css` at all, so its position cannot matter, and 4.1.0 asked\n about it anyway: a rule that reports a setup nothing is wrong with is\n the kind that gets switched off. */\n const late = ['materials.css', 'palettes.css']\n .map((file) => ({ file, at: at(file) }))\n .filter((entry) => entry.at !== -1 && tokensAt !== -1 && entry.at < tokensAt)\n\n if (late.length) {\n problems.push({\n message: `${late.map((entry) => entry.file).join(' and ')} must be imported after tokens.css, or the values they set are overwritten and the attribute they answer to does nothing.`,\n fix: \"@import 'rei-kit/tokens.css'; then the rest\",\n })\n }\n }\n\n /* The trap an app found before this check existed, and the sharpest one\n here: `@theme` compiles to `:root`, which Tailwind emits near the top of\n the stylesheet, while the kit's `.dark` block arrives after it. A role\n the app restates in `@theme` but not under `.dark` therefore keeps the\n kit's value at night — so the app comes up in somebody else's colours\n after dark, with a green build and every other check passing. */\n const kitDark = darkRoles(tokens)\n const appDark = darkRoles(css)\n const redefined = [...declared].filter((role) => kitDark.has(role) && !appDark.has(role))\n\n if (redefined.length) {\n problems.push({\n message: `These roles are redefined for the day and left at the kit's own values at night, because \\`@theme\\` lands in \\`:root\\` and the kit's \\`.dark\\` comes after it: ${redefined.join(', ')}.`,\n fix: '.dark { --color-primary: …; } — restate each one under `.dark` as well',\n })\n }\n\n /* The one fault in here that is about a value rather than a line, and the\n reason it belongs with the others: it is just as quiet. A rebranded role\n is a colour chosen by eye, the text on it comes from a token or from the\n net in `tokens.css`, and the pair is never seen together until it is on\n somebody's screen. Two apps in this family declared the right\n on-colours and then wrote `text-white` anyway; a third left the roles to\n the net, which is documented as a safety net rather than a guarantee,\n and its green landed at 3.96:1.\n\n Only roles the app itself redefines are measured — the kit's own are\n measured by the kit — and a value that is not a hex is skipped rather\n than guessed at. */\n const theme = colourValues(bodies(css, /@theme[^{]*\\{/g))\n const night = new Map([...theme, ...colourValues(bodies(css, /(?<![\\w):])\\.dark\\s*\\{/g))])\n\n const unreadable = FILLED_ROLES.flatMap((role) =>\n (\n [\n ['day', theme],\n ['night', night],\n ] as const\n ).flatMap(([mode, values]) => {\n const fill = hex(values, role)\n if (!fill) return []\n\n const ink = hex(values, `on-${role}`) ?? derivedInk(fill)\n const ratio = contrast(fill, ink)\n\n return ratio >= 4.5\n ? []\n : [`${role} by ${mode} (${fill} under ${ink}, ${ratio.toFixed(2)}:1)`]\n }),\n )\n\n if (unreadable.length) {\n problems.push({\n message: `Text on these filled roles is below WCAG AA, so a label on one of them is unreadable at normal size: ${unreadable.join('; ')}.`,\n fix: 'move the role, or declare its `--color-on-<role>` as a colour measured against it',\n })\n }\n\n return problems\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** One answer in a group of checkboxes. */
|
|
2
|
+
export interface CheckboxOption<V extends string = string> {
|
|
3
|
+
value: V;
|
|
4
|
+
label: string;
|
|
5
|
+
/** A reason this one cannot be chosen, shown under its label. */
|
|
6
|
+
hint?: string | undefined;
|
|
7
|
+
disabled?: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
declare const __VLS_export: <V extends string = string>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
10
|
+
props: import('vue').PublicProps & __VLS_PrettifyLocal<({
|
|
11
|
+
options: readonly CheckboxOption<V>[];
|
|
12
|
+
legend: string;
|
|
13
|
+
/** Shown under the group, and it marks every box invalid. */
|
|
14
|
+
error?: string | undefined;
|
|
15
|
+
hint?: string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Hides the question but keeps it as the group's accessible name — for a
|
|
18
|
+
* group whose surroundings already ask it. Dropping the legend instead
|
|
19
|
+
* leaves the set named nothing at all.
|
|
20
|
+
*/
|
|
21
|
+
legendHidden?: boolean | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Adds a box above the others that chooses or clears all of them, named by
|
|
24
|
+
* this string. Empty means no such box: it earns its place on a list long
|
|
25
|
+
* enough that ticking each one is work, and gets in the way on three.
|
|
26
|
+
*
|
|
27
|
+
* It never chooses a disabled option, so pressing it twice is not a way to
|
|
28
|
+
* select something the form has ruled out.
|
|
29
|
+
*/
|
|
30
|
+
selectAllLabel?: string | undefined;
|
|
31
|
+
/** Disables every box, including the select-all. */
|
|
32
|
+
disabled?: boolean | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Lay the boxes out in two columns from the `sm` breakpoint. One column on
|
|
35
|
+
* a phone either way: a two-column list of checkboxes on a 390px screen is
|
|
36
|
+
* two columns of truncated labels.
|
|
37
|
+
*/
|
|
38
|
+
columns?: 1 | 2 | undefined;
|
|
39
|
+
} & {
|
|
40
|
+
modelValue?: V[];
|
|
41
|
+
}) & {
|
|
42
|
+
"onUpdate:modelValue"?: (value: V[]) => any;
|
|
43
|
+
}> & (typeof globalThis extends {
|
|
44
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
45
|
+
} ? P : {});
|
|
46
|
+
expose: (exposed: {}) => void;
|
|
47
|
+
attrs: any;
|
|
48
|
+
slots: {};
|
|
49
|
+
emit: (event: "update:modelValue", value: V[]) => void;
|
|
50
|
+
}>) => import('vue').VNode & {
|
|
51
|
+
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
52
|
+
};
|
|
53
|
+
declare const _default: typeof __VLS_export;
|
|
54
|
+
export default _default;
|
|
55
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? {
|
|
56
|
+
[K in keyof T]: T[K];
|
|
57
|
+
} : {
|
|
58
|
+
[K in keyof T as K]: T[K];
|
|
59
|
+
}) & {};
|
|
@@ -4,7 +4,16 @@ export interface Tone {
|
|
|
4
4
|
fill: string;
|
|
5
5
|
/** Tinted surface for the pill, e.g. `bg-positive/5 border-positive/25`. */
|
|
6
6
|
card: string;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Foreground for the label. `text-ink`, in almost every case.
|
|
9
|
+
*
|
|
10
|
+
* Not the role: `text-positive` on a `bg-positive/5` pill is a colour on a
|
|
11
|
+
* faint wash of itself, and it measures about 2:1 painted — the fault
|
|
12
|
+
* `BaseBadge`, `BaseChip`, `BaseAlert`, `BaseListbox` and `PriceCard` were
|
|
13
|
+
* all fixed for in 2.25.0. This one takes its classes from the app, so no
|
|
14
|
+
* source-reading test could see it; the contrast scan on the showcase
|
|
15
|
+
* caught it once the playground started rendering it.
|
|
16
|
+
*/
|
|
8
17
|
text: string;
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
@@ -17,10 +26,11 @@ export interface Tone {
|
|
|
17
26
|
*/
|
|
18
27
|
type __VLS_Props = {
|
|
19
28
|
/**
|
|
20
|
-
* The colour classes for the dot and the label, as written-out
|
|
21
|
-
* `Tone` is a
|
|
22
|
-
* the app's own, and Tailwind reads source as plain
|
|
23
|
-
* at runtime never reaches the stylesheet.
|
|
29
|
+
* The colour classes for the dot, the pill and the label, as written-out
|
|
30
|
+
* class names. `Tone` is a set of classes rather than a role token because
|
|
31
|
+
* the categories here are the app's own, and Tailwind reads source as plain
|
|
32
|
+
* text: a class assembled at runtime never reaches the stylesheet. Put the
|
|
33
|
+
* role on `fill` and `card` and leave `text` as `text-ink`.
|
|
24
34
|
*/
|
|
25
35
|
tone: Tone;
|
|
26
36
|
label: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -208,6 +208,24 @@ export type { ComboboxOption } from './components/BaseCombobox.vue';
|
|
|
208
208
|
* @see {@link BaseSwitch} — the near-neighbour this is mistaken for
|
|
209
209
|
*/
|
|
210
210
|
export { default as BaseCheckbox } from './components/BaseCheckbox.vue';
|
|
211
|
+
/**
|
|
212
|
+
* Several answers from a set that is all on screen.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```vue
|
|
216
|
+
* <BaseCheckboxGroup
|
|
217
|
+
* v-model="days"
|
|
218
|
+
* legend="Remind me on"
|
|
219
|
+
* :options="weekdays"
|
|
220
|
+
* select-all-label="Every weekday"
|
|
221
|
+
* :columns="2"
|
|
222
|
+
* />
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @see {@link BaseListbox}, {@link BaseRadioGroup} — the near-neighbours this is mistaken for
|
|
226
|
+
*/
|
|
227
|
+
export { default as BaseCheckboxGroup } from './components/BaseCheckboxGroup.vue';
|
|
228
|
+
export type { CheckboxOption } from './components/BaseCheckboxGroup.vue';
|
|
211
229
|
/**
|
|
212
230
|
* A set of radios, and the reason there is no `BaseRadio`.
|
|
213
231
|
*
|
|
@@ -216,7 +234,7 @@ export { default as BaseCheckbox } from './components/BaseCheckbox.vue';
|
|
|
216
234
|
* <BaseRadioGroup v-model="plan" legend="Plan" :options="plans" />
|
|
217
235
|
* ```
|
|
218
236
|
*
|
|
219
|
-
* @see {@link BaseListbox}, {@link BaseSelect} — the near-neighbours this is mistaken for
|
|
237
|
+
* @see {@link BaseCheckboxGroup}, {@link BaseListbox}, {@link BaseSelect} — the near-neighbours this is mistaken for
|
|
220
238
|
*/
|
|
221
239
|
export { default as BaseRadioGroup } from './components/BaseRadioGroup.vue';
|
|
222
240
|
/**
|
|
@@ -764,7 +782,7 @@ export { default as BaseKbd } from './components/BaseKbd.vue';
|
|
|
764
782
|
* <BaseListbox v-model="chosen" mode="multiple" :options="people" label="People with access" />
|
|
765
783
|
* ```
|
|
766
784
|
*
|
|
767
|
-
* @see {@link BaseRadioGroup}, {@link BaseSelect}, {@link TransferList} — the near-neighbours this is mistaken for
|
|
785
|
+
* @see {@link BaseCheckboxGroup}, {@link BaseRadioGroup}, {@link BaseSelect}, {@link TransferList} — the near-neighbours this is mistaken for
|
|
768
786
|
*/
|
|
769
787
|
export { default as BaseListbox } from './components/BaseListbox.vue';
|
|
770
788
|
export type { ListboxOption } from './components/BaseListbox.vue';
|
package/dist/index.js
CHANGED
|
@@ -272,7 +272,7 @@ function useDebouncedCallback(callback, delay = 800) {
|
|
|
272
272
|
}
|
|
273
273
|
//#endregion
|
|
274
274
|
//#region src/components/BaseAvatar.vue?vue&type=script&setup=true&lang.ts
|
|
275
|
-
var _hoisted_1$
|
|
275
|
+
var _hoisted_1$52 = [
|
|
276
276
|
"role",
|
|
277
277
|
"aria-label",
|
|
278
278
|
"aria-hidden"
|
|
@@ -337,7 +337,7 @@ var BaseAvatar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__P
|
|
|
337
337
|
d: "M3.75 17c0-3.05 2.8-5.25 6.25-5.25S16.25 13.95 16.25 17",
|
|
338
338
|
"stroke-width": "1.5",
|
|
339
339
|
"stroke-linecap": "round"
|
|
340
|
-
}, null, -1)])]))], 10, _hoisted_1$
|
|
340
|
+
}, null, -1)])]))], 10, _hoisted_1$52);
|
|
341
341
|
};
|
|
342
342
|
}
|
|
343
343
|
}), [["__scopeId", "data-v-47586bec"]]);
|
|
@@ -362,7 +362,7 @@ var BaseBadge_default = /* @__PURE__ */ defineComponent({
|
|
|
362
362
|
});
|
|
363
363
|
//#endregion
|
|
364
364
|
//#region src/components/BaseMenu.vue?vue&type=script&setup=true&lang.ts
|
|
365
|
-
var _hoisted_1$
|
|
365
|
+
var _hoisted_1$51 = [
|
|
366
366
|
"aria-label",
|
|
367
367
|
"aria-expanded",
|
|
368
368
|
"aria-controls"
|
|
@@ -441,7 +441,7 @@ var BaseMenu_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
|
|
|
441
441
|
"aria-haspopup": true,
|
|
442
442
|
"aria-controls": open.value ? unref(id) : void 0,
|
|
443
443
|
onClick: _cache[0] || (_cache[0] = ($event) => open.value = !open.value)
|
|
444
|
-
}, [renderSlot(_ctx.$slots, "trigger", { open: open.value }, void 0, true)], 8, _hoisted_1$
|
|
444
|
+
}, [renderSlot(_ctx.$slots, "trigger", { open: open.value }, void 0, true)], 8, _hoisted_1$51), open.value ? (openBlock(), createElementBlock("div", {
|
|
445
445
|
key: 0,
|
|
446
446
|
id: unref(id),
|
|
447
447
|
ref_key: "panel",
|
|
@@ -574,7 +574,7 @@ function useVirtualWindow({ count, rowHeight, threshold }) {
|
|
|
574
574
|
}
|
|
575
575
|
//#endregion
|
|
576
576
|
//#region src/components/BaseCombobox.vue?vue&type=script&setup=true&lang.ts
|
|
577
|
-
var _hoisted_1$
|
|
577
|
+
var _hoisted_1$50 = ["for"];
|
|
578
578
|
var _hoisted_2$43 = [
|
|
579
579
|
"aria-label",
|
|
580
580
|
"disabled",
|
|
@@ -816,7 +816,7 @@ var BaseCombobox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
|
|
|
816
816
|
createElementVNode("label", {
|
|
817
817
|
for: unref(id),
|
|
818
818
|
class: "rk-combo-label"
|
|
819
|
-
}, toDisplayString(__props.label), 9, _hoisted_1$
|
|
819
|
+
}, toDisplayString(__props.label), 9, _hoisted_1$50),
|
|
820
820
|
createElementVNode("div", { class: normalizeClass(["rk-combo-field", multiple.value ? "is-multiple control" : ""]) }, [
|
|
821
821
|
(openBlock(true), createElementBlock(Fragment, null, renderList(multiple.value ? chosenOptions.value : [], (option) => {
|
|
822
822
|
return openBlock(), createElementBlock("span", {
|
|
@@ -919,6 +919,102 @@ var BaseCombobox_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @_
|
|
|
919
919
|
}
|
|
920
920
|
}), [["__scopeId", "data-v-f02a0924"]]);
|
|
921
921
|
//#endregion
|
|
922
|
+
//#region src/components/BaseCheckboxGroup.vue?vue&type=script&setup=true&lang.ts
|
|
923
|
+
var _hoisted_1$49 = ["aria-describedby", "aria-invalid"];
|
|
924
|
+
//#endregion
|
|
925
|
+
//#region src/components/BaseCheckboxGroup.vue
|
|
926
|
+
var BaseCheckboxGroup_default = /* @__PURE__ */ defineComponent({
|
|
927
|
+
__name: "BaseCheckboxGroup",
|
|
928
|
+
props: /*@__PURE__*/ mergeModels({
|
|
929
|
+
options: {},
|
|
930
|
+
legend: {},
|
|
931
|
+
error: { default: "" },
|
|
932
|
+
hint: { default: "" },
|
|
933
|
+
legendHidden: {
|
|
934
|
+
type: Boolean,
|
|
935
|
+
default: false
|
|
936
|
+
},
|
|
937
|
+
selectAllLabel: { default: "" },
|
|
938
|
+
disabled: {
|
|
939
|
+
type: Boolean,
|
|
940
|
+
default: false
|
|
941
|
+
},
|
|
942
|
+
columns: { default: 1 }
|
|
943
|
+
}, {
|
|
944
|
+
"modelValue": { default: () => [] },
|
|
945
|
+
"modelModifiers": {}
|
|
946
|
+
}),
|
|
947
|
+
emits: ["update:modelValue"],
|
|
948
|
+
setup(__props) {
|
|
949
|
+
const model = useModel(__props, "modelValue");
|
|
950
|
+
const id = useId();
|
|
951
|
+
const errorId = `${id}-error`;
|
|
952
|
+
const hintId = `${id}-hint`;
|
|
953
|
+
const describedBy = computed(() => {
|
|
954
|
+
if (__props.error) return errorId;
|
|
955
|
+
if (__props.hint) return hintId;
|
|
956
|
+
});
|
|
957
|
+
const choosable = computed(() => __props.options.filter((option) => !option.disabled));
|
|
958
|
+
const allChosen = computed(() => choosable.value.length > 0 && choosable.value.every((option) => model.value.includes(option.value)));
|
|
959
|
+
const someChosen = computed(() => !allChosen.value && choosable.value.some((option) => model.value.includes(option.value)));
|
|
960
|
+
function toggle(value, chosen) {
|
|
961
|
+
model.value = chosen ? [...model.value, value] : model.value.filter((chosenValue) => chosenValue !== value);
|
|
962
|
+
}
|
|
963
|
+
function toggleAll(chosen) {
|
|
964
|
+
const locked = model.value.filter((value) => __props.options.some((option) => option.value === value && option.disabled));
|
|
965
|
+
model.value = chosen ? [...locked, ...choosable.value.map((option) => option.value)] : locked;
|
|
966
|
+
}
|
|
967
|
+
return (_ctx, _cache) => {
|
|
968
|
+
return openBlock(), createElementBlock("fieldset", {
|
|
969
|
+
class: "flex flex-col gap-2",
|
|
970
|
+
"aria-describedby": describedBy.value,
|
|
971
|
+
"aria-invalid": Boolean(__props.error)
|
|
972
|
+
}, [
|
|
973
|
+
createElementVNode("legend", { class: normalizeClass(["text-ink mb-1 text-sm font-medium", __props.legendHidden ? "sr-only" : ""]) }, toDisplayString(__props.legend), 3),
|
|
974
|
+
__props.selectAllLabel ? (openBlock(), createBlock(BaseCheckbox_default, {
|
|
975
|
+
key: 0,
|
|
976
|
+
"model-value": allChosen.value,
|
|
977
|
+
label: __props.selectAllLabel,
|
|
978
|
+
indeterminate: someChosen.value,
|
|
979
|
+
disabled: __props.disabled || choosable.value.length === 0,
|
|
980
|
+
class: "border-hair/70 border-b pb-2",
|
|
981
|
+
"onUpdate:modelValue": toggleAll
|
|
982
|
+
}, null, 8, [
|
|
983
|
+
"model-value",
|
|
984
|
+
"label",
|
|
985
|
+
"indeterminate",
|
|
986
|
+
"disabled"
|
|
987
|
+
])) : createCommentVNode("", true),
|
|
988
|
+
createElementVNode("div", { class: normalizeClass(["grid gap-2", __props.columns === 2 ? "sm:grid-cols-2" : ""]) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.options, (option) => {
|
|
989
|
+
return openBlock(), createBlock(BaseCheckbox_default, {
|
|
990
|
+
key: option.value,
|
|
991
|
+
"model-value": model.value.includes(option.value),
|
|
992
|
+
label: option.label,
|
|
993
|
+
hint: option.hint ?? "",
|
|
994
|
+
disabled: __props.disabled || option.disabled,
|
|
995
|
+
"onUpdate:modelValue": (chosen) => toggle(option.value, chosen)
|
|
996
|
+
}, null, 8, [
|
|
997
|
+
"model-value",
|
|
998
|
+
"label",
|
|
999
|
+
"hint",
|
|
1000
|
+
"disabled",
|
|
1001
|
+
"onUpdate:modelValue"
|
|
1002
|
+
]);
|
|
1003
|
+
}), 128))], 2),
|
|
1004
|
+
__props.error ? (openBlock(), createElementBlock("p", {
|
|
1005
|
+
key: 1,
|
|
1006
|
+
id: errorId,
|
|
1007
|
+
class: "text-negative text-xs"
|
|
1008
|
+
}, toDisplayString(__props.error), 1)) : __props.hint ? (openBlock(), createElementBlock("p", {
|
|
1009
|
+
key: 2,
|
|
1010
|
+
id: hintId,
|
|
1011
|
+
class: "text-ink-soft text-xs"
|
|
1012
|
+
}, toDisplayString(__props.hint), 1)) : createCommentVNode("", true)
|
|
1013
|
+
], 8, _hoisted_1$49);
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
//#endregion
|
|
922
1018
|
//#region src/components/BaseRadioGroup.vue?vue&type=script&setup=true&lang.ts
|
|
923
1019
|
var _hoisted_1$48 = ["aria-describedby"];
|
|
924
1020
|
var _hoisted_2$42 = [
|
|
@@ -5198,8 +5294,8 @@ function createI18nRuntime(options) {
|
|
|
5198
5294
|
*
|
|
5199
5295
|
* The fallback keeps `vitest` and `vite dev` honest, where no define runs.
|
|
5200
5296
|
*/
|
|
5201
|
-
var VERSION = "4.
|
|
5297
|
+
var VERSION = "4.3.0";
|
|
5202
5298
|
//#endregion
|
|
5203
|
-
export { ActivityGrid_default as ActivityGrid, AnnounceHost_default as AnnounceHost, AppError, AvatarStack_default as AvatarStack, BarChart_default as BarChart, BaseAlert_default as BaseAlert, BaseAvatar_default as BaseAvatar, BaseBadge_default as BaseBadge, BaseButton_default as BaseButton, BaseCalendar_default as BaseCalendar, BaseCard_default as BaseCard, BaseCheckbox_default as BaseCheckbox, BaseChip_default as BaseChip, BaseCombobox_default as BaseCombobox, BaseDatePicker_default as BaseDatePicker, BaseInput_default as BaseInput, BaseKbd_default as BaseKbd, BaseLink_default as BaseLink, BaseListbox_default as BaseListbox, BaseMenu_default as BaseMenu, BasePopconfirm_default as BasePopconfirm, BasePopover_default as BasePopover, BaseRadioGroup_default as BaseRadioGroup, BaseRating_default as BaseRating, BaseSelect_default as BaseSelect, BaseSeparator_default as BaseSeparator, BaseSheet_default as BaseSheet, BaseSkeleton_default as BaseSkeleton, BaseSlider_default as BaseSlider, BaseSpinner_default as BaseSpinner, BaseStepper_default as BaseStepper, BaseSwitch_default as BaseSwitch, BaseTable_default as BaseTable, BaseTextarea_default as BaseTextarea, BaseTimeline_default as BaseTimeline, CircularProgress_default as CircularProgress, CodeBlock_default as CodeBlock, ColorPicker_default as ColorPicker, CopyButton_default as CopyButton, DescriptionList_default as DescriptionList, DonutChart_default as DonutChart, EmptyState_default as EmptyState, ErrorBoundary_default as ErrorBoundary, ErrorSummary_default as ErrorSummary, FileDrop_default as FileDrop, FormField_default as FormField, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, MATERIALS, NumberInput_default as NumberInput, PALETTES, PageContainer_default as PageContainer, PageHeader_default as PageHeader, PasswordInput_default as PasswordInput, PinInput_default as PinInput, PriceCard_default as PriceCard, ProgressBar_default as ProgressBar, SHEET_ROOT_ID, ScrollArea_default as ScrollArea, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, SliderField_default as SliderField, StatCard_default as StatCard, TabBar_default as TabBar, TagsInput_default as TagsInput, TimePicker_default as TimePicker, ToastHost_default as ToastHost, ToggleGroup_default as ToggleGroup, ToneDot_default as ToneDot, VERSION, VirtualList_default as VirtualList, addDays, announce, applyMaterial, applyPalette, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, elementDirection, ensureSheetRoot, formatDate, formatNumber, fromDateKey, horizontalStep, isApplePortable, isInstalled, isMaterial, isPaletteName, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setMaterialStorageKey, setPaletteStorageKey, setThemeStorageKey, startOfWeek, tapFeedback, textDirection, toAppError, toDateKey, toRedirectPath, todayKey, useAnnounce, useDebouncedCallback, useDragScroll, useMaterial, useMediaQuery, useOnline, usePalette, useTheme, useToast, useToday, useVisualViewport };
|
|
5299
|
+
export { ActivityGrid_default as ActivityGrid, AnnounceHost_default as AnnounceHost, AppError, AvatarStack_default as AvatarStack, BarChart_default as BarChart, BaseAlert_default as BaseAlert, BaseAvatar_default as BaseAvatar, BaseBadge_default as BaseBadge, BaseButton_default as BaseButton, BaseCalendar_default as BaseCalendar, BaseCard_default as BaseCard, BaseCheckbox_default as BaseCheckbox, BaseCheckboxGroup_default as BaseCheckboxGroup, BaseChip_default as BaseChip, BaseCombobox_default as BaseCombobox, BaseDatePicker_default as BaseDatePicker, BaseInput_default as BaseInput, BaseKbd_default as BaseKbd, BaseLink_default as BaseLink, BaseListbox_default as BaseListbox, BaseMenu_default as BaseMenu, BasePopconfirm_default as BasePopconfirm, BasePopover_default as BasePopover, BaseRadioGroup_default as BaseRadioGroup, BaseRating_default as BaseRating, BaseSelect_default as BaseSelect, BaseSeparator_default as BaseSeparator, BaseSheet_default as BaseSheet, BaseSkeleton_default as BaseSkeleton, BaseSlider_default as BaseSlider, BaseSpinner_default as BaseSpinner, BaseStepper_default as BaseStepper, BaseSwitch_default as BaseSwitch, BaseTable_default as BaseTable, BaseTextarea_default as BaseTextarea, BaseTimeline_default as BaseTimeline, CircularProgress_default as CircularProgress, CodeBlock_default as CodeBlock, ColorPicker_default as ColorPicker, CopyButton_default as CopyButton, DescriptionList_default as DescriptionList, DonutChart_default as DonutChart, EmptyState_default as EmptyState, ErrorBoundary_default as ErrorBoundary, ErrorSummary_default as ErrorSummary, FileDrop_default as FileDrop, FormField_default as FormField, GoogleButton_default as GoogleButton, LocaleLinks_default as LocaleLinks, MATERIALS, NumberInput_default as NumberInput, PALETTES, PageContainer_default as PageContainer, PageHeader_default as PageHeader, PasswordInput_default as PasswordInput, PinInput_default as PinInput, PriceCard_default as PriceCard, ProgressBar_default as ProgressBar, SHEET_ROOT_ID, ScrollArea_default as ScrollArea, SectionHeading_default as SectionHeading, SegmentedControl_default as SegmentedControl, SettingsGroup_default as SettingsGroup, SettingsRow_default as SettingsRow, SkeletonList_default as SkeletonList, SliderField_default as SliderField, StatCard_default as StatCard, TabBar_default as TabBar, TagsInput_default as TagsInput, TimePicker_default as TimePicker, ToastHost_default as ToastHost, ToggleGroup_default as ToggleGroup, ToneDot_default as ToneDot, VERSION, VirtualList_default as VirtualList, addDays, announce, applyMaterial, applyPalette, applyTheme, createI18nRuntime, downloadJson, eachDayOfYear, elementDirection, ensureSheetRoot, formatDate, formatNumber, fromDateKey, horizontalStep, isApplePortable, isInstalled, isMaterial, isPaletteName, isThemePreference, lastNDays, leadingBlanks, needsIosInstall, readStoredTheme, registerErrorMapper, relativeDayLabel, safeRedirect, setFormatLocale, setMaterialStorageKey, setPaletteStorageKey, setThemeStorageKey, startOfWeek, tapFeedback, textDirection, toAppError, toDateKey, toRedirectPath, todayKey, useAnnounce, useDebouncedCallback, useDragScroll, useMaterial, useMediaQuery, useOnline, usePalette, useTheme, useToast, useToday, useVisualViewport };
|
|
5204
5300
|
|
|
5205
5301
|
//# sourceMappingURL=index.js.map
|