@mlola-ui/registry 1.0.6 → 1.0.8
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/components/accordion.json +2 -2
- package/components/activity-heatmap.json +1 -1
- package/components/alert.json +1 -1
- package/components/app-shell.json +1 -1
- package/components/badge.json +1 -1
- package/components/breadcrumb.json +1 -1
- package/components/button.json +5 -3
- package/components/calendar.json +1 -1
- package/components/carousel.json +2 -2
- package/components/checkbox.json +1 -1
- package/components/color-picker.json +1 -1
- package/components/combobox.json +1 -1
- package/components/command-menu.json +1 -1
- package/components/context-menu.json +2 -2
- package/components/copy-button.json +95 -0
- package/components/date-picker.json +1 -1
- package/components/dropdown-menu.json +2 -2
- package/components/dropzone.json +1 -1
- package/components/hover-card.json +1 -1
- package/components/modal.json +2 -2
- package/components/number-input.json +1 -1
- package/components/pagination.json +1 -1
- package/components/password-input.json +75 -0
- package/components/popover.json +1 -1
- package/components/segmented-control.json +1 -1
- package/components/select.json +2 -2
- package/components/sheet.json +2 -2
- package/components/slider.json +1 -1
- package/components/spinner.json +64 -0
- package/components/stepper.json +1 -1
- package/components/tabs.json +1 -1
- package/components/tag-input.json +1 -1
- package/components/time-picker.json +1 -1
- package/components/timeline.json +1 -1
- package/components/toast.json +5 -3
- package/components/tour.json +2 -2
- package/generated/catalog.ts +235 -42
- package/generated/examples.json +268 -8
- package/index.json +20 -2
- package/package.json +1 -1
- package/snapshot/index.json +288 -47
- package/snapshot/items/components/accordion.json +2 -2
- package/snapshot/items/components/activity-heatmap.json +1 -1
- package/snapshot/items/components/alert.json +1 -1
- package/snapshot/items/components/app-shell.json +1 -1
- package/snapshot/items/components/badge.json +1 -1
- package/snapshot/items/components/breadcrumb.json +1 -1
- package/snapshot/items/components/button.json +5 -3
- package/snapshot/items/components/calendar.json +1 -1
- package/snapshot/items/components/carousel.json +2 -2
- package/snapshot/items/components/checkbox.json +1 -1
- package/snapshot/items/components/color-picker.json +1 -1
- package/snapshot/items/components/combobox.json +1 -1
- package/snapshot/items/components/command-menu.json +1 -1
- package/snapshot/items/components/context-menu.json +2 -2
- package/snapshot/items/components/copy-button.json +95 -0
- package/snapshot/items/components/date-picker.json +1 -1
- package/snapshot/items/components/dropdown-menu.json +2 -2
- package/snapshot/items/components/dropzone.json +1 -1
- package/snapshot/items/components/hover-card.json +1 -1
- package/snapshot/items/components/modal.json +2 -2
- package/snapshot/items/components/number-input.json +1 -1
- package/snapshot/items/components/pagination.json +1 -1
- package/snapshot/items/components/password-input.json +75 -0
- package/snapshot/items/components/popover.json +1 -1
- package/snapshot/items/components/segmented-control.json +1 -1
- package/snapshot/items/components/select.json +2 -2
- package/snapshot/items/components/sheet.json +2 -2
- package/snapshot/items/components/slider.json +1 -1
- package/snapshot/items/components/spinner.json +64 -0
- package/snapshot/items/components/stepper.json +1 -1
- package/snapshot/items/components/tabs.json +1 -1
- package/snapshot/items/components/tag-input.json +1 -1
- package/snapshot/items/components/time-picker.json +1 -1
- package/snapshot/items/components/timeline.json +1 -1
- package/snapshot/items/components/toast.json +5 -3
- package/snapshot/items/components/tour.json +2 -2
- package/snapshot/source/packages/components/_internal/clipboard.ts +31 -0
- package/snapshot/source/packages/components/activity-heatmap/activity-heatmap.tsx +2 -2
- package/snapshot/source/packages/components/button/button.tsx +2 -1
- package/snapshot/source/packages/components/copy-button/copy-button.tsx +59 -0
- package/snapshot/source/packages/components/password-input/password-input.tsx +118 -0
- package/snapshot/source/packages/components/spinner/spinner.tsx +36 -0
- package/snapshot/source/packages/components/toast/toast.tsx +2 -1
- package/snapshot/source-manifest.json +7 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schema/registry-item.schema.json",
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"name": "copy-button",
|
|
5
|
+
"title": "Copy Button",
|
|
6
|
+
"description": "Copies a value to the clipboard and confirms it, with the outcome spoken to screen readers and a refusal explained",
|
|
7
|
+
"category": "actions",
|
|
8
|
+
"tags": [
|
|
9
|
+
"clipboard",
|
|
10
|
+
"copy",
|
|
11
|
+
"share"
|
|
12
|
+
],
|
|
13
|
+
"type": "registry:ui",
|
|
14
|
+
"usage": {
|
|
15
|
+
"importPath": "{{aliases.components}}/copy-button",
|
|
16
|
+
"exportName": "CopyButton",
|
|
17
|
+
"isDefault": false
|
|
18
|
+
},
|
|
19
|
+
"registryDependencies": [
|
|
20
|
+
"button"
|
|
21
|
+
],
|
|
22
|
+
"engineDependencies": {
|
|
23
|
+
"@mlola-ui/icons": "^1.0.8"
|
|
24
|
+
},
|
|
25
|
+
"options": [
|
|
26
|
+
{
|
|
27
|
+
"component": "CopyButton",
|
|
28
|
+
"prop": "size",
|
|
29
|
+
"values": [
|
|
30
|
+
"sm",
|
|
31
|
+
"md",
|
|
32
|
+
"lg",
|
|
33
|
+
"icon"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"component": "CopyButton",
|
|
38
|
+
"prop": "variant",
|
|
39
|
+
"values": [
|
|
40
|
+
"primary",
|
|
41
|
+
"secondary",
|
|
42
|
+
"subtle",
|
|
43
|
+
"danger",
|
|
44
|
+
"outline",
|
|
45
|
+
"link"
|
|
46
|
+
],
|
|
47
|
+
"default": "secondary"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"variants": [
|
|
51
|
+
"primary",
|
|
52
|
+
"secondary",
|
|
53
|
+
"subtle",
|
|
54
|
+
"danger",
|
|
55
|
+
"outline",
|
|
56
|
+
"link"
|
|
57
|
+
],
|
|
58
|
+
"sizes": [
|
|
59
|
+
"sm",
|
|
60
|
+
"md",
|
|
61
|
+
"lg",
|
|
62
|
+
"icon"
|
|
63
|
+
],
|
|
64
|
+
"files": [
|
|
65
|
+
{
|
|
66
|
+
"path": "packages/components/_internal/clipboard.ts",
|
|
67
|
+
"target": "{{aliases.components}}/_internal/clipboard.ts",
|
|
68
|
+
"type": "registry:ui",
|
|
69
|
+
"integrity": "sha256-0y5p88CTXWuOrUYlC5J4XP1VCrOwkx9jYWVHmD2LouQ="
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"path": "packages/components/_internal/react.ts",
|
|
73
|
+
"target": "{{aliases.components}}/_internal/react.ts",
|
|
74
|
+
"type": "registry:ui",
|
|
75
|
+
"integrity": "sha256-SH63/haiB4Q93uFjMIMe5xZbaxENu3VI+GcidV8HELM="
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"path": "packages/components/copy-button/copy-button.tsx",
|
|
79
|
+
"target": "{{aliases.components}}/copy-button.tsx",
|
|
80
|
+
"type": "registry:ui",
|
|
81
|
+
"integrity": "sha256-6fvfifoiDOtW+WxKWon4SGEn3THtERuCX2hqGSZDtVM="
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"themeContract": {
|
|
85
|
+
"attribute": "data-theme",
|
|
86
|
+
"modeAttribute": "data-mode",
|
|
87
|
+
"compatibleThemes": [
|
|
88
|
+
"graphite",
|
|
89
|
+
"atelier",
|
|
90
|
+
"machined",
|
|
91
|
+
"aerogel",
|
|
92
|
+
"nordic"
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schema/registry-item.schema.json",
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"name": "password-input",
|
|
5
|
+
"title": "Password Input",
|
|
6
|
+
"description": "A password field with a show and hide toggle and, when choosing a new password, a meter that says how strong it is and what would make it stronger",
|
|
7
|
+
"category": "forms",
|
|
8
|
+
"tags": [
|
|
9
|
+
"password",
|
|
10
|
+
"auth",
|
|
11
|
+
"form"
|
|
12
|
+
],
|
|
13
|
+
"type": "registry:ui",
|
|
14
|
+
"usage": {
|
|
15
|
+
"importPath": "{{aliases.components}}/password-input",
|
|
16
|
+
"exportName": "PasswordInput",
|
|
17
|
+
"isDefault": false
|
|
18
|
+
},
|
|
19
|
+
"registryDependencies": [
|
|
20
|
+
"input"
|
|
21
|
+
],
|
|
22
|
+
"engineDependencies": {
|
|
23
|
+
"@mlola-ui/behavior": "^1.0.8",
|
|
24
|
+
"@mlola-ui/icons": "^1.0.8"
|
|
25
|
+
},
|
|
26
|
+
"options": [
|
|
27
|
+
{
|
|
28
|
+
"component": "PasswordInput",
|
|
29
|
+
"prop": "size",
|
|
30
|
+
"values": [
|
|
31
|
+
"sm",
|
|
32
|
+
"md",
|
|
33
|
+
"lg"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"component": "PasswordInput",
|
|
38
|
+
"prop": "variant",
|
|
39
|
+
"values": [
|
|
40
|
+
"default",
|
|
41
|
+
"filled",
|
|
42
|
+
"subtle"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"variants": [
|
|
47
|
+
"default",
|
|
48
|
+
"filled",
|
|
49
|
+
"subtle"
|
|
50
|
+
],
|
|
51
|
+
"sizes": [
|
|
52
|
+
"sm",
|
|
53
|
+
"md",
|
|
54
|
+
"lg"
|
|
55
|
+
],
|
|
56
|
+
"files": [
|
|
57
|
+
{
|
|
58
|
+
"path": "packages/components/password-input/password-input.tsx",
|
|
59
|
+
"target": "{{aliases.components}}/password-input.tsx",
|
|
60
|
+
"type": "registry:ui",
|
|
61
|
+
"integrity": "sha256-oY//vTILhUicJyfAWZrSUTl8WWsf4P2S1Bh0x4OxwLs="
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"themeContract": {
|
|
65
|
+
"attribute": "data-theme",
|
|
66
|
+
"modeAttribute": "data-mode",
|
|
67
|
+
"compatibleThemes": [
|
|
68
|
+
"graphite",
|
|
69
|
+
"atelier",
|
|
70
|
+
"machined",
|
|
71
|
+
"aerogel",
|
|
72
|
+
"nordic"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../schema/registry-item.schema.json",
|
|
3
|
+
"schemaVersion": 2,
|
|
4
|
+
"name": "spinner",
|
|
5
|
+
"title": "Spinner",
|
|
6
|
+
"description": "Work in progress whose end is unknown, drawn in the color of the text around it and named for screen readers when it stands alone",
|
|
7
|
+
"category": "feedback",
|
|
8
|
+
"tags": [
|
|
9
|
+
"loading",
|
|
10
|
+
"busy",
|
|
11
|
+
"status"
|
|
12
|
+
],
|
|
13
|
+
"type": "registry:ui",
|
|
14
|
+
"usage": {
|
|
15
|
+
"importPath": "{{aliases.components}}/spinner",
|
|
16
|
+
"exportName": "Spinner",
|
|
17
|
+
"isDefault": false
|
|
18
|
+
},
|
|
19
|
+
"registryDependencies": [],
|
|
20
|
+
"engineDependencies": {},
|
|
21
|
+
"options": [
|
|
22
|
+
{
|
|
23
|
+
"component": "Spinner",
|
|
24
|
+
"prop": "size",
|
|
25
|
+
"values": [
|
|
26
|
+
"sm",
|
|
27
|
+
"md",
|
|
28
|
+
"lg"
|
|
29
|
+
],
|
|
30
|
+
"default": "md"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"variants": [],
|
|
34
|
+
"sizes": [
|
|
35
|
+
"sm",
|
|
36
|
+
"md",
|
|
37
|
+
"lg"
|
|
38
|
+
],
|
|
39
|
+
"files": [
|
|
40
|
+
{
|
|
41
|
+
"path": "packages/components/_internal/react.ts",
|
|
42
|
+
"target": "{{aliases.components}}/_internal/react.ts",
|
|
43
|
+
"type": "registry:ui",
|
|
44
|
+
"integrity": "sha256-SH63/haiB4Q93uFjMIMe5xZbaxENu3VI+GcidV8HELM="
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"path": "packages/components/spinner/spinner.tsx",
|
|
48
|
+
"target": "{{aliases.components}}/spinner.tsx",
|
|
49
|
+
"type": "registry:ui",
|
|
50
|
+
"integrity": "sha256-FZQmcGkQ8N0485n9bYsLTpTnolbM729YKjkQrXriz9A="
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"themeContract": {
|
|
54
|
+
"attribute": "data-theme",
|
|
55
|
+
"modeAttribute": "data-mode",
|
|
56
|
+
"compatibleThemes": [
|
|
57
|
+
"graphite",
|
|
58
|
+
"atelier",
|
|
59
|
+
"machined",
|
|
60
|
+
"aerogel",
|
|
61
|
+
"nordic"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
"exportName": "Toaster",
|
|
17
17
|
"isDefault": false
|
|
18
18
|
},
|
|
19
|
-
"registryDependencies": [
|
|
19
|
+
"registryDependencies": [
|
|
20
|
+
"spinner"
|
|
21
|
+
],
|
|
20
22
|
"engineDependencies": {
|
|
21
|
-
"@mlola-ui/icons": "^1.0.
|
|
23
|
+
"@mlola-ui/icons": "^1.0.8"
|
|
22
24
|
},
|
|
23
25
|
"options": [
|
|
24
26
|
{
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
"path": "packages/components/toast/toast.tsx",
|
|
47
49
|
"target": "{{aliases.components}}/toast.tsx",
|
|
48
50
|
"type": "registry:ui",
|
|
49
|
-
"integrity": "sha256-
|
|
51
|
+
"integrity": "sha256-f0DwN0XuV46fSsQf3i9RJcfQbAs2mSz7A8UJoIIICHg="
|
|
50
52
|
}
|
|
51
53
|
],
|
|
52
54
|
"themeContract": {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
/** The shared request words: at rest, then the outcome of the last copy. */
|
|
6
|
+
export type CopyState = "idle" | "success" | "error";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Copy text to the clipboard and hold the outcome for a moment, so a control
|
|
10
|
+
* can confirm it. A refusal (no permission, an insecure page) is an outcome
|
|
11
|
+
* too: it is reported, never swallowed, so the person can copy by hand.
|
|
12
|
+
*/
|
|
13
|
+
export function useCopy(hold = 1600) {
|
|
14
|
+
const [state, setState] = React.useState<CopyState>("idle");
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if (state === "idle") return;
|
|
17
|
+
const timer = window.setTimeout(() => setState("idle"), hold);
|
|
18
|
+
return () => window.clearTimeout(timer);
|
|
19
|
+
}, [state, hold]);
|
|
20
|
+
const copy = React.useCallback(async (text: string) => {
|
|
21
|
+
try {
|
|
22
|
+
await navigator.clipboard.writeText(text);
|
|
23
|
+
setState("success");
|
|
24
|
+
return true;
|
|
25
|
+
} catch {
|
|
26
|
+
setState("error");
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
return { state, copy };
|
|
31
|
+
}
|
|
@@ -133,11 +133,11 @@ export function ActivityHeatmap({
|
|
|
133
133
|
</div>
|
|
134
134
|
{Array.from({ length: 7 }, (_, day) => {
|
|
135
135
|
const name = dayName.format(new Date(`${calendar.weeks[0][day].date}T00:00:00Z`));
|
|
136
|
-
const
|
|
136
|
+
const labeled = [1, 3, 5].includes(new Date(`${calendar.weeks[0][day].date}T00:00:00Z`).getUTCDay());
|
|
137
137
|
return (
|
|
138
138
|
<div key={day} role="row" className="ml-heatmap-row">
|
|
139
139
|
<span role="rowheader" className="ml-heatmap-weekday" style={{ gridRow: day + 2 }}>
|
|
140
|
-
<span aria-hidden={!
|
|
140
|
+
<span aria-hidden={!labeled || undefined} data-hidden={!labeled || undefined}>
|
|
141
141
|
{name}
|
|
142
142
|
</span>
|
|
143
143
|
</span>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Magnetic } from "@mlola-ui/motion";
|
|
5
5
|
import { cx } from "../_internal/react";
|
|
6
|
+
import { Spinner } from "../spinner/spinner";
|
|
6
7
|
|
|
7
8
|
export type ButtonVariant = "primary" | "secondary" | "subtle" | "danger" | "outline" | "link";
|
|
8
9
|
type ButtonSize = "sm" | "md" | "lg" | "icon";
|
|
@@ -30,7 +31,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
30
31
|
className={cx("ml-button", className)}
|
|
31
32
|
{...props}
|
|
32
33
|
>
|
|
33
|
-
{loading ? <
|
|
34
|
+
{loading ? <Spinner size="sm" /> : null}
|
|
34
35
|
<span className="ml-button-label">{children}</span>
|
|
35
36
|
</button>
|
|
36
37
|
);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { IconCheck, IconCopy } from "@mlola-ui/icons";
|
|
5
|
+
import { Button, type ButtonProps } from "../button/button";
|
|
6
|
+
import { useCopy } from "../_internal/clipboard";
|
|
7
|
+
import { cx } from "../_internal/react";
|
|
8
|
+
|
|
9
|
+
export interface CopyButtonProps extends Omit<ButtonProps, "value" | "onClick" | "loading"> {
|
|
10
|
+
/** The text to copy, or a function that returns it at the moment of copying. */
|
|
11
|
+
value: string | (() => string);
|
|
12
|
+
/** The action, spoken on an icon-only button and shown as its tooltip: "Copy link". */
|
|
13
|
+
label?: string;
|
|
14
|
+
/** Announced, and shown on a text button, once the copy succeeds. */
|
|
15
|
+
copiedLabel?: string;
|
|
16
|
+
/** Announced when the browser refuses (no permission, an insecure page). */
|
|
17
|
+
errorLabel?: string;
|
|
18
|
+
/** After each attempt: true when the text reached the clipboard. */
|
|
19
|
+
onCopied?: (copied: boolean) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Copies a value and confirms it: the icon turns to a check for a moment and
|
|
24
|
+
* a screen reader hears "Copied". Without children it is an icon button;
|
|
25
|
+
* with children it keeps its text and shows the confirmation in place.
|
|
26
|
+
*/
|
|
27
|
+
const CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(
|
|
28
|
+
({ value, label = "Copy", copiedLabel = "Copied", errorLabel = "Could not copy; select the text and copy it by hand", onCopied, variant = "secondary", size, className, children, ...props }, ref) => {
|
|
29
|
+
const { state, copy } = useCopy();
|
|
30
|
+
const iconOnly = children === undefined || children === null;
|
|
31
|
+
const icon = state === "success" ? <IconCheck aria-hidden="true" size="1em" animate /> : <IconCopy aria-hidden="true" size="1em" />;
|
|
32
|
+
return (
|
|
33
|
+
<Button
|
|
34
|
+
ref={ref}
|
|
35
|
+
variant={variant}
|
|
36
|
+
size={size ?? (iconOnly ? "icon" : "md")}
|
|
37
|
+
aria-label={iconOnly ? label : undefined}
|
|
38
|
+
title={iconOnly ? label : undefined}
|
|
39
|
+
className={cx("ml-copy-button", className)}
|
|
40
|
+
data-status={state === "idle" ? undefined : state}
|
|
41
|
+
onClick={async () => {
|
|
42
|
+
const copied = await copy(typeof value === "function" ? value() : value);
|
|
43
|
+
onCopied?.(copied);
|
|
44
|
+
}}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{icon}
|
|
48
|
+
{iconOnly ? null : state === "success" ? copiedLabel : children}
|
|
49
|
+
{/* The outcome is spoken where a changing label would not be. */}
|
|
50
|
+
<span className="ml-visually-hidden" role="status">
|
|
51
|
+
{state === "success" ? copiedLabel : state === "error" ? errorLabel : ""}
|
|
52
|
+
</span>
|
|
53
|
+
</Button>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
CopyButton.displayName = "CopyButton";
|
|
58
|
+
|
|
59
|
+
export { CopyButton };
|