@enigmax/primitives 0.17.0 → 0.19.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/button-CaXaqG_K.d.ts +63 -0
- package/dist/chunk-2QFTRNAZ.js +88 -0
- package/dist/chunk-6BGBYUSZ.js +114 -0
- package/dist/chunk-AU3H5WIY.js +107 -0
- package/dist/{chunk-UZFEEFMF.js → chunk-D5A2ZMAG.js} +34 -1
- package/dist/chunk-DTWZDONY.js +99 -0
- package/dist/chunk-F25CGNQC.js +19 -0
- package/dist/chunk-FWVWX67R.js +462 -0
- package/dist/chunk-HC2ME5PU.js +168 -0
- package/dist/chunk-HS3X3XCW.js +43 -0
- package/dist/chunk-IXVMRVD4.js +96 -0
- package/dist/chunk-KJINGUQN.js +188 -0
- package/dist/chunk-MMQPZGSU.js +161 -0
- package/dist/chunk-OCMI7R6H.js +79 -0
- package/dist/chunk-QQFNAKMY.js +47 -0
- package/dist/chunk-QYMUIW5I.js +28 -0
- package/dist/chunk-R4ZAEE7V.js +249 -0
- package/dist/chunk-S653GLSF.js +17 -0
- package/dist/chunk-SNYUBXWQ.js +149 -0
- package/dist/chunk-U3V4EHOB.js +41 -0
- package/dist/chunk-UOSSNUSC.js +309 -0
- package/dist/chunk-XNNQRA35.js +31 -0
- package/dist/chunk-XQHCZAPJ.js +102 -0
- package/dist/chunk-ZCUFYBPB.js +154 -0
- package/dist/chunk-ZWR2EXHQ.js +55 -0
- package/dist/flags-BBJc9unY.d.ts +133 -0
- package/dist/index-dTdAbOWl.d.ts +144 -0
- package/dist/index.d.ts +18 -603
- package/dist/index.js +11 -2
- package/dist/input-BwXjFenq.d.ts +77 -0
- package/dist/marquee-CJ3Uwy3E.d.ts +81 -0
- package/dist/network-D2LsBG_k.d.ts +39 -0
- package/dist/next/index.d.ts +22 -4
- package/dist/next/index.js +25 -4
- package/dist/notifications-BpVV6sel.d.ts +70 -0
- package/dist/palette-D7iuQh_T.d.ts +86 -0
- package/dist/password-3DRQYAYQ.js +2 -0
- package/dist/password-C8lG4Zm9.d.ts +71 -0
- package/dist/password-FB2CUEKJ.js +1 -0
- package/dist/react/button.d.ts +75 -0
- package/dist/react/button.js +4 -0
- package/dist/react/flag.d.ts +37 -0
- package/dist/react/flag.js +3 -0
- package/dist/react/index.d.ts +29 -312
- package/dist/react/index.js +24 -3
- package/dist/react/input.d.ts +4 -0
- package/dist/react/input.js +3 -0
- package/dist/react/marquee.d.ts +44 -0
- package/dist/react/marquee.js +3 -0
- package/dist/react/network.d.ts +20 -0
- package/dist/react/network.js +3 -0
- package/dist/react/notifications.d.ts +17 -0
- package/dist/react/notifications.js +3 -0
- package/dist/react/palette.d.ts +231 -0
- package/dist/react/palette.js +5 -0
- package/dist/react/relative-time.d.ts +21 -0
- package/dist/react/relative-time.js +3 -0
- package/dist/react/search.d.ts +30 -0
- package/dist/react/search.js +3 -0
- package/dist/react/slot.d.ts +47 -0
- package/dist/react/slot.js +2 -0
- package/dist/react/toast.d.ts +40 -0
- package/dist/react/toast.js +4 -0
- package/dist/react-router/index.d.ts +22 -4
- package/dist/react-router/index.js +25 -4
- package/dist/relative-time-YpRTG7YH.d.ts +106 -0
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.js +1 -1
- package/dist/{search-CsO3L1Lw.d.ts → search-DXxY8SEH.d.ts} +15 -1
- package/dist/search-UQEXAPQB.js +50 -0
- package/package.json +54 -3
- package/recipes/input/styles.css +60 -20
- package/recipes/palette/styles.css +243 -0
- package/registry.json +186 -18
- package/src/core/flags.ts +88 -42
- package/src/core/input-icons.ts +34 -0
- package/src/core/input.ts +5 -21
- package/src/core/palette.ts +0 -0
- package/src/core/search.ts +60 -0
- package/src/index.ts +19 -3
- package/src/react/button.tsx +70 -4
- package/src/react/flag.tsx +18 -6
- package/src/react/index.ts +50 -5
- package/src/react/input/icon.tsx +17 -0
- package/src/react/input/index.tsx +307 -0
- package/src/react/input/password.tsx +174 -0
- package/src/react/input/search.tsx +82 -0
- package/src/react/input/types.ts +146 -0
- package/src/react/input/write-value.ts +18 -0
- package/src/react/palette/context.ts +60 -0
- package/src/react/palette/index.tsx +66 -0
- package/src/react/palette/root.tsx +661 -0
- package/src/react/slot.tsx +91 -0
- package/src/react/use-button.ts +3 -1
- package/dist/chunk-4GLYRF4B.js +0 -1259
- package/dist/chunk-JFNND6P4.js +0 -749
- package/src/react/input.tsx +0 -429
package/src/core/search.ts
CHANGED
|
@@ -106,6 +106,61 @@ function substringMatcher<T>(query: string, items: readonly T[], keys: string[])
|
|
|
106
106
|
return results.sort((left, right) => left.score - right.score);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Subsequence ranking: every letter of the query, in order, anywhere in the text.
|
|
111
|
+
*
|
|
112
|
+
* This is what a COMMAND palette wants and a substring filter cannot do: "qgate" finds
|
|
113
|
+
* "Quality gate" and "plyg" finds "Playground", because the letters only have to appear in
|
|
114
|
+
* order. The score rewards consecutive runs and word starts and penalises gaps, so the
|
|
115
|
+
* closest thing to what was typed comes first rather than the first thing that happened to
|
|
116
|
+
* contain the letters.
|
|
117
|
+
*
|
|
118
|
+
* Pass it as `matcher`, or take it as the default from the palette. The substring matcher
|
|
119
|
+
* stays the default for a plain search FIELD, where a typo should fail rather than quietly
|
|
120
|
+
* match something four words away.
|
|
121
|
+
*/
|
|
122
|
+
export function subsequenceMatcher<T>(keys: string[] = []): NonNullable<SearchOptions<T>["matcher"]> {
|
|
123
|
+
return (query, items) => {
|
|
124
|
+
const needle = fold(query);
|
|
125
|
+
const results: SearchMatch<T>[] = [];
|
|
126
|
+
|
|
127
|
+
for (const item of items) {
|
|
128
|
+
const fields = keys.length ? keys : [""];
|
|
129
|
+
let best: SearchMatch<T> | null = null;
|
|
130
|
+
|
|
131
|
+
for (const key of fields) {
|
|
132
|
+
const haystack = fold(key ? readPath(item, key) : String(item));
|
|
133
|
+
const score = subsequenceScore(needle, haystack);
|
|
134
|
+
if (score < 0) continue;
|
|
135
|
+
// The engine orders by ASCENDING score (Fuse's convention, 0 is exact), and
|
|
136
|
+
// this one counts upwards, so it is negated rather than compared backwards -
|
|
137
|
+
// getting that wrong ranks the worst match first and looks like a broken
|
|
138
|
+
// search rather than a sign error.
|
|
139
|
+
const ranked = -score;
|
|
140
|
+
if (!best || ranked < best.score) best = { item, score: ranked, key: key || undefined };
|
|
141
|
+
}
|
|
142
|
+
if (best) results.push(best);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return results.sort((left, right) => left.score - right.score);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** -1 when the query is not a subsequence of the text; otherwise higher is better. */
|
|
150
|
+
function subsequenceScore(query: string, text: string): number {
|
|
151
|
+
if (!query) return 0;
|
|
152
|
+
let from = 0, score = 0, run = 0;
|
|
153
|
+
for (let i = 0; i < query.length; i++) {
|
|
154
|
+
const at = text.indexOf(query[i], from);
|
|
155
|
+
if (at === -1) return -1;
|
|
156
|
+
run = i > 0 && at === from ? run + 1 : 0;
|
|
157
|
+
const wordStart = at === 0 || /[^a-z0-9]/.test(text[at - 1]);
|
|
158
|
+
score += 10 + run * 6 + (wordStart ? 8 : 0) - Math.min(at - from, 6);
|
|
159
|
+
from = at + 1;
|
|
160
|
+
}
|
|
161
|
+
return score;
|
|
162
|
+
}
|
|
163
|
+
|
|
109
164
|
export function createSearch<T>(options: SearchOptions<T> = {}): SearchInstance<T> {
|
|
110
165
|
let opts: SearchOptions<T> = { ...options };
|
|
111
166
|
let items: readonly T[] = opts.items ?? [];
|
|
@@ -205,6 +260,11 @@ export function createSearch<T>(options: SearchOptions<T> = {}): SearchInstance<
|
|
|
205
260
|
input.addEventListener("input", onInput);
|
|
206
261
|
input.addEventListener("keydown", onKeyDown);
|
|
207
262
|
input.dataset.enigmaSearch = "";
|
|
263
|
+
// A field can already hold a value when the engine reaches it: a query restored
|
|
264
|
+
// from the URL, a browser refill, or simply someone typing before the module
|
|
265
|
+
// that searches finished loading. Binding without reading it leaves the field
|
|
266
|
+
// full and the list empty until the NEXT keystroke, which reads as broken.
|
|
267
|
+
if (input.value) this.searchNow(input.value);
|
|
208
268
|
return () => {
|
|
209
269
|
input.removeEventListener("input", onInput);
|
|
210
270
|
input.removeEventListener("keydown", onKeyDown);
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { createMarquee, type MarqueeOptions, type MarqueeInstance, type MarqueeHover } from "@/core/marquee";
|
|
2
2
|
export { createInput, type InputOptions, type InputInstance, type InputAction, type InputIcon, type InputActionState } from "@/core/input";
|
|
3
|
-
export { createSearch, type SearchOptions, type SearchInstance, type SearchMatch, type FuseConstructor, type FuseLike } from "@/core/search";
|
|
3
|
+
export { createSearch, subsequenceMatcher, type SearchOptions, type SearchInstance, type SearchMatch, type FuseConstructor, type FuseLike } from "@/core/search";
|
|
4
4
|
export { createButton, type ButtonOptions, type ButtonInstance, type ButtonState, type ButtonElement, type ButtonCooldown } from "@/core/button";
|
|
5
5
|
export { INPUT_ICON_PATHS, iconMarkup } from "@/core/input";
|
|
6
6
|
export {
|
|
@@ -31,13 +31,13 @@ export {
|
|
|
31
31
|
flagSrc,
|
|
32
32
|
flagView,
|
|
33
33
|
flagAttributes,
|
|
34
|
+
flagName,
|
|
34
35
|
normalizeFlagCode,
|
|
35
36
|
configureFlags,
|
|
36
37
|
flagConfig,
|
|
37
38
|
resetFlagConfig,
|
|
38
39
|
FLAG_RATIO,
|
|
39
|
-
|
|
40
|
-
CIRCLE_FLAGS_REF,
|
|
40
|
+
FLAG_CDN,
|
|
41
41
|
type FlagOptions,
|
|
42
42
|
type FlagConfig,
|
|
43
43
|
type FlagView,
|
|
@@ -45,3 +45,19 @@ export {
|
|
|
45
45
|
type FlagFormat,
|
|
46
46
|
type FlagSource
|
|
47
47
|
} from "@/core/flags";
|
|
48
|
+
// The palette's non-rendering half: no framework anywhere near it, so a vanilla or Astro
|
|
49
|
+
// page can run the same keyboard arithmetic, grouping and history the React palette does.
|
|
50
|
+
export {
|
|
51
|
+
createRecentStore,
|
|
52
|
+
recentKey,
|
|
53
|
+
groupRows,
|
|
54
|
+
moveActive,
|
|
55
|
+
shortcutLabel,
|
|
56
|
+
isPaletteShortcut,
|
|
57
|
+
type RecentEntry,
|
|
58
|
+
type RecentStore,
|
|
59
|
+
type RecentStoreOptions,
|
|
60
|
+
type RowGroup,
|
|
61
|
+
type PositionedRow,
|
|
62
|
+
type PaletteKey
|
|
63
|
+
} from "@/core/palette";
|
package/src/react/button.tsx
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { Slot } from "@/react/slot";
|
|
3
4
|
import { useButton } from "@/react/use-button";
|
|
4
5
|
import { getLinkComponent } from "@/react/link";
|
|
5
6
|
import type { ButtonOptions, ButtonState } from "@/core/button";
|
|
6
|
-
import { createElement, forwardRef, type ComponentPropsWithoutRef, type ElementType, type ReactNode } from "react";
|
|
7
|
+
import { createElement, forwardRef, isValidElement, type ComponentPropsWithoutRef, type ElementType, type ReactNode } from "react";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* `<Button>` - the component, for the ninety percent.
|
|
@@ -51,6 +52,49 @@ export interface ButtonProps extends ButtonOptions, Omit<ComponentPropsWithoutRe
|
|
|
51
52
|
pending?: ReactNode;
|
|
52
53
|
/** `type` on a real button. Ignored on a link, which has none. */
|
|
53
54
|
type?: "button" | "submit" | "reset";
|
|
55
|
+
/**
|
|
56
|
+
* Show the shortcut as a key badge after the label, the way Stripe writes
|
|
57
|
+
* "Create invoice n".
|
|
58
|
+
*
|
|
59
|
+
* `"auto"` (the default) shows it when there is a shortcut AND the label renders text:
|
|
60
|
+
* beside a lone glyph on an icon-only button the badge is noise, and it would double the
|
|
61
|
+
* width of the smallest control on the screen. `true` and `false` decide it outright.
|
|
62
|
+
*/
|
|
63
|
+
shortcutHint?: boolean | "auto";
|
|
64
|
+
/** Render the badge yourself. Return null to drop it for one button. */
|
|
65
|
+
renderShortcut?: (key: string) => ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Put the behaviour on YOUR element instead of ours - a `motion.button`, your design
|
|
68
|
+
* system's button, a router Link, anything.
|
|
69
|
+
*
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <Button asChild cooldown={30_000} onPress={resend}>
|
|
72
|
+
* <motion.button whileTap={{ scale: 0.98 }}>Resend</motion.button>
|
|
73
|
+
* </Button>
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* The child owns its own markup, so the shortcut badge is not injected - the child is
|
|
77
|
+
* rendered exactly as written. Read `state.shortcut` from `useButton` to place your own.
|
|
78
|
+
*/
|
|
79
|
+
asChild?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Whether a label renders any TEXT.
|
|
84
|
+
*
|
|
85
|
+
* Walked rather than assumed: `<Button shortcut="s"><Icon /></Button>` and
|
|
86
|
+
* `<Button shortcut="s"><Icon /> Save</Button>` differ only in a string buried in the tree,
|
|
87
|
+
* and that string is the whole difference between a hint and a decoration. An element whose
|
|
88
|
+
* text arrives from somewhere this cannot see reports false, so `auto` errs towards the
|
|
89
|
+
* quieter button; `shortcutHint` overrides it in one word.
|
|
90
|
+
*/
|
|
91
|
+
function hasText(node: ReactNode): boolean {
|
|
92
|
+
if (node === null || node === undefined || typeof node === "boolean") return false;
|
|
93
|
+
if (typeof node === "string") return node.trim().length > 0;
|
|
94
|
+
if (typeof node === "number") return true;
|
|
95
|
+
if (Array.isArray(node)) return node.some(hasText);
|
|
96
|
+
if (isValidElement(node)) return hasText((node.props as { children?: ReactNode; }).children);
|
|
97
|
+
return false;
|
|
54
98
|
}
|
|
55
99
|
|
|
56
100
|
export const Button = forwardRef<HTMLElement, ButtonProps>(function Button({
|
|
@@ -66,6 +110,9 @@ export const Button = forwardRef<HTMLElement, ButtonProps>(function Button({
|
|
|
66
110
|
onClick,
|
|
67
111
|
onChange,
|
|
68
112
|
type = "button",
|
|
113
|
+
shortcutHint = "auto",
|
|
114
|
+
renderShortcut,
|
|
115
|
+
asChild = false,
|
|
69
116
|
...rest
|
|
70
117
|
}, ref) {
|
|
71
118
|
const button = useButton({ href, disabled, loading, cooldown, shortcut, onPress: onPress ?? onClick, onChange });
|
|
@@ -75,17 +122,36 @@ export const Button = forwardRef<HTMLElement, ButtonProps>(function Button({
|
|
|
75
122
|
// to setLinkComponent - and a plain <a> when nothing was. The package cannot import a
|
|
76
123
|
// router itself, so registering one is what turns every href into a client navigation
|
|
77
124
|
// without a word at the call site.
|
|
78
|
-
const Tag: ElementType = as ?? (state.element === "a" ? getLinkComponent() : "button");
|
|
125
|
+
const Tag: ElementType = asChild ? Slot : as ?? (state.element === "a" ? getLinkComponent() : "button");
|
|
79
126
|
|
|
80
127
|
// `type="button"` is the default on purpose. A bare <button> inside a form submits it,
|
|
81
|
-
// so an action button that forgot it posts the form instead of doing its job.
|
|
128
|
+
// so an action button that forgot it posts the form instead of doing its job. Never
|
|
129
|
+
// injected through a slot: the child may be an anchor or a div, where `type` is either
|
|
130
|
+
// invalid or means something else entirely.
|
|
82
131
|
const native = Tag === "button" ? { type } : {};
|
|
83
132
|
|
|
84
133
|
const label = typeof children === "function" ? children(state) : children;
|
|
134
|
+
const content = state.loading && pending !== undefined ? pending : label;
|
|
135
|
+
|
|
136
|
+
// Not while it is working: the label has already been replaced by whatever `pending`
|
|
137
|
+
// says, and a key that does nothing right now is not a hint.
|
|
138
|
+
// Never through a slot: it takes exactly one child, and a second one would throw.
|
|
139
|
+
const showHint = !asChild && Boolean(state.shortcut) && !state.loading &&
|
|
140
|
+
(shortcutHint === "auto" ? hasText(content) : shortcutHint);
|
|
141
|
+
const hint = showHint && state.shortcut
|
|
142
|
+
? renderShortcut?.(state.shortcut) ?? createElement(
|
|
143
|
+
"kbd",
|
|
144
|
+
// Hidden from the accessible name - the button already carries
|
|
145
|
+
// `aria-keyshortcuts`, and "Save n" is not what anyone wants read out.
|
|
146
|
+
{ "data-enigma-button-key": "", "aria-hidden": true },
|
|
147
|
+
state.shortcut
|
|
148
|
+
)
|
|
149
|
+
: null;
|
|
85
150
|
|
|
86
151
|
return createElement(
|
|
87
152
|
Tag,
|
|
88
153
|
{ ref, ...button.props, ...native, ...rest },
|
|
89
|
-
|
|
154
|
+
content,
|
|
155
|
+
hint
|
|
90
156
|
);
|
|
91
157
|
});
|
package/src/react/flag.tsx
CHANGED
|
@@ -7,10 +7,17 @@ export interface FlagProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "sr
|
|
|
7
7
|
/** `es`, `ES`, `en-GB`, `gb-eng`, or the emoji flag you are replacing. */
|
|
8
8
|
code: string | null | undefined;
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Your own accessible name. Left out, the country's own name is used, in the reader's
|
|
11
|
+
* language - so a flag standing on its own is never a silent image.
|
|
12
12
|
*/
|
|
13
13
|
label?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Beside a country name that is already on screen the flag is decoration, and repeating
|
|
16
|
+
* the name only makes a screen reader say it twice.
|
|
17
|
+
*/
|
|
18
|
+
decorative?: boolean;
|
|
19
|
+
/** Language the automatic name is written in. Follows the document's `lang` otherwise. */
|
|
20
|
+
locale?: string;
|
|
14
21
|
shape?: FlagShape;
|
|
15
22
|
format?: FlagFormat;
|
|
16
23
|
source?: FlagSource;
|
|
@@ -28,8 +35,8 @@ export interface FlagProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "sr
|
|
|
28
35
|
* The defaults come from `configureFlags`, so moving the whole application from the CDN to
|
|
29
36
|
* a downloaded set is one line at startup and no change at any call site.
|
|
30
37
|
*/
|
|
31
|
-
export function Flag({ code, label, shape, format, source, basePath, size, fallback = null, ...rest }: FlagProps): ReactNode {
|
|
32
|
-
const options: FlagOptions = { shape, format, source, basePath, size };
|
|
38
|
+
export function Flag({ code, label, decorative = false, locale, shape, format, source, basePath, size, fallback = null, ...rest }: FlagProps): ReactNode {
|
|
39
|
+
const options: FlagOptions = { shape, format, source, basePath, size, locale };
|
|
33
40
|
// An explicit `undefined` prop must not beat the configured default, so the unset ones
|
|
34
41
|
// are dropped rather than spread over the config.
|
|
35
42
|
for (const key of Object.keys(options) as (keyof FlagOptions)[]) {
|
|
@@ -39,16 +46,21 @@ export function Flag({ code, label, shape, format, source, basePath, size, fallb
|
|
|
39
46
|
const view = flagView(code, options);
|
|
40
47
|
if (!view) return fallback;
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
// Automatic unless told otherwise: the country's own name, in the reader's language.
|
|
50
|
+
const name = decorative ? "" : label?.trim() || view.name || "";
|
|
43
51
|
return (
|
|
44
52
|
<img
|
|
45
53
|
src={view.src}
|
|
46
|
-
alt={name
|
|
54
|
+
alt={name}
|
|
47
55
|
width={view.width}
|
|
48
56
|
height={view.height}
|
|
49
57
|
loading="lazy"
|
|
50
58
|
decoding="async"
|
|
51
59
|
aria-hidden={name ? undefined : true}
|
|
60
|
+
// The name comes from the reader's own runtime, so a server render and its
|
|
61
|
+
// hydration can legitimately disagree on the language it is written in. Same
|
|
62
|
+
// escape hatch, and same reason, as the relative timestamp.
|
|
63
|
+
suppressHydrationWarning
|
|
52
64
|
data-enigma-flag=""
|
|
53
65
|
data-flag-code={view.code}
|
|
54
66
|
data-flag-shape={view.shape}
|
package/src/react/index.ts
CHANGED
|
@@ -10,16 +10,23 @@ export { useButton, type UseButtonResult } from "@/react/use-button";
|
|
|
10
10
|
export { Button, type ButtonProps } from "@/react/button";
|
|
11
11
|
export { setLinkComponent, getLinkComponent } from "@/react/link";
|
|
12
12
|
export { type ButtonOptions, type ButtonState } from "@/core/button";
|
|
13
|
+
export { Input } from "@/react/input";
|
|
13
14
|
export {
|
|
14
|
-
Input,
|
|
15
|
-
PasswordStrength,
|
|
16
15
|
type InputProps,
|
|
17
|
-
type
|
|
16
|
+
type InputBaseProps,
|
|
17
|
+
type InputType,
|
|
18
18
|
type FieldAction,
|
|
19
19
|
type BreachChecker,
|
|
20
20
|
type BreachState,
|
|
21
|
-
type BreachStatus
|
|
22
|
-
|
|
21
|
+
type BreachStatus,
|
|
22
|
+
type PasswordOnlyProps,
|
|
23
|
+
type SearchOnlyProps,
|
|
24
|
+
type PlainOnlyProps
|
|
25
|
+
} from "@/react/input/types";
|
|
26
|
+
// The meter as a standalone component, for a form that renders it somewhere else. Re-exported
|
|
27
|
+
// from the chunk rather than through `Input`, so importing only `Input` leaves it behind.
|
|
28
|
+
export { PasswordStrength, type PasswordStrengthProps } from "@/react/input/password";
|
|
29
|
+
export { Slot, mergeSlotProps, type SlotProps } from "@/react/slot";
|
|
23
30
|
export {
|
|
24
31
|
generatePassword,
|
|
25
32
|
estimatePasswordStrength,
|
|
@@ -33,11 +40,22 @@ export { Toaster, type ToasterProps, type ToastPosition, type ToastControls } fr
|
|
|
33
40
|
export { useNetworkState } from "@/react/use-network-state";
|
|
34
41
|
export { type NetworkState } from "@/core/network";
|
|
35
42
|
export { RelativeTime, type RelativeTimeProps } from "@/react/relative-time";
|
|
43
|
+
// The option types too: a wrapper around any of these has to be able to name its own props.
|
|
44
|
+
export type {
|
|
45
|
+
RelativeTimeOptions,
|
|
46
|
+
RelativeTimeFormat,
|
|
47
|
+
RelativeTimeTense,
|
|
48
|
+
RelativeTimePrecision,
|
|
49
|
+
RelativeTimeStyle
|
|
50
|
+
} from "@/core/relative-time";
|
|
51
|
+
export type { NotificationTone, NotificationAction, NotificationInput, Notification, NotificationsOptions } from "@/core/notifications";
|
|
52
|
+
export type { SearchInstance, FuseLike } from "@/core/search";
|
|
36
53
|
export { Flag, type FlagProps } from "@/react/flag";
|
|
37
54
|
export {
|
|
38
55
|
flagSrc,
|
|
39
56
|
flagView,
|
|
40
57
|
flagAttributes,
|
|
58
|
+
flagName,
|
|
41
59
|
normalizeFlagCode,
|
|
42
60
|
configureFlags,
|
|
43
61
|
type FlagOptions,
|
|
@@ -45,3 +63,30 @@ export {
|
|
|
45
63
|
type FlagFormat,
|
|
46
64
|
type FlagSource
|
|
47
65
|
} from "@/core/flags";
|
|
66
|
+
export {
|
|
67
|
+
SearchPalette,
|
|
68
|
+
PaletteRoot,
|
|
69
|
+
PaletteTrigger,
|
|
70
|
+
PaletteContent,
|
|
71
|
+
PaletteField,
|
|
72
|
+
PaletteList,
|
|
73
|
+
PaletteItem,
|
|
74
|
+
PaletteFooter,
|
|
75
|
+
usePaletteContext,
|
|
76
|
+
type SearchPaletteProps,
|
|
77
|
+
type PaletteRootProps,
|
|
78
|
+
type PaletteListProps,
|
|
79
|
+
type PaletteSection,
|
|
80
|
+
type PaletteRow,
|
|
81
|
+
type PaletteContextValue
|
|
82
|
+
} from "@/react/palette";
|
|
83
|
+
export {
|
|
84
|
+
createRecentStore,
|
|
85
|
+
recentKey,
|
|
86
|
+
groupRows,
|
|
87
|
+
moveActive,
|
|
88
|
+
shortcutLabel,
|
|
89
|
+
isPaletteShortcut,
|
|
90
|
+
type RecentEntry,
|
|
91
|
+
type RecentStore
|
|
92
|
+
} from "@/core/palette";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The built-in glyphs, drawn from the shared path data.
|
|
5
|
+
*
|
|
6
|
+
* Stroked with `currentColor` at 1em, so an icon inherits the field's text colour and size
|
|
7
|
+
* and needs no styling of its own. Replacing one is a `FieldAction` with the same `name`.
|
|
8
|
+
*/
|
|
9
|
+
const ICON_PROPS = {
|
|
10
|
+
viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none",
|
|
11
|
+
stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round",
|
|
12
|
+
"aria-hidden": true
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export function Icon({ paths }: { paths: readonly string[]; }): ReactNode {
|
|
16
|
+
return <svg {...ICON_PROPS}>{paths.map((path) => <path key={path} d={path} />)}</svg>;
|
|
17
|
+
}
|