@urbicon-ui/blocks 6.36.0 → 6.37.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/dist/components/PinInput/PinInput.svelte +321 -0
- package/dist/components/PinInput/PinInput.svelte.d.ts +4 -0
- package/dist/components/PinInput/index.d.ts +98 -0
- package/dist/components/PinInput/index.js +2 -0
- package/dist/components/PinInput/pin-input.variants.d.ts +96 -0
- package/dist/components/PinInput/pin-input.variants.js +111 -0
- package/dist/components/QRCode/QRCode.svelte +107 -0
- package/dist/components/QRCode/QRCode.svelte.d.ts +4 -0
- package/dist/components/QRCode/index.d.ts +74 -0
- package/dist/components/QRCode/index.js +3 -0
- package/dist/components/QRCode/qr-code.variants.d.ts +25 -0
- package/dist/components/QRCode/qr-code.variants.js +24 -0
- package/dist/components/QRCode/qr-encode.d.ts +22 -0
- package/dist/components/QRCode/qr-encode.js +528 -0
- package/dist/components/TimeInput/TimeInput.svelte +521 -0
- package/dist/components/TimeInput/TimeInput.svelte.d.ts +4 -0
- package/dist/components/TimeInput/index.d.ts +93 -0
- package/dist/components/TimeInput/index.js +2 -0
- package/dist/components/TimeInput/time-input.variants.d.ts +131 -0
- package/dist/components/TimeInput/time-input.variants.js +120 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +3 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/translations/de.d.ts +6 -0
- package/dist/translations/de.js +6 -0
- package/dist/translations/en.d.ts +6 -0
- package/dist/translations/en.js +6 -0
- package/package.json +3 -3
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useBlocksI18n } from '../..';
|
|
3
|
+
import { getBlocksConfig, resolveSlotClasses } from '../../provider';
|
|
4
|
+
import type { QRCodeProps } from './index';
|
|
5
|
+
import { qrCodeVariants, type QRCodeVariants } from './qr-code.variants';
|
|
6
|
+
import { encodeQr } from './qr-encode';
|
|
7
|
+
|
|
8
|
+
const bt = useBlocksI18n();
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
value,
|
|
12
|
+
errorCorrection = 'M',
|
|
13
|
+
size = 160,
|
|
14
|
+
quietZone = 4,
|
|
15
|
+
foreground = 'currentColor',
|
|
16
|
+
background = 'transparent',
|
|
17
|
+
minVersion,
|
|
18
|
+
maxVersion,
|
|
19
|
+
frame = 'none',
|
|
20
|
+
onError,
|
|
21
|
+
class: className = '',
|
|
22
|
+
unstyled: unstyledProp = false,
|
|
23
|
+
slotClasses: slotClassesProp = {},
|
|
24
|
+
preset,
|
|
25
|
+
id,
|
|
26
|
+
'aria-label': ariaLabel
|
|
27
|
+
}: QRCodeProps = $props();
|
|
28
|
+
|
|
29
|
+
const blocksConfig = getBlocksConfig();
|
|
30
|
+
const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
|
|
31
|
+
|
|
32
|
+
const variantProps: QRCodeVariants = $derived({ frame });
|
|
33
|
+
const styles = $derived(qrCodeVariants(variantProps));
|
|
34
|
+
const slotClasses = $derived(
|
|
35
|
+
resolveSlotClasses(blocksConfig, 'QRCode', preset, variantProps, slotClassesProp)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Encode once per (value, level, bounds) change. A too-long payload surfaces
|
|
39
|
+
// as an `error` result (visible fallback) rather than throwing through render.
|
|
40
|
+
// Kept pure — the `onError` side effect runs from an $effect below, so a
|
|
41
|
+
// consumer handler may safely mutate its own state (a callback fired inside
|
|
42
|
+
// `$derived.by` would trip `state_unsafe_mutation`).
|
|
43
|
+
const result = $derived.by((): { modules: boolean[][] } | { error: Error } => {
|
|
44
|
+
try {
|
|
45
|
+
return { modules: encodeQr(value, errorCorrection, { minVersion, maxVersion }) };
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
$effect(() => {
|
|
52
|
+
if ('error' in result) onError?.(result.error);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const dimension = $derived('modules' in result ? result.modules.length + quietZone * 2 : 0);
|
|
56
|
+
|
|
57
|
+
// One SVG path for all dark modules — a single element scales crisply and
|
|
58
|
+
// keeps the DOM light even for large codes (hundreds of modules).
|
|
59
|
+
const pathData = $derived.by(() => {
|
|
60
|
+
if (!('modules' in result)) return '';
|
|
61
|
+
const mod = result.modules;
|
|
62
|
+
let d = '';
|
|
63
|
+
for (let y = 0; y < mod.length; y++) {
|
|
64
|
+
for (let x = 0; x < mod.length; x++) {
|
|
65
|
+
if (mod[y][x]) d += `M${x + quietZone} ${y + quietZone}h1v1h-1z`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return d;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const label = $derived(ariaLabel ?? bt('accessibility.qrCode'));
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<span
|
|
75
|
+
{id}
|
|
76
|
+
class={unstyled
|
|
77
|
+
? [slotClasses?.root, className].filter(Boolean).join(' ')
|
|
78
|
+
: styles.root({ class: [slotClasses?.root, className] })}
|
|
79
|
+
>
|
|
80
|
+
{#if 'modules' in result}
|
|
81
|
+
<svg
|
|
82
|
+
width={size}
|
|
83
|
+
height={size}
|
|
84
|
+
viewBox="0 0 {dimension} {dimension}"
|
|
85
|
+
shape-rendering="crispEdges"
|
|
86
|
+
role="img"
|
|
87
|
+
aria-label={label}
|
|
88
|
+
class={unstyled ? (slotClasses?.svg ?? '') : styles.svg({ class: slotClasses?.svg })}
|
|
89
|
+
>
|
|
90
|
+
{#if background !== 'transparent' && background !== 'none'}
|
|
91
|
+
<rect width={dimension} height={dimension} fill={background} />
|
|
92
|
+
{/if}
|
|
93
|
+
<path d={pathData} fill={foreground} />
|
|
94
|
+
</svg>
|
|
95
|
+
{:else}
|
|
96
|
+
<span
|
|
97
|
+
role="img"
|
|
98
|
+
aria-label={label}
|
|
99
|
+
style="width: {size}px; height: {size}px;"
|
|
100
|
+
class={unstyled
|
|
101
|
+
? (slotClasses?.fallback ?? '')
|
|
102
|
+
: styles.fallback({ class: slotClasses?.fallback })}
|
|
103
|
+
>
|
|
104
|
+
{result.error.message}
|
|
105
|
+
</span>
|
|
106
|
+
{/if}
|
|
107
|
+
</span>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { QRCodeSlots, QRCodeVariants } from './qr-code.variants.js';
|
|
2
|
+
/**
|
|
3
|
+
* @description Renders any text or URL as a scannable QR code — SVG output, no
|
|
4
|
+
* runtime dependency. The encoder (`encodeQr`, exported alongside) is a from-
|
|
5
|
+
* scratch ISO/IEC 18004 implementation covering numeric / alphanumeric / byte
|
|
6
|
+
* modes, all 40 versions, the four error-correction levels, and automatic mask
|
|
7
|
+
* selection; every generated matrix is verified to round-trip through a real
|
|
8
|
+
* decoder. This completes the auth package's zero-dependency 2FA story: pass an
|
|
9
|
+
* `otpauth://` URI here instead of wiring an external QR library into
|
|
10
|
+
* `TwoFactorManager`'s `qr` snippet.
|
|
11
|
+
*
|
|
12
|
+
* @tag display
|
|
13
|
+
* @related PinInput
|
|
14
|
+
* @stability beta
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```svelte
|
|
18
|
+
* <QRCode value="https://ui.urbicon.de" size={180} frame="card" />
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @example Encoding a 2FA otpauth URI with high error correction
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <QRCode value={otpauthUri} errorCorrection="H" size={200} frame="card" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface QRCodeProps extends QRCodeVariants {
|
|
27
|
+
/** The data to encode — text, a URL, an `otpauth://` URI, etc. */
|
|
28
|
+
value: string;
|
|
29
|
+
/**
|
|
30
|
+
* Error-correction level: higher levels survive more damage/occlusion at the
|
|
31
|
+
* cost of a denser (larger) code. L≈7%, M≈15%, Q≈25%, H≈30%. @default 'M'
|
|
32
|
+
*/
|
|
33
|
+
errorCorrection?: 'L' | 'M' | 'Q' | 'H';
|
|
34
|
+
/** Rendered edge length in pixels (the code is square). @default 160 */
|
|
35
|
+
size?: number;
|
|
36
|
+
/** Width of the mandatory light border, in modules. The spec requires ≥4. @default 4 */
|
|
37
|
+
quietZone?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Colour of the dark modules — any CSS colour. Defaults to `currentColor` so
|
|
40
|
+
* the code inherits the surrounding text colour. For guaranteed scannability
|
|
41
|
+
* keep a high-contrast dark-on-light pairing (see `frame="card"`).
|
|
42
|
+
* @default 'currentColor'
|
|
43
|
+
*/
|
|
44
|
+
foreground?: string;
|
|
45
|
+
/** Background fill — any CSS colour. @default 'transparent' */
|
|
46
|
+
background?: string;
|
|
47
|
+
/** Lower bound on the QR version (1–40) to force a minimum size. */
|
|
48
|
+
minVersion?: number;
|
|
49
|
+
/** Upper bound on the QR version (1–40); encoding throws if the data does not fit. */
|
|
50
|
+
maxVersion?: number;
|
|
51
|
+
/** Called when the data cannot be encoded (e.g. too long for `maxVersion`). */
|
|
52
|
+
onError?: (error: Error) => void;
|
|
53
|
+
/** Extra classes merged onto the root wrapper. */
|
|
54
|
+
class?: string;
|
|
55
|
+
/** Remove all default tv() classes — only user-provided classes apply. */
|
|
56
|
+
unstyled?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Per-slot class overrides merged with tv() styles. Slots: root (what `class`
|
|
59
|
+
* also targets) | svg | fallback.
|
|
60
|
+
*/
|
|
61
|
+
slotClasses?: Partial<Record<QRCodeSlots, string>>;
|
|
62
|
+
/** Apply a named preset registered via `<BlocksProvider presets={{ QRCode: {...} }}>`. */
|
|
63
|
+
preset?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Accessible name announced for the code. Defaults to a localized "QR code".
|
|
66
|
+
* Avoid echoing sensitive payloads (e.g. a 2FA secret) into this label.
|
|
67
|
+
*/
|
|
68
|
+
'aria-label'?: string;
|
|
69
|
+
/** Root id. */
|
|
70
|
+
id?: string;
|
|
71
|
+
}
|
|
72
|
+
export { default as QRCode } from './QRCode.svelte';
|
|
73
|
+
export { type QRCodeVariants, qrCodeVariants } from './qr-code.variants.js';
|
|
74
|
+
export { encodeQr, type QrEcl } from './qr-encode.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type SlotNames, type VariantProps } from '../../utils/variants.js';
|
|
2
|
+
export declare const qrCodeVariants: ((props?: {
|
|
3
|
+
frame?: "none" | "card" | undefined;
|
|
4
|
+
} | undefined) => {
|
|
5
|
+
root: (props?: ({
|
|
6
|
+
frame?: "none" | "card" | undefined;
|
|
7
|
+
} & {
|
|
8
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
9
|
+
}) | undefined) => string;
|
|
10
|
+
svg: (props?: ({
|
|
11
|
+
frame?: "none" | "card" | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
14
|
+
}) | undefined) => string;
|
|
15
|
+
fallback: (props?: ({
|
|
16
|
+
frame?: "none" | "card" | undefined;
|
|
17
|
+
} & {
|
|
18
|
+
class?: string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | (string | number | false | /*elided*/ any | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined)[] | Record<string, boolean | null | undefined> | null | undefined;
|
|
19
|
+
}) | undefined) => string;
|
|
20
|
+
}) & {
|
|
21
|
+
readonly config: import("../../utils/variants.js").TVConfig;
|
|
22
|
+
};
|
|
23
|
+
export type QRCodeVariants = VariantProps<typeof qrCodeVariants>;
|
|
24
|
+
/** Slot names derived from the `tv()` config above — single source of truth for `slotClasses`. */
|
|
25
|
+
export type QRCodeSlots = SlotNames<typeof qrCodeVariants>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { tv } from '../../utils/variants.js';
|
|
2
|
+
export const qrCodeVariants = tv({
|
|
3
|
+
slots: {
|
|
4
|
+
root: ['inline-flex items-center justify-center'],
|
|
5
|
+
svg: ['block h-auto max-w-full'],
|
|
6
|
+
fallback: [
|
|
7
|
+
'inline-flex items-center justify-center text-center text-xs',
|
|
8
|
+
'border border-dashed border-border-default text-text-tertiary bg-surface-subtle rounded-modify p-2'
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
variants: {
|
|
12
|
+
// Optional framing so the code can sit on a guaranteed-light card (the
|
|
13
|
+
// scan-safe default look) without the consumer hand-building one.
|
|
14
|
+
frame: {
|
|
15
|
+
none: {},
|
|
16
|
+
card: {
|
|
17
|
+
root: 'bg-surface-base border border-border-subtle rounded-lg p-3 shadow-[var(--blocks-shadow-sm)]'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
frame: 'none'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-dependency QR Code encoder (ISO/IEC 18004).
|
|
3
|
+
*
|
|
4
|
+
* Produces the module matrix for a piece of text — no rendering, no runtime
|
|
5
|
+
* dependency. Supports numeric / alphanumeric / byte segment modes (whichever
|
|
6
|
+
* is most compact for the input), all 40 versions, the four error-correction
|
|
7
|
+
* levels, and automatic data-mask selection by the standard penalty rules.
|
|
8
|
+
*
|
|
9
|
+
* The algorithm follows the reference structure of Project Nayuki's public
|
|
10
|
+
* QR generator (the canonical, spec-faithful design); the code here is an
|
|
11
|
+
* independent TypeScript implementation of the ISO standard.
|
|
12
|
+
*/
|
|
13
|
+
export type QrEcl = 'L' | 'M' | 'Q' | 'H';
|
|
14
|
+
/**
|
|
15
|
+
* Encode `text` into a QR module matrix (`true` = dark). The returned matrix has
|
|
16
|
+
* no quiet zone — the renderer adds it. Throws when the text does not fit any
|
|
17
|
+
* version at the requested ECC level.
|
|
18
|
+
*/
|
|
19
|
+
export declare function encodeQr(text: string, ecl?: QrEcl, opts?: {
|
|
20
|
+
minVersion?: number;
|
|
21
|
+
maxVersion?: number;
|
|
22
|
+
}): boolean[][];
|