@godxjp/ui 13.16.4 → 13.17.1

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.
@@ -1,12 +1,27 @@
1
1
  import type { ReactNode } from "react";
2
+ import { type LucideIcon } from "lucide-react";
3
+ export type TimelineStatus = "done" | "current" | "pending";
4
+ export type TimelineVariant = "icon" | "ordinal" | "status";
2
5
  export type TimelineItem = {
3
6
  title: ReactNode;
4
7
  location?: ReactNode;
5
8
  time?: ReactNode;
6
9
  note?: ReactNode;
10
+ /** Shorthand for `status: "current"`. `status` wins when both are set. */
7
11
  current?: boolean;
12
+ /** Explicit 3-state. Resolves as `status ?? (current ? "current" : <legacy>)`. */
13
+ status?: TimelineStatus;
14
+ /** Per-item glyph override; wins over the variant/status auto-glyph. */
15
+ icon?: LucideIcon;
8
16
  };
9
17
  export type TimelineProps = {
10
18
  items: TimelineItem[];
19
+ /**
20
+ * Rail glyph strategy. `icon` (default) keeps the legacy look (Plane for the
21
+ * current step, CheckCircle2 otherwise). `ordinal` numbers every step
22
+ * (1,2,3…) and lets status drive colour only. `status` picks the glyph by
23
+ * status: done → check, current → filled dot, pending → the step number.
24
+ */
25
+ variant?: TimelineVariant;
11
26
  };
12
- export declare function Timeline({ items }: TimelineProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function Timeline({ items, variant }: TimelineProps): import("react/jsx-runtime").JSX.Element;
@@ -1,22 +1,72 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { CheckCircle2, Plane } from "lucide-react";
3
- function Timeline({ items }) {
4
- return /* @__PURE__ */ jsx("ol", { className: "ui-timeline", children: items.map((item, index) => {
5
- const Icon = item.current ? Plane : CheckCircle2;
2
+ import { Check, CheckCircle2, Plane } from "lucide-react";
3
+ const SR_PREFIX = {
4
+ done: "Completed: ",
5
+ current: "Current: ",
6
+ pending: "Upcoming: "
7
+ };
8
+ function resolveStatus(item) {
9
+ if (item.status) {
10
+ return item.status;
11
+ }
12
+ if (item.current) {
13
+ return "current";
14
+ }
15
+ return "done";
16
+ }
17
+ function Timeline({ items, variant = "icon" }) {
18
+ return /* @__PURE__ */ jsx("ol", { className: "ui-timeline", "data-variant": variant, children: items.map((item, index) => {
19
+ const status = resolveStatus(item);
20
+ const isCurrent = status === "current";
21
+ const ordinal = index + 1;
22
+ const lineCompleted = status === "done" || status === "current";
23
+ let glyph;
24
+ if (item.icon) {
25
+ const Icon = item.icon;
26
+ glyph = /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" });
27
+ } else if (variant === "ordinal") {
28
+ glyph = /* @__PURE__ */ jsx("span", { className: "ui-timeline-ordinal", children: ordinal });
29
+ } else if (variant === "status") {
30
+ if (status === "done") {
31
+ glyph = /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" });
32
+ } else if (status === "current") {
33
+ glyph = /* @__PURE__ */ jsx("span", { className: "ui-timeline-pip", "aria-hidden": "true" });
34
+ } else {
35
+ glyph = /* @__PURE__ */ jsx("span", { className: "ui-timeline-ordinal", children: ordinal });
36
+ }
37
+ } else {
38
+ const Icon = isCurrent ? Plane : CheckCircle2;
39
+ glyph = /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" });
40
+ }
6
41
  return /* @__PURE__ */ jsxs(
7
42
  "li",
8
43
  {
9
44
  className: "ui-timeline-item",
10
- "aria-current": item.current ? "step" : void 0,
45
+ "data-status": status,
46
+ "aria-current": isCurrent ? "step" : void 0,
11
47
  children: [
12
48
  /* @__PURE__ */ jsxs("div", { className: "ui-timeline-rail", children: [
13
- /* @__PURE__ */ jsx("span", { className: "ui-timeline-dot", "data-current": item.current ? "true" : void 0, children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }) }),
14
- index !== items.length - 1 ? /* @__PURE__ */ jsx("span", { className: "ui-timeline-line" }) : null
49
+ /* @__PURE__ */ jsx(
50
+ "span",
51
+ {
52
+ className: "ui-timeline-dot",
53
+ "data-status": status,
54
+ "data-current": isCurrent ? "true" : void 0,
55
+ children: glyph
56
+ }
57
+ ),
58
+ index !== items.length - 1 ? /* @__PURE__ */ jsx(
59
+ "span",
60
+ {
61
+ className: "ui-timeline-line",
62
+ "data-completed": lineCompleted ? "true" : void 0
63
+ }
64
+ ) : null
15
65
  ] }),
16
66
  /* @__PURE__ */ jsxs("div", { className: "ui-timeline-body", children: [
17
67
  /* @__PURE__ */ jsxs("div", { className: "ui-timeline-head", children: [
18
- /* @__PURE__ */ jsxs("span", { className: "ui-timeline-title", children: [
19
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: item.current ? "Current: " : "Completed: " }),
68
+ /* @__PURE__ */ jsxs("span", { className: "ui-timeline-title", "data-current": isCurrent ? "true" : void 0, children: [
69
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: SR_PREFIX[status] }),
20
70
  item.title
21
71
  ] }),
22
72
  item.time ? /* @__PURE__ */ jsx("span", { className: "ui-timeline-time", children: item.time }) : null
@@ -149,14 +149,34 @@
149
149
  width: 1.5rem;
150
150
  height: 1.5rem;
151
151
  place-items: center;
152
+ border: 1px solid transparent;
152
153
  border-radius: var(--radius-pill);
153
154
  background: hsl(var(--success));
154
155
  color: hsl(var(--success-foreground));
156
+ font-size: var(--timeline-note-font-size);
157
+ font-weight: var(--font-weight-medium);
158
+ line-height: 1;
159
+ }
160
+
161
+ /* done → success fill. */
162
+ .ui-timeline-dot[data-status="done"] {
163
+ background: hsl(var(--success));
164
+ color: hsl(var(--success-foreground));
155
165
  }
156
166
 
167
+ /* current → primary fill + ring (also the legacy data-current shorthand). */
168
+ .ui-timeline-dot[data-status="current"],
157
169
  .ui-timeline-dot[data-current="true"] {
158
170
  background: hsl(var(--primary));
159
171
  color: hsl(var(--primary-foreground));
172
+ box-shadow: 0 0 0 3px hsl(var(--primary) / 0.18);
173
+ }
174
+
175
+ /* pending → muted outline on the surface (no fill). */
176
+ .ui-timeline-dot[data-status="pending"] {
177
+ border-color: hsl(var(--border));
178
+ background: hsl(var(--background));
179
+ color: hsl(var(--muted-foreground));
160
180
  }
161
181
 
162
182
  .ui-timeline-dot svg {
@@ -164,6 +184,15 @@
164
184
  height: 0.75rem;
165
185
  }
166
186
 
187
+ /* current pip glyph for variant="status" (filled inner dot). */
188
+ .ui-timeline-pip {
189
+ display: block;
190
+ width: 0.5rem;
191
+ height: 0.5rem;
192
+ border-radius: var(--radius-pill);
193
+ background: currentColor;
194
+ }
195
+
167
196
  .ui-timeline-line {
168
197
  width: 1px;
169
198
  min-height: 2.25rem;
@@ -171,6 +200,11 @@
171
200
  background: hsl(var(--border));
172
201
  }
173
202
 
203
+ /* The segment below a done/current step reads as travelled (completed). */
204
+ .ui-timeline-line[data-completed="true"] {
205
+ background: hsl(var(--primary));
206
+ }
207
+
174
208
  .ui-timeline-head {
175
209
  display: flex;
176
210
  align-items: center;
@@ -182,6 +216,11 @@
182
216
  font-weight: var(--font-weight-medium);
183
217
  }
184
218
 
219
+ .ui-timeline-title[data-current="true"] {
220
+ color: hsl(var(--primary));
221
+ font-weight: var(--font-weight-semibold);
222
+ }
223
+
185
224
  .ui-timeline-time,
186
225
  .ui-timeline-location,
187
226
  .ui-timeline-note {
@@ -20,6 +20,12 @@
20
20
  .ui-form-field {
21
21
  display: grid;
22
22
  gap: var(--field-label-gap);
23
+ /* Never stretch to a taller grid-row sibling (e.g. a field carrying a
24
+ * helper/error). Stretching would let .ui-form-field-control's
25
+ * align-content:center vertically centre the control, so a field WITHOUT a
26
+ * helper sits lower than one WITH a helper in the same ResponsiveGrid row.
27
+ * Hugging content keeps every control top-aligned across the row. */
28
+ align-self: start;
23
29
  }
24
30
 
25
31
  .ui-form-field-label {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "13.16.4",
3
+ "version": "13.17.1",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@10.29.1",
6
6
  "sideEffects": false,