@ultimat3/ui 22.15.0 → 24.0.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/CATALOG.md +45 -14
- package/CLAUDE.md +16 -52
- package/README.md +69 -13
- package/package.json +6 -5
- package/src/a11y.ts +24 -10
- package/src/catalog/parse-component.ts +85 -12
- package/src/catalog/render-catalog.ts +21 -1
- package/src/components/Avatar.module.scss +1 -1
- package/src/components/BarChart.module.scss +12 -1
- package/src/components/BarChart.tsx +41 -15
- package/src/components/Button.tsx +3 -8
- package/src/components/Card.module.scss +1 -1
- package/src/components/Checkbox.module.scss +4 -4
- package/src/components/Checkbox.tsx +6 -0
- package/src/components/CommandPalette.module.scss +8 -5
- package/src/components/DataTable.module.scss +2 -0
- package/src/components/DataTable.tsx +135 -71
- package/src/components/Dialog.module.scss +2 -2
- package/src/components/Dropzone.module.scss +6 -4
- package/src/components/Dropzone.tsx +9 -8
- package/src/components/FileInput.module.scss +4 -2
- package/src/components/FileInput.tsx +6 -2
- package/src/components/Form.module.scss +2 -2
- package/src/components/Input.module.scss +1 -1
- package/src/components/Link.tsx +64 -12
- package/src/components/Meter.tsx +3 -3
- package/src/components/Pagination.tsx +89 -37
- package/src/components/Popover.module.scss +17 -20
- package/src/components/Popover.tsx +12 -5
- package/src/components/Radio.module.scss +2 -2
- package/src/components/Select.module.scss +3 -3
- package/src/components/Skeleton.module.scss +1 -1
- package/src/components/Spinner.module.scss +3 -3
- package/src/components/Switch.module.scss +2 -2
- package/src/components/Table.module.scss +2 -2
- package/src/components/Tabs.module.scss +11 -5
- package/src/components/Textarea.tsx +7 -4
- package/src/components/ThemeToggle.module.scss +1 -1
- package/src/components/Toast.module.scss +2 -2
- package/src/components/Tooltip.module.scss +8 -3
- package/src/components/bar-chart-view.ts +35 -5
- package/src/components/button-classes.ts +25 -0
- package/src/components/file-input-view.ts +31 -12
- package/src/components/link-classes.ts +38 -0
- package/src/components/link-target.ts +34 -1
- package/src/components/meter-view.ts +13 -0
- package/src/components/qr-matrix.ts +16 -23
- package/src/components/qr-reader.ts +187 -0
- package/src/components/relative-time-view.ts +11 -4
- package/src/form/form-binding.ts +9 -8
- package/src/icons/build-icons.ts +96 -20
- package/src/index.ts +15 -9
- package/src/sass-probe.ts +55 -0
- package/src/theme/brand.ts +22 -3
- package/src/theme/theme.ts +35 -10
- package/src/toast/toast-state.ts +0 -0
- package/src/tokens/_index.scss +3 -1
- package/src/tokens/_mixins.scss +56 -8
- package/src/tokens/_motion.scss +4 -0
- package/src/tokens/_stroke.scss +20 -0
- package/src/tokens/_units.scss +47 -0
- package/src/tokens/reset.scss +4 -4
- package/src/tokens/theme.scss +2 -0
- package/src/tokens/tokens.ts +6 -0
- package/src/theme/inline-script.ts +0 -49
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Computed lengths: px → rem, and one fluid `clamp()` between two viewport widths. Functions only —
|
|
2
|
+
// nothing here emits CSS. The scale maps restate a constant; these compute a value from one.
|
|
3
|
+
|
|
4
|
+
@use 'sass:math';
|
|
5
|
+
@use 'sass:meta';
|
|
6
|
+
@use 'sass:string';
|
|
7
|
+
|
|
8
|
+
/// The one conversion both functions read, so `fluid(16px, 32px)` and `rem(16px)` cannot disagree
|
|
9
|
+
/// about what a px is — and so a refusal names the function the author actually called.
|
|
10
|
+
@function -to-rem($value, $root, $caller, $example) {
|
|
11
|
+
@if meta.type-of($value) != 'number' or not (math.is-unitless($value) or math.unit($value) == 'px' or math.unit($value) == 'rem') {
|
|
12
|
+
@error string.unquote('X_UI_INVALID_VALUE: #{$caller}() expected px, rem or a unitless px count — got `#{meta.inspect($value)}`. fix: pass a px length, e.g. #{$example}');
|
|
13
|
+
}
|
|
14
|
+
@if math.is-unitless($value) {
|
|
15
|
+
@return math.div($value * 1px, $root) * 1rem;
|
|
16
|
+
}
|
|
17
|
+
@if math.unit($value) == 'rem' {
|
|
18
|
+
@return $value;
|
|
19
|
+
}
|
|
20
|
+
@return math.div($value, $root) * 1rem;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// px → rem against the root size. Lengths are rem so a layout holds at 200% text zoom; a unit
|
|
24
|
+
/// that cannot be converted is a build error rather than a size that is subtly off.
|
|
25
|
+
@function rem($value, $root: 16px) {
|
|
26
|
+
@return -to-rem($value, $root, 'rem', 'rem(24px)');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// One `clamp()` with a real slope and intercept: `$min` at a viewport `$from` wide, `$max` at
|
|
30
|
+
/// `$to`, linear between and held outside. Type and space then scale with the viewport instead of
|
|
31
|
+
/// stepping at a breakpoint. `$min` above `$max` is a size that shrinks, and is legal.
|
|
32
|
+
@function fluid($min, $max, $from: 20rem, $to: 80rem) {
|
|
33
|
+
$min: -to-rem($min, 16px, 'fluid', 'fluid(16px, 32px)');
|
|
34
|
+
$max: -to-rem($max, 16px, 'fluid', 'fluid(16px, 32px)');
|
|
35
|
+
$from: -to-rem($from, 16px, 'fluid', 'fluid(16px, 32px)');
|
|
36
|
+
$to: -to-rem($to, 16px, 'fluid', 'fluid(16px, 32px)');
|
|
37
|
+
|
|
38
|
+
@if $from >= $to {
|
|
39
|
+
@error string.unquote('X_UI_INVALID_VALUE: fluid() needs $from below $to — got `#{$from}` and `#{$to}`. fix: pass the narrower viewport first, e.g. fluid(1rem, 2rem, 20rem, 80rem)');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
$slope: math.div($max - $min, $to - $from);
|
|
43
|
+
$intercept: $min - $slope * $from;
|
|
44
|
+
// `clamp(MIN, VAL, MAX)` with MIN above MAX answers MIN at every width, so the bounds are
|
|
45
|
+
// ordered here rather than trusted to be.
|
|
46
|
+
@return clamp(math.min($min, $max), $intercept + $slope * 100vw, math.max($min, $max));
|
|
47
|
+
}
|
package/src/tokens/reset.scss
CHANGED
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
|
|
116
116
|
// Themed keyboard focus for anything the component layer forgot.
|
|
117
117
|
:where(:focus-visible) {
|
|
118
|
-
outline:
|
|
119
|
-
outline-offset:
|
|
118
|
+
outline: t.stroke(thick) solid t.role('accent');
|
|
119
|
+
outline-offset: t.stroke(thick);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
:where(:focus:not(:focus-visible)) {
|
|
@@ -153,9 +153,9 @@ html:has(dialog:modal) {
|
|
|
153
153
|
*,
|
|
154
154
|
*::before,
|
|
155
155
|
*::after {
|
|
156
|
-
animation-duration:
|
|
156
|
+
animation-duration: t.$still !important;
|
|
157
157
|
animation-iteration-count: 1 !important;
|
|
158
|
-
transition-duration:
|
|
158
|
+
transition-duration: t.$still !important;
|
|
159
159
|
scroll-behavior: auto !important;
|
|
160
160
|
}
|
|
161
161
|
}
|
package/src/tokens/theme.scss
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
@use 'radius';
|
|
8
8
|
@use 'shadow';
|
|
9
9
|
@use 'space';
|
|
10
|
+
@use 'stroke';
|
|
10
11
|
@use 'typography';
|
|
11
12
|
@use 'z';
|
|
12
13
|
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
@include shadow.emit;
|
|
15
16
|
@include space.emit;
|
|
16
17
|
@include radius.emit;
|
|
18
|
+
@include stroke.emit;
|
|
17
19
|
@include typography.emit;
|
|
18
20
|
@include motion.emit;
|
|
19
21
|
@include z.emit;
|
package/src/tokens/tokens.ts
CHANGED
|
@@ -119,6 +119,12 @@ export const radiusTokens = {
|
|
|
119
119
|
|
|
120
120
|
export type RadiusName = keyof typeof radiusTokens;
|
|
121
121
|
|
|
122
|
+
export const strokeTokens = {
|
|
123
|
+
hairline: '1px',
|
|
124
|
+
thick: '2px',
|
|
125
|
+
heavy: '3px',
|
|
126
|
+
} as const;
|
|
127
|
+
|
|
122
128
|
export const zTokens = {
|
|
123
129
|
base: '0',
|
|
124
130
|
raised: '10',
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// The blocking <head> snippet that kills the flash of wrong theme. Built from the
|
|
2
|
-
// same constants as theme.ts so the two can never disagree. Server/build-only:
|
|
3
|
-
// the hash is computed with Bun's hasher for the CSP header.
|
|
4
|
-
//
|
|
5
|
-
// DEPRECATED `As of 20.2.0`: the framework's boot inlines this script itself, from
|
|
6
|
-
// `@ultimat3/render`'s `themeScript({ fallback })` with `theme.defaultMode` in `app.config.ts` as
|
|
7
|
-
// the fallback, and admits it to the CSP — an app no longer writes either half. This module stays
|
|
8
|
-
// exported for one major so an app that inlined it by hand keeps building; it is removed in 21.
|
|
9
|
-
|
|
10
|
-
import { runtimeMissingError } from '../errors';
|
|
11
|
-
import { THEME_ATTRIBUTE, THEME_MEDIA_QUERY, THEME_STORAGE_KEY } from './theme';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Minified on purpose — this runs before first paint and is inlined into every
|
|
15
|
-
* document. Reads the explicit choice, falls back to the OS, and always stamps
|
|
16
|
-
* `data-theme` so screenshots and Playwright runs are deterministic.
|
|
17
|
-
*/
|
|
18
|
-
export const THEME_INLINE_SCRIPT =
|
|
19
|
-
`try{var d=document.documentElement,` +
|
|
20
|
-
`s=localStorage.getItem('${THEME_STORAGE_KEY}');` +
|
|
21
|
-
`d.setAttribute('${THEME_ATTRIBUTE}',` +
|
|
22
|
-
`s==='light'||s==='dark'?s:` +
|
|
23
|
-
`matchMedia('${THEME_MEDIA_QUERY}').matches?'dark':'light')}catch(e){}`;
|
|
24
|
-
|
|
25
|
-
/** The exact tag to inline. No attributes, so the CSP hash stays valid. */
|
|
26
|
-
export function themeInlineScriptTag(): string {
|
|
27
|
-
return `<script>${THEME_INLINE_SCRIPT}</script>`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
let cachedHash: string | null = null;
|
|
31
|
-
|
|
32
|
-
/** `sha256-<base64>` for `script-src`. Stable for a given script text. */
|
|
33
|
-
export function themeInlineScriptHash(): string {
|
|
34
|
-
if (cachedHash !== null) return cachedHash;
|
|
35
|
-
if (typeof Bun === 'undefined') {
|
|
36
|
-
throw runtimeMissingError(
|
|
37
|
-
'Bun.CryptoHasher to hash the theme inline script',
|
|
38
|
-
'call themeInlineScriptHash() during build or SSR, never in browser code',
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
const digest = new Bun.CryptoHasher('sha256').update(THEME_INLINE_SCRIPT).digest('base64');
|
|
42
|
-
cachedHash = `sha256-${digest}`;
|
|
43
|
-
return cachedHash;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Quoted CSP source expression, ready to concatenate into a `script-src`. */
|
|
47
|
-
export function themeInlineScriptCspSource(): string {
|
|
48
|
-
return `'${themeInlineScriptHash()}'`;
|
|
49
|
-
}
|