@dazl/component-gallery 1.0.0 → 2.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/README.md CHANGED
@@ -65,13 +65,15 @@ import { Button } from './button';
65
65
  export default function ButtonExample() {
66
66
  return (
67
67
  <>
68
- <Section title="Sizes" layout="row">
68
+ <Section layout="row">
69
+ <Section.Title>Sizes</Section.Title>
69
70
  <Button size="sm">Small</Button>
70
71
  <Button size="md">Medium</Button>
71
72
  <Button size="lg">Large</Button>
72
73
  </Section>
73
74
 
74
- <Section title="States" layout="column">
75
+ <Section layout="column">
76
+ <Section.Title>States</Section.Title>
75
77
  <Button disabled>Disabled</Button>
76
78
  <Button loading>Loading</Button>
77
79
  </Section>
@@ -1,7 +1,11 @@
1
1
  export interface SectionProps {
2
- title?: string;
3
2
  layout?: 'row' | 'column';
4
3
  children: React.ReactNode;
5
4
  }
6
- export declare const Section: ({ title, layout, children }: SectionProps) => import("react/jsx-runtime").JSX.Element;
5
+ export declare const Section: {
6
+ ({ layout, children }: SectionProps): import("react/jsx-runtime").JSX.Element;
7
+ Title: import("react").FC<{
8
+ children: React.ReactNode;
9
+ }>;
10
+ };
7
11
  //# sourceMappingURL=example.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../../src/example/example.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,eAAO,MAAM,OAAO,GAAI,6BAAwC,YAAY,4CAS3E,CAAC"}
1
+ {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../../src/example/example.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IACzB,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,eAAO,MAAM,OAAO;2BAAqC,YAAY;;kBAa5B,KAAK,CAAC,SAAS;;CAFvD,CAAC"}
package/dist/index.js CHANGED
@@ -1,13 +1,28 @@
1
- import { jsxs as c, jsx as t } from "react/jsx-runtime";
2
- const i = "_section_omose_1", l = "_sectionTitle_omose_9", m = "_sectionContentColumn_omose_18", C = "_sectionContentRow_omose_24", o = {
3
- section: i,
4
- sectionTitle: l,
5
- sectionContentColumn: m,
6
- sectionContentRow: C
7
- }, u = ({ title: n, layout: e = "column", children: s }) => /* @__PURE__ */ c("section", { className: o.section, children: [
8
- n ? /* @__PURE__ */ t("div", { className: o.sectionTitle, children: n }) : null,
9
- /* @__PURE__ */ t("div", { className: e === "column" ? o.sectionContentColumn : o.sectionContentRow, children: s })
10
- ] });
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { Children, isValidElement } from "react";
3
+ const section = "section";
4
+ const sectionTitle = "sectionTitle";
5
+ const sectionContentColumn = "sectionContentColumn";
6
+ const sectionContentRow = "sectionContentRow";
7
+ const styles = {
8
+ section,
9
+ sectionTitle,
10
+ sectionContentColumn,
11
+ sectionContentRow
12
+ };
13
+ const Section = ({ layout = "column", children }) => {
14
+ const { title, content } = extractTitle(children);
15
+ return /* @__PURE__ */ jsxs("section", { className: styles.section, children: [
16
+ title,
17
+ /* @__PURE__ */ jsx("div", { className: layout === "column" ? styles.sectionContentColumn : styles.sectionContentRow, children: content })
18
+ ] });
19
+ };
20
+ const SectionTitle = ({ children }) => /* @__PURE__ */ jsx("div", { className: styles.sectionTitle, children });
21
+ Section.Title = SectionTitle;
22
+ const extractTitle = (children) => {
23
+ const [title, ...content] = Children.toArray(children);
24
+ return isValidElement(title) && title.type === SectionTitle ? { title, content } : { title: null, content: children };
25
+ };
11
26
  export {
12
- u as Section
27
+ Section
13
28
  };
@@ -1,59 +1,92 @@
1
- import { jsxs as u, jsx as n } from "react/jsx-runtime";
2
- import { useCallback as f, memo as _, useState as h, lazy as b, Suspense as g, Component as v, useMemo as w, useEffect as p, useRef as E } from "react";
3
- import { z as s } from "zod";
4
- const S = "_root_segk4_1", k = {
5
- root: S
6
- }, y = () => f((e) => {
7
- if (!e) return;
8
- const r = (t) => {
9
- if (!(t.target instanceof Element)) return;
10
- const a = t.target.closest("a");
11
- if (!a) return;
12
- const o = a.getAttribute("href");
13
- o && (t.preventDefault(), C(o) && window.open(o, "_blank", "noopener,noreferrer"));
14
- };
15
- return e.addEventListener("click", r), () => {
16
- e.removeEventListener("click", r);
17
- };
18
- }, []), C = (e) => /^[a-z][a-z0-9+.-]*:/i.test(e), x = "_card_krcb2_1", N = "_cardHeader_krcb2_10", I = "_cardContent_krcb2_19", L = "_loadingState_krcb2_23", D = "_spinner_krcb2_30", H = "_errorState_krcb2_45", P = "_errorIcon_krcb2_56", z = "_errorMessage_krcb2_60", c = {
19
- card: x,
20
- cardHeader: N,
21
- cardContent: I,
22
- loadingState: L,
23
- spinner: D,
24
- errorState: H,
25
- errorIcon: P,
26
- errorMessage: z
27
- }, M = _(function({ example: r }) {
28
- const [t] = h(() => b(() => import(
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useCallback, memo, useState, lazy, Suspense, Component, useMemo, useEffect, useRef } from "react";
3
+ import { z } from "zod";
4
+ const componentGallery = "componentGallery";
5
+ const styles$1 = {
6
+ componentGallery
7
+ };
8
+ const useLinkBehaviorOverride = () => {
9
+ return useCallback((container) => {
10
+ if (!container) return;
11
+ const handleClick = (event) => {
12
+ if (!(event.target instanceof Element)) return;
13
+ const link = event.target.closest("a");
14
+ if (!link) return;
15
+ const href = link.getAttribute("href");
16
+ if (!href) return;
17
+ event.preventDefault();
18
+ if (isAbsoluteUrl(href)) {
19
+ window.open(href, "_blank", "noopener,noreferrer");
20
+ }
21
+ };
22
+ container.addEventListener("click", handleClick);
23
+ return () => {
24
+ container.removeEventListener("click", handleClick);
25
+ };
26
+ }, []);
27
+ };
28
+ const isAbsoluteUrl = (url) => /^[a-z][a-z0-9+.-]*:/i.test(url);
29
+ const exampleCard = "exampleCard";
30
+ const exampleCardHeader = "exampleCardHeader";
31
+ const exampleCardContent = "exampleCardContent";
32
+ const exampleCardLoading = "exampleCardLoading";
33
+ const exampleCardSpinner = "exampleCardSpinner";
34
+ const exampleCardError = "exampleCardError";
35
+ const exampleCardErrorIcon = "exampleCardErrorIcon";
36
+ const exampleCardErrorMessage = "exampleCardErrorMessage";
37
+ const styles = {
38
+ exampleCard,
39
+ exampleCardHeader,
40
+ exampleCardContent,
41
+ exampleCardLoading,
42
+ exampleCardSpinner,
43
+ exampleCardError,
44
+ exampleCardErrorIcon,
45
+ exampleCardErrorMessage
46
+ };
47
+ const ExampleCard = memo(function Example({ example }) {
48
+ const [Component2] = useState(() => lazy(() => import(
29
49
  /* @vite-ignore */
30
- `/${r.relativePath}`
31
- ))), a = y();
32
- return /* @__PURE__ */ u("div", { id: r.relativePath, className: c.card, children: [
33
- /* @__PURE__ */ n("h3", { className: c.cardHeader, children: r.displayName }),
34
- /* @__PURE__ */ n("div", { ref: a, className: c.cardContent, children: /* @__PURE__ */ n(R, { fallback: (o) => /* @__PURE__ */ n(F, { error: o }), children: /* @__PURE__ */ n(g, { fallback: /* @__PURE__ */ n(A, {}), children: /* @__PURE__ */ n(t, {}) }) }) })
50
+ `/${example.relativePath}`
51
+ )));
52
+ const contentRef = useLinkBehaviorOverride();
53
+ return /* @__PURE__ */ jsxs("div", { id: example.relativePath, className: styles.exampleCard, children: [
54
+ /* @__PURE__ */ jsx("h3", { className: styles.exampleCardHeader, children: example.displayName }),
55
+ /* @__PURE__ */ jsx(ErrorBoundary, { fallback: (error) => /* @__PURE__ */ jsx(ExampleCardError, { error }), children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ExampleCardLoading, {}), children: /* @__PURE__ */ jsx("div", { ref: contentRef, className: styles.exampleCardContent, children: /* @__PURE__ */ jsx(Component2, {}) }) }) })
56
+ ] });
57
+ });
58
+ const ExampleCardLoading = () => {
59
+ return /* @__PURE__ */ jsx("div", { className: styles.exampleCardLoading, children: /* @__PURE__ */ jsx("div", { className: styles.exampleCardSpinner }) });
60
+ };
61
+ const ExampleCardError = ({ error }) => {
62
+ return /* @__PURE__ */ jsxs("div", { className: styles.exampleCardError, children: [
63
+ /* @__PURE__ */ jsx("span", { className: styles.exampleCardErrorIcon, children: "⚠" }),
64
+ /* @__PURE__ */ jsx("span", { className: styles.exampleCardErrorMessage, children: error.message })
35
65
  ] });
36
- }), A = () => /* @__PURE__ */ n("div", { className: c.loadingState, children: /* @__PURE__ */ n("div", { className: c.spinner }) }), F = ({ error: e }) => /* @__PURE__ */ u("div", { className: c.errorState, children: [
37
- /* @__PURE__ */ n("span", { className: c.errorIcon, children: "⚠" }),
38
- /* @__PURE__ */ n("span", { className: c.errorMessage, children: e.message })
39
- ] });
40
- class R extends v {
41
- constructor(r) {
42
- super(r), this.state = { error: null };
66
+ };
67
+ class ErrorBoundary extends Component {
68
+ constructor(props) {
69
+ super(props);
70
+ this.state = { error: null };
43
71
  }
44
- static getDerivedStateFromError(r) {
45
- return { error: r };
72
+ static getDerivedStateFromError(error) {
73
+ return { error };
46
74
  }
47
75
  render() {
48
- return this.state.error ? this.props.fallback(this.state.error) : this.props.children;
76
+ if (this.state.error) {
77
+ return this.props.fallback(this.state.error);
78
+ }
79
+ return this.props.children;
49
80
  }
50
81
  }
51
- const j = (e) => {
52
- const r = document.getElementById(e);
53
- r && (r.scrollIntoView({
82
+ const scrollExampleCardIntoView = (id) => {
83
+ const element = document.getElementById(id);
84
+ if (!element) return;
85
+ element.scrollIntoView({
54
86
  behavior: "instant",
55
87
  block: "nearest"
56
- }), r.animate(
88
+ });
89
+ element.animate(
57
90
  [
58
91
  { boxShadow: "0 0 0 0 #3E63DD" },
59
92
  { boxShadow: "0 0 0 4px #3E63DD", offset: 0.2 },
@@ -63,53 +96,74 @@ const j = (e) => {
63
96
  duration: 900,
64
97
  easing: "ease-out"
65
98
  }
66
- ));
67
- }, O = s.object({
68
- displayName: s.string(),
69
- relativePath: s.string()
70
- }), B = s.object({
71
- columns: s.number().default(2),
72
- examples: s.array(O).default([]),
73
- selectedExamplePath: s.string().optional(),
74
- random: s.number().optional()
75
- }), G = () => {
76
- const [e, r] = h(() => typeof window < "u" ? window.location.hash : "");
77
- return p(() => {
78
- const t = () => r(window.location.hash);
79
- return window.addEventListener("hashchange", t), () => window.removeEventListener("hashchange", t);
80
- }, []), e;
81
- }, U = () => {
82
- const e = G();
83
- return w(() => {
99
+ );
100
+ };
101
+ const componentExampleInfoSchema = z.object({
102
+ displayName: z.string(),
103
+ relativePath: z.string()
104
+ });
105
+ const componentGalleryParamsSchema = z.object({
106
+ columns: z.number().default(2),
107
+ examples: z.array(componentExampleInfoSchema).default([]),
108
+ selectedExamplePath: z.string().optional(),
109
+ random: z.number().optional()
110
+ });
111
+ const useLocationHash = () => {
112
+ const [hash, setHash] = useState(() => typeof window !== "undefined" ? window.location.hash : "");
113
+ useEffect(() => {
114
+ const handleHashChange = () => setHash(window.location.hash);
115
+ window.addEventListener("hashchange", handleHashChange);
116
+ return () => window.removeEventListener("hashchange", handleHashChange);
117
+ }, []);
118
+ return hash;
119
+ };
120
+ const useComponentGalleryParams = () => {
121
+ const hash = useLocationHash();
122
+ return useMemo(() => {
84
123
  try {
85
- const r = JSON.parse(decodeURIComponent(e.slice(1)));
86
- return B.parse(r);
124
+ const parsed = JSON.parse(decodeURIComponent(hash.slice(1)));
125
+ return componentGalleryParamsSchema.parse(parsed);
87
126
  } catch {
88
127
  return { columns: 1, examples: [] };
89
128
  }
90
- }, [e]);
91
- }, V = (e, r) => {
92
- const t = E(null);
93
- return p(() => {
94
- const a = t.current;
95
- if (!e || !a) return;
96
- let o = !1, l = !1, i = 0;
97
- const d = () => {
98
- l || (o = !0);
99
- }, m = new ResizeObserver(() => {
100
- o || (l = !0, j(e), cancelAnimationFrame(i), i = requestAnimationFrame(() => {
101
- l = !1;
102
- }));
129
+ }, [hash]);
130
+ };
131
+ const useScrollToExampleCard = (relativePath, counter) => {
132
+ const containerRef = useRef(null);
133
+ useEffect(() => {
134
+ const container = containerRef.current;
135
+ if (!relativePath || !container) return;
136
+ let scrolledManually = false;
137
+ let scrollingProgrammatically = false;
138
+ let animationFrameId = 0;
139
+ const handleScroll = () => {
140
+ if (scrollingProgrammatically) return;
141
+ scrolledManually = true;
142
+ };
143
+ const resizeObserver = new ResizeObserver(() => {
144
+ if (scrolledManually) return;
145
+ scrollingProgrammatically = true;
146
+ scrollExampleCardIntoView(relativePath);
147
+ cancelAnimationFrame(animationFrameId);
148
+ animationFrameId = requestAnimationFrame(() => {
149
+ scrollingProgrammatically = false;
150
+ });
103
151
  });
104
- return window.addEventListener("scroll", d), m.observe(a), () => {
105
- window.removeEventListener("scroll", d), m.disconnect(), cancelAnimationFrame(i);
152
+ window.addEventListener("scroll", handleScroll);
153
+ resizeObserver.observe(container);
154
+ return () => {
155
+ window.removeEventListener("scroll", handleScroll);
156
+ resizeObserver.disconnect();
157
+ cancelAnimationFrame(animationFrameId);
106
158
  };
107
- }, [e, r]), t;
159
+ }, [relativePath, counter]);
160
+ return containerRef;
108
161
  };
109
- function T() {
110
- const { columns: e, examples: r, selectedExamplePath: t, random: a } = U(), o = V(t, a);
111
- return /* @__PURE__ */ n("div", { ref: o, className: k.root, style: { "--columns": e }, children: r.map((l) => /* @__PURE__ */ n(M, { example: l }, l.relativePath)) });
162
+ function ComponentGallery() {
163
+ const { columns, examples, selectedExamplePath, random } = useComponentGalleryParams();
164
+ const ref = useScrollToExampleCard(selectedExamplePath, random);
165
+ return /* @__PURE__ */ jsx("div", { ref, className: styles$1.componentGallery, style: { "--columns": columns }, children: examples.map((example) => /* @__PURE__ */ jsx(ExampleCard, { example }, example.relativePath)) });
112
166
  }
113
167
  export {
114
- T as default
168
+ ComponentGallery as default
115
169
  };
package/dist/styles.css CHANGED
@@ -1 +1,105 @@
1
- ._section_omose_1{margin-bottom:32px}._section_omose_1:last-child{margin-bottom:0}._sectionTitle_omose_9{margin-bottom:16px;font-weight:700;font-size:12px;color:light-dark(#666,#999);text-transform:uppercase;letter-spacing:.05em}._sectionContentColumn_omose_18{display:flex;flex-direction:column;gap:16px}._sectionContentRow_omose_24{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;gap:16px}._root_segk4_1{display:grid;gap:16px;padding:16px;grid-template-columns:repeat(var(--columns, 1),minmax(0,1fr))}._root_segk4_1>*{scroll-margin:16px}._card_krcb2_1{border-radius:8px;padding:16px;display:flex;flex-direction:column;background-color:light-dark(#fff,#222);overflow:auto}._cardHeader_krcb2_10{margin-bottom:16px;font-weight:700;font-size:12px;color:light-dark(#666,#999);text-transform:uppercase;letter-spacing:.05em}._cardContent_krcb2_19{flex:1}._loadingState_krcb2_23{display:flex;align-items:center;justify-content:center;padding:0 32px 32px}._spinner_krcb2_30{width:24px;height:24px;border:2px solid light-dark(#eee,#444);border-top-color:light-dark(#999,#888);border-radius:50%;animation:_spin_krcb2_30 1s linear infinite}@keyframes _spin_krcb2_30{to{transform:rotate(360deg)}}._errorState_krcb2_45{padding:32px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;text-align:center;color:light-dark(#ce2c31,#ec5d5e)}._errorIcon_krcb2_56{font-size:32px}._errorMessage_krcb2_60{border-radius:4px;overflow-wrap:break-word;font-weight:400;font-size:14px}
1
+ .section {
2
+ margin-bottom: 32px;
3
+ }
4
+
5
+ .section:last-child {
6
+ margin-bottom: 0;
7
+ }
8
+
9
+ .sectionTitle {
10
+ margin-bottom: 16px;
11
+ font: bold 12px/1.5 system-ui;
12
+ color: light-dark(#666, #999);
13
+ text-transform: uppercase;
14
+ letter-spacing: 0.05em;
15
+ }
16
+
17
+ .sectionContentColumn {
18
+ display: flex;
19
+ flex-direction: column;
20
+ gap: 16px;
21
+ }
22
+
23
+ .sectionContentRow {
24
+ display: flex;
25
+ flex-direction: row;
26
+ flex-wrap: wrap;
27
+ align-items: center;
28
+ gap: 16px;
29
+ }
30
+ html,
31
+ body {
32
+ overscroll-behavior: none;
33
+ background-color: light-dark(#f3f3f3, #151515);
34
+ }
35
+
36
+ .componentGallery {
37
+ display: grid;
38
+ gap: 16px;
39
+ padding: 16px;
40
+ grid-template-columns: repeat(var(--columns, 1), minmax(0, 1fr));
41
+
42
+ & > * {
43
+ /** Give extra space to example cards when scrolling programmatically. */
44
+ scroll-margin: 16px;
45
+ }
46
+ }
47
+ .exampleCard {
48
+ border-radius: 8px;
49
+ padding: 16px;
50
+ display: flex;
51
+ flex-direction: column;
52
+ background-color: light-dark(#fff, #222);
53
+ overflow: auto;
54
+ }
55
+
56
+ .exampleCardHeader {
57
+ margin-bottom: 16px;
58
+ font: bold 12px/1.5 system-ui;
59
+ color: light-dark(#666, #999);
60
+ text-transform: uppercase;
61
+ letter-spacing: 0.05em;
62
+ }
63
+
64
+ .exampleCardContent {
65
+ }
66
+
67
+ .exampleCardLoading {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ padding: 0 32px 32px 32px;
72
+ }
73
+
74
+ .exampleCardSpinner {
75
+ width: 24px;
76
+ height: 24px;
77
+ border: 2px solid light-dark(#eee, #444);
78
+ border-top-color: light-dark(#999, #888);
79
+ border-radius: 50%;
80
+ animation: exampleCardSpin 1s linear infinite;
81
+ }
82
+
83
+ .exampleCardError {
84
+ padding: 32px 0;
85
+ display: flex;
86
+ flex-direction: column;
87
+ gap: 8px;
88
+ text-align: center;
89
+ color: light-dark(#ce2c31, #ec5d5e);
90
+ }
91
+
92
+ .exampleCardErrorIcon {
93
+ font: 32px/1.5 system-ui;
94
+ }
95
+
96
+ .exampleCardErrorMessage {
97
+ font: 14px/1.5 system-ui;
98
+ overflow-wrap: break-word;
99
+ }
100
+
101
+ @keyframes exampleCardSpin {
102
+ to {
103
+ transform: rotate(360deg);
104
+ }
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dazl/component-gallery",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "View your components in a gallery inside Dazl",
5
5
  "author": "Dazl",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "directory": "packages/component-gallery"
35
35
  },
36
36
  "dependencies": {
37
- "zod": ">=4.0.0"
37
+ "zod": "^4.3.6"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": ">=19.0.0"
@@ -8,8 +8,7 @@
8
8
 
9
9
  .sectionTitle {
10
10
  margin-bottom: 16px;
11
- font-weight: bold;
12
- font-size: 12px;
11
+ font: bold 12px/1.5 system-ui;
13
12
  color: light-dark(#666, #999);
14
13
  text-transform: uppercase;
15
14
  letter-spacing: 0.05em;
@@ -1,18 +1,34 @@
1
+ import { Children, isValidElement, type ReactNode } from 'react';
2
+
1
3
  import styles from './example.module.css';
2
4
 
3
5
  export interface SectionProps {
4
- title?: string;
5
6
  layout?: 'row' | 'column';
6
7
  children: React.ReactNode;
7
8
  }
8
9
 
9
- export const Section = ({ title, layout = 'column', children }: SectionProps) => {
10
+ export const Section = ({ layout = 'column', children }: SectionProps) => {
11
+ const { title, content } = extractTitle(children);
12
+
10
13
  return (
11
14
  <section className={styles.section}>
12
- {title ? <div className={styles.sectionTitle}>{title}</div> : null}
15
+ {title}
13
16
  <div className={layout === 'column' ? styles.sectionContentColumn : styles.sectionContentRow}>
14
- {children}
17
+ {content}
15
18
  </div>
16
19
  </section>
17
20
  );
18
21
  };
22
+
23
+ const SectionTitle: React.FC<{ children: React.ReactNode }> = ({ children }) => (
24
+ <div className={styles.sectionTitle}>{children}</div>
25
+ );
26
+
27
+ Section.Title = SectionTitle;
28
+
29
+ const extractTitle = (children: ReactNode): { title: ReactNode; content: ReactNode } => {
30
+ const [title, ...content] = Children.toArray(children);
31
+ return isValidElement(title) && title.type === SectionTitle
32
+ ? { title, content }
33
+ : { title: null, content: children };
34
+ };
@@ -1,10 +1,17 @@
1
- .root {
1
+ html,
2
+ body {
3
+ overscroll-behavior: none;
4
+ background-color: light-dark(#f3f3f3, #151515);
5
+ }
6
+
7
+ .componentGallery {
2
8
  display: grid;
3
9
  gap: 16px;
4
10
  padding: 16px;
5
11
  grid-template-columns: repeat(var(--columns, 1), minmax(0, 1fr));
6
- }
7
12
 
8
- .root > * {
9
- scroll-margin: 16px;
13
+ & > * {
14
+ /** Give extra space to example cards when scrolling programmatically. */
15
+ scroll-margin: 16px;
16
+ }
10
17
  }
@@ -9,7 +9,7 @@ export default function ComponentGallery() {
9
9
  const ref = useScrollToExampleCard(selectedExamplePath, random);
10
10
 
11
11
  return (
12
- <div ref={ref} className={styles.root} style={{ '--columns': columns } as CSSProperties}>
12
+ <div ref={ref} className={styles.componentGallery} style={{ '--columns': columns } as CSSProperties}>
13
13
  {examples.map((example) => (
14
14
  <ExampleCard key={example.relativePath} example={example} />
15
15
  ))}
@@ -1,4 +1,4 @@
1
- .card {
1
+ .exampleCard {
2
2
  border-radius: 8px;
3
3
  padding: 16px;
4
4
  display: flex;
@@ -7,59 +7,53 @@
7
7
  overflow: auto;
8
8
  }
9
9
 
10
- .cardHeader {
10
+ .exampleCardHeader {
11
11
  margin-bottom: 16px;
12
- font-weight: bold;
13
- font-size: 12px;
12
+ font: bold 12px/1.5 system-ui;
14
13
  color: light-dark(#666, #999);
15
14
  text-transform: uppercase;
16
15
  letter-spacing: 0.05em;
17
16
  }
18
17
 
19
- .cardContent {
20
- flex: 1;
18
+ .exampleCardContent {
21
19
  }
22
20
 
23
- .loadingState {
21
+ .exampleCardLoading {
24
22
  display: flex;
25
23
  align-items: center;
26
24
  justify-content: center;
27
25
  padding: 0 32px 32px 32px;
28
26
  }
29
27
 
30
- .spinner {
28
+ .exampleCardSpinner {
31
29
  width: 24px;
32
30
  height: 24px;
33
31
  border: 2px solid light-dark(#eee, #444);
34
32
  border-top-color: light-dark(#999, #888);
35
33
  border-radius: 50%;
36
- animation: spin 1s linear infinite;
34
+ animation: exampleCardSpin 1s linear infinite;
37
35
  }
38
36
 
39
- @keyframes spin {
40
- to {
41
- transform: rotate(360deg);
42
- }
43
- }
44
-
45
- .errorState {
46
- padding: 32px;
37
+ .exampleCardError {
38
+ padding: 32px 0;
47
39
  display: flex;
48
40
  flex-direction: column;
49
- align-items: center;
50
- justify-content: center;
51
41
  gap: 8px;
52
42
  text-align: center;
53
43
  color: light-dark(#ce2c31, #ec5d5e);
54
44
  }
55
45
 
56
- .errorIcon {
57
- font-size: 32px;
46
+ .exampleCardErrorIcon {
47
+ font: 32px/1.5 system-ui;
58
48
  }
59
49
 
60
- .errorMessage {
61
- border-radius: 4px;
50
+ .exampleCardErrorMessage {
51
+ font: 14px/1.5 system-ui;
62
52
  overflow-wrap: break-word;
63
- font-weight: 400;
64
- font-size: 14px;
53
+ }
54
+
55
+ @keyframes exampleCardSpin {
56
+ to {
57
+ transform: rotate(360deg);
58
+ }
65
59
  }
@@ -7,37 +7,37 @@ interface ExampleCardProps {
7
7
  example: ComponentExampleInfo;
8
8
  }
9
9
 
10
- export const ExampleCard = memo(function ExampleCard({ example }: ExampleCardProps) {
10
+ export const ExampleCard = memo(function Example({ example }: ExampleCardProps) {
11
11
  const [Component] = useState(() => lazy(() => import(/* @vite-ignore */ `/${example.relativePath}`)));
12
12
  const contentRef = useLinkBehaviorOverride();
13
13
 
14
14
  return (
15
- <div id={example.relativePath} className={styles.card}>
16
- <h3 className={styles.cardHeader}>{example.displayName}</h3>
17
- <div ref={contentRef} className={styles.cardContent}>
18
- <ErrorBoundary fallback={(error) => <ErrorState error={error} />}>
19
- <Suspense fallback={<LoadingState />}>
15
+ <div id={example.relativePath} className={styles.exampleCard}>
16
+ <h3 className={styles.exampleCardHeader}>{example.displayName}</h3>
17
+ <ErrorBoundary fallback={(error) => <ExampleCardError error={error} />}>
18
+ <Suspense fallback={<ExampleCardLoading />}>
19
+ <div ref={contentRef} className={styles.exampleCardContent}>
20
20
  <Component />
21
- </Suspense>
22
- </ErrorBoundary>
23
- </div>
21
+ </div>
22
+ </Suspense>
23
+ </ErrorBoundary>
24
24
  </div>
25
25
  );
26
26
  });
27
27
 
28
- const LoadingState = () => {
28
+ const ExampleCardLoading = () => {
29
29
  return (
30
- <div className={styles.loadingState}>
31
- <div className={styles.spinner} />
30
+ <div className={styles.exampleCardLoading}>
31
+ <div className={styles.exampleCardSpinner} />
32
32
  </div>
33
33
  );
34
34
  };
35
35
 
36
- const ErrorState = ({ error }: { error: Error }) => {
36
+ const ExampleCardError = ({ error }: { error: Error }) => {
37
37
  return (
38
- <div className={styles.errorState}>
39
- <span className={styles.errorIcon}>⚠</span>
40
- <span className={styles.errorMessage}>{error.message}</span>
38
+ <div className={styles.exampleCardError}>
39
+ <span className={styles.exampleCardErrorIcon}>⚠</span>
40
+ <span className={styles.exampleCardErrorMessage}>{error.message}</span>
41
41
  </div>
42
42
  );
43
43
  };