@abuzareal/contour-ui 0.6.2 → 0.6.3

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/COMBOBOX.md +1 -1
  3. package/COMPONENT-GUIDES.md +3534 -0
  4. package/COMPOSITION.md +1 -1
  5. package/CONTRIBUTING.md +4 -0
  6. package/DOCS.md +61 -61
  7. package/FORMS.md +1 -1
  8. package/GETTING-STARTED.md +3 -3
  9. package/HELPERS.md +1 -1
  10. package/HOOKS.md +1 -1
  11. package/MANUAL-QA.md +1 -1
  12. package/MIGRATION.md +1 -1
  13. package/MODELS.md +1 -1
  14. package/OVERLAYS.md +1 -1
  15. package/PROP-REFERENCE.md +4 -4
  16. package/PUBLIC-API.md +166 -166
  17. package/README.md +5 -3
  18. package/RECIPES.md +1 -1
  19. package/REFERENCE.md +55 -55
  20. package/STYLING.md +1 -1
  21. package/SUPPORT.md +1 -1
  22. package/THREE.md +1 -1
  23. package/docs/component-guides.json +2217 -0
  24. package/docs/prop-metadata.json +5734 -751
  25. package/examples/catalogue/buttons.tsx +27 -0
  26. package/examples/catalogue/choices.tsx +51 -0
  27. package/examples/catalogue/combobox.tsx +119 -0
  28. package/examples/catalogue/content.tsx +108 -0
  29. package/examples/catalogue/data-table.tsx +18 -0
  30. package/examples/catalogue/dialogs.tsx +20 -0
  31. package/examples/catalogue/feedback.tsx +24 -0
  32. package/examples/catalogue/field.tsx +18 -0
  33. package/examples/catalogue/floating.tsx +41 -0
  34. package/examples/catalogue/identity.tsx +35 -0
  35. package/examples/catalogue/metadata.tsx +17 -0
  36. package/examples/catalogue/motion.tsx +47 -0
  37. package/examples/catalogue/navigation.tsx +43 -0
  38. package/examples/catalogue/scene-boundary.tsx +37 -0
  39. package/examples/catalogue/text-inputs.tsx +60 -0
  40. package/examples/catalogue/theme-copy.tsx +32 -0
  41. package/examples/catalogue/toasts.tsx +21 -0
  42. package/examples/reproduction/package.json +1 -1
  43. package/examples/starters/next/package.json +1 -1
  44. package/examples/starters/vite/package.json +1 -1
  45. package/package.json +5 -2
@@ -0,0 +1,27 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Button,
4
+ IconButton,
5
+ ExternalLink,
6
+ DepthButton,
7
+ } from "@abuzareal/contour-ui";
8
+
9
+ export default function Actions() {
10
+ const [count, setCount] = useState(0);
11
+ return (
12
+ <>
13
+ <Button onClick={() => setCount((value) => value + 1)}>
14
+ Count {count}
15
+ </Button>
16
+ <IconButton label="Reset count" onClick={() => setCount(0)}>
17
+ <span aria-hidden="true">↺</span>
18
+ </IconButton>
19
+ <ExternalLink href="https://www.npmjs.com/package/@abuzareal/contour-ui">
20
+ Package on npm
21
+ </ExternalLink>
22
+ <DepthButton onClick={() => setCount((value) => value + 1)}>
23
+ Add one
24
+ </DepthButton>
25
+ </>
26
+ );
27
+ }
@@ -0,0 +1,51 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Checkbox,
4
+ RadioGroup,
5
+ Switch,
6
+ Slider,
7
+ SegmentedControl,
8
+ } from "@abuzareal/contour-ui";
9
+
10
+ type View = "overview" | "details";
11
+ const options: { value: View; label: string }[] = [
12
+ { value: "overview", label: "Overview" },
13
+ { value: "details", label: "Details" },
14
+ ];
15
+ export default function Preferences() {
16
+ const [view, setView] = useState<View>("overview");
17
+ const [checked, setChecked] = useState(false);
18
+ const [volume, setVolume] = useState(40);
19
+ return (
20
+ <>
21
+ <p>
22
+ Local selection only; this does not configure a global density mode.
23
+ </p>
24
+ <Checkbox
25
+ label="Include archived items"
26
+ checked={checked}
27
+ onChange={(event) => setChecked(event.target.checked)}
28
+ />
29
+ <RadioGroup
30
+ legend="Example view"
31
+ options={options}
32
+ value={view}
33
+ onChange={setView}
34
+ />
35
+ <SegmentedControl
36
+ label="Preview view"
37
+ options={options}
38
+ value={view}
39
+ onChange={setView}
40
+ />
41
+ <Switch label="Example setting" checked={checked} onChange={setChecked} />
42
+ <Slider
43
+ label="Example volume"
44
+ value={volume}
45
+ onChange={setVolume}
46
+ min={0}
47
+ max={100}
48
+ />
49
+ </>
50
+ );
51
+ }
@@ -0,0 +1,119 @@
1
+ import { useRef, useState } from "react";
2
+ import {
3
+ Button,
4
+ Combobox,
5
+ Dialog,
6
+ Popover,
7
+ Table,
8
+ TextField,
9
+ ThemeScope,
10
+ } from "@abuzareal/contour-ui";
11
+ const projects = [
12
+ { value: "atlas", label: "Atlas" },
13
+ {
14
+ value: "field",
15
+ label:
16
+ "Field notes — a long project label that remains readable on a narrow screen",
17
+ },
18
+ { value: "closed", label: "Closed project", disabled: true },
19
+ ];
20
+ export default function SelectionExample() {
21
+ const input = useRef<HTMLInputElement>(null);
22
+ const [values, setValues] = useState("");
23
+ const [open, setOpen] = useState(false);
24
+ const [theme, setTheme] = useState<"light" | "dark">("light");
25
+ return (
26
+ <div className="demo-stack">
27
+ <p>Local form inspection. No project or settings are saved.</p>
28
+ <form
29
+ onSubmit={(event) => {
30
+ event.preventDefault();
31
+ setValues(
32
+ JSON.stringify(
33
+ Object.fromEntries(new FormData(event.currentTarget)),
34
+ ),
35
+ );
36
+ }}
37
+ onReset={() => setValues("")}
38
+ >
39
+ <Combobox
40
+ ref={input}
41
+ label="Project"
42
+ name="project"
43
+ options={projects}
44
+ defaultValue="atlas"
45
+ required
46
+ hint="Choose an allowed project; typed text is not a selection."
47
+ />
48
+ <TextField label="Note after project" name="note" />
49
+ <div className="demo-row">
50
+ <Button type="submit">Inspect project values</Button>
51
+ <Button type="reset" variant="secondary">
52
+ Reset project
53
+ </Button>
54
+ <Button onClick={() => input.current?.focus()}>Focus project</Button>
55
+ </div>
56
+ </form>
57
+ <output aria-label="Project form values">{values}</output>
58
+ <div className="demo-row">
59
+ <Button
60
+ onClick={(event) => {
61
+ event.currentTarget.focus();
62
+ setOpen(true);
63
+ }}
64
+ >
65
+ Open picker dialog
66
+ </Button>
67
+ <Popover trigger="Open parent layer" title="Nested layers">
68
+ <Popover trigger="Open child layer" title="Child layer">
69
+ <p>First Escape closes this child; second closes its parent.</p>
70
+ <Combobox label="Nested project" options={projects} />
71
+ </Popover>
72
+ </Popover>
73
+ </div>
74
+ <Dialog
75
+ title="Choose a local project"
76
+ open={open}
77
+ onClose={() => setOpen(false)}
78
+ >
79
+ <Combobox
80
+ label="Dialog project"
81
+ options={projects}
82
+ defaultValue="atlas"
83
+ />
84
+ <Button onClick={() => setOpen(false)}>Keep local selection</Button>
85
+ </Dialog>
86
+ <ThemeScope
87
+ theme={theme}
88
+ onThemeChange={setTheme}
89
+ style={{ padding: 16 }}
90
+ >
91
+ <p>Independent embedded theme: {theme}.</p>
92
+ <Button
93
+ onClick={() =>
94
+ setTheme((value) => (value === "light" ? "dark" : "light"))
95
+ }
96
+ >
97
+ Toggle embedded theme
98
+ </Button>
99
+ <Combobox label="Embedded project" options={projects} />
100
+ <Table caption="Local project identity" showCaption>
101
+ <thead>
102
+ <tr>
103
+ <th scope="col">ID</th>
104
+ <th scope="col">Label</th>
105
+ </tr>
106
+ </thead>
107
+ <tbody>
108
+ {projects.map((project) => (
109
+ <tr key={project.value}>
110
+ <th scope="row">{project.value}</th>
111
+ <td>{project.label}</td>
112
+ </tr>
113
+ ))}
114
+ </tbody>
115
+ </Table>
116
+ </ThemeScope>
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,108 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Card,
4
+ Disclosure,
5
+ Divider,
6
+ Timeline,
7
+ EmptyState,
8
+ Button,
9
+ } from "@abuzareal/contour-ui";
10
+
11
+ export default function Content() {
12
+ const [situation, setSituation] = useState<
13
+ "empty" | "filtered" | "error" | "created"
14
+ >("empty");
15
+ const [status, setStatus] = useState("");
16
+ const recovery = () => {
17
+ setSituation(situation === "empty" ? "created" : "empty");
18
+ setStatus(
19
+ situation === "error"
20
+ ? "Local retry completed with no saved projects. No service was contacted."
21
+ : situation === "filtered"
22
+ ? "Local filter cleared."
23
+ : "One local example project added. Nothing was saved.",
24
+ );
25
+ };
26
+ return (
27
+ <>
28
+ <Card
29
+ title="Project notes"
30
+ eyebrow="Example"
31
+ footer={<a href="/docs/getting-started">Read setup</a>}
32
+ >
33
+ <p>Keep the action separate from the card container.</p>
34
+ </Card>
35
+ <Disclosure className="project-notes" summary="Technical details">
36
+ <p>Your application's notes go here.</p>
37
+ </Disclosure>
38
+ <Divider label="History" />
39
+ <Timeline
40
+ entries={[
41
+ {
42
+ date: "Draft",
43
+ title: "Example milestone",
44
+ body: "Illustrative content.",
45
+ },
46
+ ]}
47
+ />
48
+ <section aria-label="Empty-state situations">
49
+ <p>Choose an illustrative local outcome. No request is sent.</p>
50
+ <Button
51
+ onClick={() => {
52
+ setSituation("empty");
53
+ setStatus("");
54
+ }}
55
+ >
56
+ Show no saved data
57
+ </Button>
58
+ <Button
59
+ onClick={() => {
60
+ setSituation("filtered");
61
+ setStatus("");
62
+ }}
63
+ >
64
+ Show no matches
65
+ </Button>
66
+ <Button
67
+ onClick={() => {
68
+ setSituation("error");
69
+ setStatus("");
70
+ }}
71
+ >
72
+ Simulate service failure
73
+ </Button>
74
+ {situation === "created" ? (
75
+ <p>Local example project.</p>
76
+ ) : (
77
+ <EmptyState
78
+ icon={<span aria-hidden="true">○</span>}
79
+ title={
80
+ situation === "error"
81
+ ? "Projects unavailable"
82
+ : situation === "filtered"
83
+ ? "No matching projects"
84
+ : "No saved projects"
85
+ }
86
+ message={
87
+ situation === "error"
88
+ ? "The illustrative request failed. This is not an empty successful result."
89
+ : situation === "filtered"
90
+ ? "Try clearing your filters."
91
+ : "Add a local example to see it here."
92
+ }
93
+ action={
94
+ <Button onClick={recovery}>
95
+ {situation === "error"
96
+ ? "Retry local example"
97
+ : situation === "filtered"
98
+ ? "Clear local filters"
99
+ : "Add a local project"}
100
+ </Button>
101
+ }
102
+ />
103
+ )}
104
+ <p role="status">{status}</p>
105
+ </section>
106
+ </>
107
+ );
108
+ }
@@ -0,0 +1,18 @@
1
+ import { DataTable, type TableColumn } from "@abuzareal/contour-ui";
2
+
3
+ type Project = { id: string; name: string; examples: number };
4
+ const rows: Project[] = [{ id: "demo", name: "Example project", examples: 3 }];
5
+ const columns: TableColumn<Project>[] = [
6
+ { key: "name", header: "Project", sortable: true },
7
+ { key: "examples", header: "Examples", align: "end", sortable: true },
8
+ ];
9
+ export default function ProjectTable() {
10
+ return (
11
+ <DataTable
12
+ caption="Illustrative projects"
13
+ columns={columns}
14
+ rows={rows}
15
+ rowKey="id"
16
+ />
17
+ );
18
+ }
@@ -0,0 +1,20 @@
1
+ import { useState } from "react";
2
+ import { Button, Dialog } from "@abuzareal/contour-ui";
3
+
4
+ export default function DetailsDialog() {
5
+ const [open, setOpen] = useState(false);
6
+ return (
7
+ <>
8
+ <Button onClick={() => setOpen(true)}>Open details</Button>
9
+ <Dialog
10
+ open={open}
11
+ onClose={() => setOpen(false)}
12
+ title="Project details"
13
+ description="A local example."
14
+ footer={<Button onClick={() => setOpen(false)}>Close</Button>}
15
+ >
16
+ <p>Your application's details go here.</p>
17
+ </Dialog>
18
+ </>
19
+ );
20
+ }
@@ -0,0 +1,24 @@
1
+ import { useState } from "react";
2
+ import { Alert, ProgressBar, Spinner, Skeleton } from "@abuzareal/contour-ui";
3
+
4
+ export default function Feedback() {
5
+ const [visible, setVisible] = useState(true);
6
+ return (
7
+ <>
8
+ {visible && (
9
+ <Alert
10
+ title="Example warning"
11
+ tone="warning"
12
+ onDismiss={() => setVisible(false)}
13
+ >
14
+ This is a local demonstration.
15
+ </Alert>
16
+ )}
17
+ <div aria-busy="true">
18
+ <Spinner label="Loading example" />
19
+ <ProgressBar label="Example upload" value={35} />
20
+ <Skeleton width="12rem" height="1rem" />
21
+ </div>
22
+ </>
23
+ );
24
+ }
@@ -0,0 +1,18 @@
1
+ import { useId } from "react";
2
+ import { Field, fieldDescribedBy } from "@abuzareal/contour-ui";
3
+
4
+ export default function DateField() {
5
+ const id = useId();
6
+ const hint = "Choose a delivery date.";
7
+ return (
8
+ <Field id={id} label="Delivery date" hint={hint} required>
9
+ <input
10
+ id={id}
11
+ type="date"
12
+ name="delivery"
13
+ required
14
+ aria-describedby={fieldDescribedBy(id, hint)}
15
+ />
16
+ </Field>
17
+ );
18
+ }
@@ -0,0 +1,41 @@
1
+ import { useState } from "react";
2
+ import { DropdownMenu, Popover, Tooltip, Button } from "@abuzareal/contour-ui";
3
+
4
+ export default function FloatingContent() {
5
+ const [message, setMessage] = useState("");
6
+ const [menuOpen, setMenuOpen] = useState(false);
7
+ return (
8
+ <>
9
+ <DropdownMenu
10
+ label="Example actions"
11
+ open={menuOpen}
12
+ onOpenChange={setMenuOpen}
13
+ items={[
14
+ {
15
+ id: "select",
16
+ label: "Select example",
17
+ onSelect: () => setMessage("Example selected locally."),
18
+ },
19
+ {
20
+ id: "unavailable",
21
+ label: "Unavailable example",
22
+ disabled: true,
23
+ onSelect: () => setMessage("Unavailable action."),
24
+ },
25
+ {
26
+ id: "details",
27
+ label: "View details",
28
+ onSelect: () => setMessage("Details selected locally."),
29
+ },
30
+ ]}
31
+ />
32
+ <Popover trigger="About this example" title="Details">
33
+ <p>No network action occurs.</p>
34
+ </Popover>
35
+ <Tooltip content="Displays a local message">
36
+ <Button onClick={() => setMessage("Hello")}>Say hello</Button>
37
+ </Tooltip>
38
+ <p role="status">{message}</p>
39
+ </>
40
+ );
41
+ }
@@ -0,0 +1,35 @@
1
+ import {
2
+ BrandMark,
3
+ Reveal,
4
+ SectionLabel,
5
+ RevealHeading,
6
+ GitHubIcon,
7
+ LinkedInIcon,
8
+ ExternalLink,
9
+ } from "@abuzareal/contour-ui";
10
+
11
+ export default function Identity() {
12
+ return (
13
+ <>
14
+ <BrandMark label="Legacy aa identity example" href="#work-title" />
15
+ <Reveal>
16
+ <SectionLabel
17
+ section={{
18
+ id: "work",
19
+ number: 1,
20
+ label: "Work",
21
+ title: "Selected work",
22
+ }}
23
+ aside="Example"
24
+ />
25
+ <RevealHeading id="work-title" lines={[{ text: "Selected work" }]} />
26
+ </Reveal>
27
+ <ExternalLink href="https://github.com" aria-label="GitHub">
28
+ <GitHubIcon />
29
+ </ExternalLink>
30
+ <ExternalLink href="https://www.linkedin.com" aria-label="LinkedIn">
31
+ <LinkedInIcon />
32
+ </ExternalLink>
33
+ </>
34
+ );
35
+ }
@@ -0,0 +1,17 @@
1
+ import { useState } from "react";
2
+ import { Badge, Tag, Avatar, Stat, Kbd } from "@abuzareal/contour-ui";
3
+
4
+ export default function Metadata() {
5
+ const [showTag, setShowTag] = useState(true);
6
+ return (
7
+ <>
8
+ <Badge tone="info">Example status</Badge>
9
+ {showTag && <Tag onRemove={() => setShowTag(false)}>React</Tag>}
10
+ <Avatar name="Alex Chen" />
11
+ <Stat value={12} label="Example records" />
12
+ <p>
13
+ Use <Kbd>Tab</Kbd> to move between controls.
14
+ </p>
15
+ </>
16
+ );
17
+ }
@@ -0,0 +1,47 @@
1
+ import { useState } from "react";
2
+ import {
3
+ Reveal,
4
+ Magnetic,
5
+ CountUp,
6
+ Marquee,
7
+ ScrollProgress,
8
+ TiltCard,
9
+ FlipCard,
10
+ Button,
11
+ } from "@abuzareal/contour-ui";
12
+
13
+ export default function MotionExamples() {
14
+ const [paused, setPaused] = useState(false);
15
+ return (
16
+ <div data-motion={paused ? "off" : undefined}>
17
+ <Button
18
+ aria-pressed={paused}
19
+ onClick={() => setPaused((value) => !value)}
20
+ >
21
+ Pause examples
22
+ </Button>
23
+ <ScrollProgress />
24
+ <Reveal>
25
+ <p>Content entering view.</p>
26
+ </Reveal>
27
+ <Magnetic>
28
+ <Button>Pointer example</Button>
29
+ </Magnetic>
30
+ <p>
31
+ Example records: <CountUp to={12} />
32
+ </p>
33
+ <Marquee
34
+ label="Example technologies"
35
+ items={["React", "TypeScript", "CSS"]}
36
+ />
37
+ <TiltCard>
38
+ <p>CSS depth example.</p>
39
+ </TiltCard>
40
+ <FlipCard
41
+ label="Show other side"
42
+ front={<p>Front</p>}
43
+ back={<p>Back</p>}
44
+ />
45
+ </div>
46
+ );
47
+ }
@@ -0,0 +1,43 @@
1
+ import { useState } from "react";
2
+ import { Tabs, Breadcrumbs, Pagination, Stepper } from "@abuzareal/contour-ui";
3
+
4
+ export default function Navigation() {
5
+ const [page, setPage] = useState(1);
6
+ const [activeTabId, setActiveTabId] = useState("overview");
7
+ return (
8
+ <>
9
+ <Breadcrumbs
10
+ items={[{ label: "Home", href: "/" }, { label: "Projects" }]}
11
+ />
12
+ <Tabs
13
+ label="Project information"
14
+ activeTabId={activeTabId}
15
+ onTabChange={setActiveTabId}
16
+ tabs={[
17
+ {
18
+ id: "overview",
19
+ label: "Overview",
20
+ content: <p>Project overview.</p>,
21
+ },
22
+ { id: "details", label: "Details", content: <p>Project details.</p> },
23
+ ]}
24
+ />
25
+ <p role="status">Selected tab: {activeTabId}.</p>
26
+ <Pagination
27
+ page={page}
28
+ pageCount={5}
29
+ onChange={setPage}
30
+ label="Result pages"
31
+ />
32
+ <p>Selected page: {page}. Supply your own results.</p>
33
+ <Stepper
34
+ current={1}
35
+ steps={[
36
+ { label: "Details" },
37
+ { label: "Review" },
38
+ { label: "Complete" },
39
+ ]}
40
+ />
41
+ </>
42
+ );
43
+ }
@@ -0,0 +1,37 @@
1
+ import { useState } from "react";
2
+ import { Button, SceneBoundary } from "@abuzareal/contour-ui";
3
+
4
+ function DemonstrationChild({ fail }: { fail: boolean }) {
5
+ if (fail) throw Error("Intentional local SceneBoundary demonstration");
6
+ return (
7
+ <p>The optional child rendered. No WebGL dependency is loaded here.</p>
8
+ );
9
+ }
10
+
11
+ export default function BoundaryExample() {
12
+ const [fail, setFail] = useState(false);
13
+ const [attempt, setAttempt] = useState(0);
14
+ return (
15
+ <section aria-label="Scene boundary example">
16
+ <p>
17
+ A static fallback stays outside the boundary. A caught render failure
18
+ hides only its child. The expected demonstration error may be logged by
19
+ React; it does not represent a renderer or service failure.
20
+ </p>
21
+ <SceneBoundary key={attempt}>
22
+ <DemonstrationChild fail={fail} />
23
+ </SceneBoundary>
24
+ <Button onClick={() => setFail(true)}>
25
+ Simulate child render failure
26
+ </Button>
27
+ <Button
28
+ onClick={() => {
29
+ setFail(false);
30
+ setAttempt((value) => value + 1);
31
+ }}
32
+ >
33
+ Remount demonstration
34
+ </Button>
35
+ </section>
36
+ );
37
+ }
@@ -0,0 +1,60 @@
1
+ import { useRef, useState } from "react";
2
+ import {
3
+ TextField,
4
+ TextArea,
5
+ SelectField,
6
+ Button,
7
+ } from "@abuzareal/contour-ui";
8
+
9
+ export default function ContactFields() {
10
+ const emailInput = useRef<HTMLInputElement>(null);
11
+ const [email, setEmail] = useState("");
12
+ const [message, setMessage] = useState("");
13
+ const [topic, setTopic] = useState("feedback");
14
+ const [submitted, setSubmitted] = useState(false);
15
+ return (
16
+ <form
17
+ onSubmit={(event) => {
18
+ event.preventDefault();
19
+ setSubmitted(true);
20
+ }}
21
+ >
22
+ <TextField
23
+ ref={emailInput}
24
+ label="Email"
25
+ name="email"
26
+ type="email"
27
+ required
28
+ value={email}
29
+ onChange={(event) => setEmail(event.target.value)}
30
+ hint="Use a working email address."
31
+ />
32
+ <TextArea
33
+ label="Message"
34
+ name="message"
35
+ required
36
+ autoResize
37
+ maxLength={500}
38
+ value={message}
39
+ onChange={(event) => setMessage(event.target.value)}
40
+ />
41
+ <SelectField
42
+ label="Topic"
43
+ name="topic"
44
+ value={topic}
45
+ options={[
46
+ { value: "feedback", label: "Feedback" },
47
+ { value: "question", label: "Question" },
48
+ ]}
49
+ onChange={(event) => setTopic(event.target.value)}
50
+ />
51
+ <Button type="submit">Validate locally</Button>
52
+ <Button type="button" onClick={() => emailInput.current?.focus()}>
53
+ Focus email
54
+ </Button>
55
+ {submitted && (
56
+ <p role="status">Fields validated locally. No message was sent.</p>
57
+ )}
58
+ </form>
59
+ );
60
+ }