torch-glare 2.4.4 → 2.5.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.
Files changed (147) hide show
  1. package/apps/lib/components/DataViews/{badgeAdapter.ts → badge.ts} +2 -2
  2. package/apps/lib/components/DataViews/cell.tsx +324 -0
  3. package/apps/lib/components/DataViews/context.ts +144 -0
  4. package/apps/lib/components/DataViews/data-views.tsx +383 -0
  5. package/apps/lib/components/DataViews/filters/children.tsx +98 -0
  6. package/apps/lib/components/DataViews/filters/custom.tsx +34 -0
  7. package/apps/lib/components/DataViews/filters/filters.tsx +163 -0
  8. package/apps/lib/components/DataViews/filters/index.ts +4 -0
  9. package/apps/lib/components/DataViews/filters/labelled.tsx +20 -0
  10. package/apps/lib/components/DataViews/filters/presets.tsx +65 -0
  11. package/apps/lib/components/DataViews/filters/summary.tsx +65 -0
  12. package/apps/lib/components/DataViews/filters/sync.tsx +35 -0
  13. package/apps/lib/components/DataViews/filters/values.ts +173 -0
  14. package/apps/lib/components/DataViews/header.tsx +217 -0
  15. package/apps/lib/components/DataViews/hooks/index.ts +5 -0
  16. package/apps/lib/components/DataViews/hooks/useActiveRow.ts +22 -0
  17. package/apps/lib/components/DataViews/hooks/useControllable.ts +52 -0
  18. package/apps/lib/components/DataViews/index.ts +74 -26
  19. package/apps/lib/components/DataViews/panel/columns.tsx +153 -0
  20. package/apps/lib/components/DataViews/panel/controls.tsx +106 -0
  21. package/apps/lib/components/DataViews/panel/index.ts +3 -0
  22. package/apps/lib/components/DataViews/panel/panel.tsx +164 -0
  23. package/apps/lib/components/DataViews/panel/saved-views.tsx +67 -0
  24. package/apps/lib/components/DataViews/panel/section.tsx +79 -0
  25. package/apps/lib/components/DataViews/panel/sort.tsx +42 -0
  26. package/apps/lib/components/DataViews/panel/tab.tsx +31 -0
  27. package/apps/lib/components/DataViews/slots.ts +63 -0
  28. package/apps/lib/components/DataViews/states.tsx +38 -0
  29. package/apps/lib/components/DataViews/types.ts +485 -178
  30. package/apps/lib/components/DataViews/views/board-view.tsx +379 -0
  31. package/apps/lib/components/DataViews/views/card-rows.tsx +36 -0
  32. package/apps/lib/components/DataViews/views/inbox-view.tsx +257 -0
  33. package/apps/lib/components/DataViews/views/pane-views.tsx +192 -0
  34. package/apps/lib/components/DataViews/views/table-view.tsx +426 -0
  35. package/apps/lib/components/DataViews/views/tree-view.tsx +365 -0
  36. package/apps/lib/components/FormBuilder/context.ts +20 -6
  37. package/apps/lib/components/FormBuilder/field-kind.ts +28 -0
  38. package/apps/lib/components/FormBuilder/fields/DateField.tsx +3 -3
  39. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +7 -6
  40. package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +30 -4
  41. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +7 -7
  42. package/apps/lib/components/FormBuilder/fields/TableField.tsx +80 -52
  43. package/apps/lib/components/FormBuilder/fields/TextField.tsx +9 -9
  44. package/apps/lib/components/FormBuilder/form-builder.tsx +66 -6
  45. package/apps/lib/components/FormBuilder/index.ts +3 -1
  46. package/apps/lib/components/FormBuilder/types.ts +40 -0
  47. package/apps/lib/components/Input.tsx +3 -0
  48. package/apps/lib/components/SearchableTable.tsx +5 -4
  49. package/apps/lib/components/SectionBlock.tsx +58 -11
  50. package/apps/lib/components/Select.tsx +3 -1
  51. package/apps/lib/components/TabSwitch.tsx +16 -4
  52. package/apps/lib/components/Table.tsx +265 -67
  53. package/apps/lib/components/TreeFolder/TreeFolder.tsx +6 -3
  54. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +16 -14
  55. package/apps/lib/components/TreeFolder/index.ts +1 -1
  56. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +70 -207
  57. package/apps/lib/hooks/useDragDrop.tsx +365 -0
  58. package/apps/lib/hooks/useInfiniteScroll.ts +108 -0
  59. package/apps/lib/registry.json +159 -4
  60. package/apps/lib/tsconfig.tsbuildinfo +1 -1
  61. package/apps/lib/utils/dataViews/path.ts +67 -0
  62. package/apps/lib/utils/dataViews/query.ts +73 -0
  63. package/apps/lib/utils/dataViews/types.ts +187 -0
  64. package/docs/components/breadcrumb.md +1 -1
  65. package/docs/components/button-group.md +1 -1
  66. package/docs/components/button.md +1 -1
  67. package/docs/components/card.md +1 -1
  68. package/docs/components/checkbox.md +1 -1
  69. package/docs/components/data-views/backend-response.md +324 -0
  70. package/docs/components/data-views/examples/a11y-rtl.md +250 -0
  71. package/docs/components/data-views/examples/api-orders-route.md +130 -0
  72. package/docs/components/data-views/examples/fields.md +362 -0
  73. package/docs/components/data-views/examples/filters.md +308 -0
  74. package/docs/components/data-views/examples/inbox-routing.md +218 -0
  75. package/docs/components/data-views/examples/index.md +29 -0
  76. package/docs/components/data-views/examples/overview.md +244 -0
  77. package/docs/components/data-views/examples/panel.md +212 -0
  78. package/docs/components/data-views/examples/scale.md +231 -0
  79. package/docs/components/data-views/examples/server-side.md +210 -0
  80. package/docs/components/data-views/examples/state.md +250 -0
  81. package/docs/components/data-views/examples/tree-custom.md +388 -0
  82. package/docs/components/data-views/examples/view-registry.md +313 -0
  83. package/docs/components/data-views/examples/views.md +534 -0
  84. package/docs/components/data-views/guide.md +405 -0
  85. package/docs/components/data-views/index.md +1504 -0
  86. package/docs/components/data-views/migration.md +79 -0
  87. package/docs/components/date-picker.md +0 -1
  88. package/docs/components/form-builder.md +19 -8
  89. package/docs/components/form-renderer.md +2 -1
  90. package/docs/components/form.md +1 -1
  91. package/docs/components/input-field.md +1 -1
  92. package/docs/components/input-otp.md +1 -1
  93. package/docs/components/input.md +1 -1
  94. package/docs/components/labeled-check-box.md +1 -1
  95. package/docs/components/labeled-radio.md +1 -1
  96. package/docs/components/radio-card.md +1 -1
  97. package/docs/components/radio.md +1 -1
  98. package/docs/components/search-field.md +1 -1
  99. package/docs/components/section-block.md +79 -3
  100. package/docs/components/select.md +1 -1
  101. package/docs/components/simple-select.md +1 -1
  102. package/docs/components/switch.md +1 -1
  103. package/docs/components/tab-switch.md +1 -1
  104. package/docs/components/table.md +45 -8
  105. package/docs/components/text-editor.md +1 -1
  106. package/docs/components/textarea.md +1 -1
  107. package/docs/components/toggle-button.md +1 -1
  108. package/docs/components/toggle.md +1 -1
  109. package/docs/components/tree-folder.md +110 -0
  110. package/docs/how-to/forms-with-form-builder.md +6 -4
  111. package/docs/reference/components.md +16 -6
  112. package/docs/tutorials/component-composition.md +11 -13
  113. package/package.json +3 -2
  114. package/apps/lib/components/DataViews/DataViewRadio.tsx +0 -49
  115. package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +0 -393
  116. package/apps/lib/components/DataViews/DataViewsHeader.tsx +0 -207
  117. package/apps/lib/components/DataViews/DataViewsLayout.tsx +0 -332
  118. package/apps/lib/components/DataViews/FilterPanel.tsx +0 -493
  119. package/apps/lib/components/DataViews/HeaderSearch.tsx +0 -93
  120. package/apps/lib/components/DataViews/InboxView.tsx +0 -463
  121. package/apps/lib/components/DataViews/InboxViewCard.tsx +0 -127
  122. package/apps/lib/components/DataViews/KanbanView.tsx +0 -336
  123. package/apps/lib/components/DataViews/PanelControls.tsx +0 -39
  124. package/apps/lib/components/DataViews/SettingsPanel.tsx +0 -279
  125. package/apps/lib/components/DataViews/TableView.tsx +0 -212
  126. package/apps/lib/components/DataViews/TreeView.tsx +0 -364
  127. package/apps/lib/components/DataViews/fieldRenderers.tsx +0 -299
  128. package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +0 -87
  129. package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +0 -120
  130. package/apps/lib/components/DataViews/filters/PresetChips.tsx +0 -45
  131. package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +0 -165
  132. package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +0 -50
  133. package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +0 -74
  134. package/apps/lib/hooks/useDataViewsState.ts +0 -175
  135. package/apps/lib/utils/dataViews/columnUtils.ts +0 -132
  136. package/apps/lib/utils/dataViews/fieldUtils.ts +0 -197
  137. package/apps/lib/utils/dataViews/nestedDataUtils.tsx +0 -371
  138. package/apps/lib/utils/dataViews/pathUtils.ts +0 -139
  139. package/apps/lib/utils/dataViews/rangeUtils.ts +0 -234
  140. package/apps/lib/utils/dataViews/treeUtils.ts +0 -396
  141. package/docs/components/data-views-config-panel.md +0 -208
  142. package/docs/components/data-views-layout.md +0 -291
  143. package/docs/components/inbox-view.md +0 -170
  144. package/docs/components/kanban-view.md +0 -135
  145. package/docs/components/table-view.md +0 -141
  146. package/docs/components/tree-view.md +0 -147
  147. package/docs/how-to/data-views-from-backend-response.md +0 -194
@@ -0,0 +1,79 @@
1
+ "use client";
2
+
3
+ import { useId, useState } from "react";
4
+ import { cn } from "../../../utils/cn";
5
+ import { ConclusionHeader } from "../../ConclusionHeader";
6
+ import type { PanelSectionProps } from "../types";
7
+
8
+ /**
9
+ * A titled block in the rail — and, when it has a title, a **collapsible** one.
10
+ *
11
+ * The header is `ConclusionHeader`, the same clickable section header `FormSummary` uses for its
12
+ * groups and the one Figma draws in this rail (`Conclusion-Header-1.0`). `Columns`, `Sort` and
13
+ * `SavedViews` all render through here, so they all fold from one implementation.
14
+ *
15
+ * It colours itself from `content-presentation-global-*` tokens rather than a `text-white`
16
+ * literal, which is what lets the same section sit in this dark rail and in the light content
17
+ * area without a second variant.
18
+ */
19
+ export function Section({
20
+ title,
21
+ description,
22
+ collapsible = true,
23
+ defaultOpen = true,
24
+ children,
25
+ className,
26
+ }: PanelSectionProps) {
27
+ const [open, setOpen] = useState(defaultOpen);
28
+ const bodyId = useId();
29
+ const isCollapsible = collapsible && title != null;
30
+ const shown = !isCollapsible || open;
31
+
32
+ return (
33
+ <section className={cn("flex flex-col", className)}>
34
+ {title != null &&
35
+ (isCollapsible ? (
36
+ <ConclusionHeader
37
+ label={title}
38
+ open={open}
39
+ onOpenChange={setOpen}
40
+ aria-controls={bodyId}
41
+ />
42
+ ) : (
43
+ <h3 className="text-content-presentation-global-primary text-[18px] font-[510] leading-[1.32] tracking-[-0.01em]">
44
+ {title}
45
+ </h3>
46
+ ))}
47
+
48
+ {/* Figma groups the description with the header (a 47px block: 28px header + 18px line),
49
+ not with the rows — so it sits outside the fold and still reads when the group is shut. */}
50
+ {description != null && (
51
+ <p className="typography-body-small-regular text-content-presentation-global-primary">
52
+ {description}
53
+ </p>
54
+ )}
55
+
56
+ {/* Fold/unfold: `grid-rows-[0fr → 1fr]` animates to the body's natural height without
57
+ measuring it, so this stays a pure-CSS transition. The inner `overflow-hidden` is what
58
+ the 0fr track actually clips — it has to stay a bare wrapper. There is no flex `gap`
59
+ here either: a gap survives a collapsed body as dead space, so the spacing below the
60
+ title lives on the body instead, inside what collapses. */}
61
+ <div
62
+ id={bodyId}
63
+ // A collapsed body keeps its controls in the DOM, so `inert` is what takes them out of
64
+ // the tab order and the a11y tree — `overflow-hidden` alone would still let focus in.
65
+ inert={!shown}
66
+ aria-hidden={!shown}
67
+ className={cn(
68
+ "grid transition-[grid-template-rows,opacity] duration-200 ease-in-out",
69
+ "motion-reduce:transition-none",
70
+ shown ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0",
71
+ )}
72
+ >
73
+ <div className="overflow-hidden">
74
+ <div className={cn("space-y-3", title != null && "pt-3")}>{children}</div>
75
+ </div>
76
+ </div>
77
+ </section>
78
+ );
79
+ }
@@ -0,0 +1,42 @@
1
+ "use client";
2
+
3
+ import { formatPathLabel } from "../../../utils/dataViews/path";
4
+ import { useDataViewsData, useDataViewsView } from "../context";
5
+ import { RadioGroup } from "./controls";
6
+ import { Section } from "./section";
7
+ import type { PanelSortProps } from "../types";
8
+
9
+ /**
10
+ * `DataViews.Panel.Sort` — the same intent the table headers emit, reachable from views that have
11
+ * no headers. Picking an option writes the sort into the root's query, which leaves through
12
+ * `onQueryChange`; nothing is reordered here, and this part takes no callback of its own.
13
+ */
14
+ export function Sort({ title = "Default Sort", className }: PanelSortProps) {
15
+ const { fields } = useDataViewsData();
16
+ const { sort, setSort } = useDataViewsView();
17
+ const sortable = fields.filter((f) => f.type !== "hidden");
18
+
19
+ return (
20
+ <Section title={title} className={className}>
21
+ {sortable.length === 0 ? (
22
+ <p className="text-content-presentation-global-tertiary text-xs">No sortable columns.</p>
23
+ ) : (
24
+ <RadioGroup
25
+ value={sort?.path ?? ""}
26
+ onValueChange={(path) =>
27
+ // Re-picking the active field flips the direction, as clicking a header twice does.
28
+ setSort(
29
+ sort?.path === path && sort.direction === "asc"
30
+ ? { path, direction: "desc" }
31
+ : { path, direction: "asc" },
32
+ )
33
+ }
34
+ items={sortable.map((field) => ({
35
+ value: field.path,
36
+ label: field.label ?? formatPathLabel(field.path),
37
+ }))}
38
+ />
39
+ )}
40
+ </Section>
41
+ );
42
+ }
@@ -0,0 +1,31 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { useDataViewsPanelTabs } from "../context";
5
+ import type { PanelTabProps } from "../types";
6
+
7
+ const TAB_MARKER = "__dvPanelTab";
8
+
9
+ /**
10
+ * One tab in the panel. It renders its own children when it is the open tab and nothing when it
11
+ * is not, so a tab is declared and filled in one place:
12
+ *
13
+ * ```tsx
14
+ * <DataViews.Panel.Tab value="columns" label="Columns">
15
+ * <DataViews.Panel.Columns />
16
+ * </DataViews.Panel.Tab>
17
+ * ```
18
+ */
19
+ export function Tab({ value, children }: PanelTabProps) {
20
+ const { tab } = useDataViewsPanelTabs();
21
+ if (tab !== value) return null;
22
+ return <>{children}</>;
23
+ }
24
+ (Tab as unknown as Record<string, boolean>)[TAB_MARKER] = true;
25
+
26
+ export function isTabElement(node: React.ReactNode): node is React.ReactElement<PanelTabProps> {
27
+ return (
28
+ React.isValidElement(node) &&
29
+ (node.type as unknown as Record<string, unknown>)?.[TAB_MARKER] === true
30
+ );
31
+ }
@@ -0,0 +1,63 @@
1
+ import React from "react";
2
+
3
+ /**
4
+ * How the root recognises its children.
5
+ *
6
+ * Children are the API: rendering `<DataViews.Table/>` is what makes a table view exist, and
7
+ * rendering `<DataViews.Panel/>` is what puts a sidebar beside it. To lay those out the root has
8
+ * to tell them apart, so each part is stamped with a marker here and found with the matching
9
+ * guard — never `child.type === Panel`, which breaks across duplicated module instances and
10
+ * silently stops matching after a hot reload.
11
+ *
12
+ * Markers and guards live in this one file rather than beside their components so that the root
13
+ * can import all of them without importing the components themselves — otherwise `header.tsx`
14
+ * would have to import from `data-views.tsx`, which already imports `header.tsx`.
15
+ */
16
+
17
+ function mark<P extends object>(component: React.ComponentType<P>, key: string) {
18
+ (component as unknown as Record<string, boolean>)[key] = true;
19
+ return component;
20
+ }
21
+
22
+ function isMarked(node: React.ReactNode, key: string) {
23
+ return React.isValidElement(node) && (node.type as unknown as Record<string, unknown>)?.[key] === true;
24
+ }
25
+
26
+ // ─── Views ────────────────────────────────────────────────────────────────────
27
+
28
+ /** What a view is called in the switcher when the caller does not say. */
29
+ export interface ViewMeta {
30
+ defaultId: string;
31
+ defaultLabel: string;
32
+ }
33
+
34
+ export function markView<P extends object>(component: React.ComponentType<P>, meta: ViewMeta) {
35
+ (component as unknown as { __dataView: ViewMeta }).__dataView = meta;
36
+ return component;
37
+ }
38
+
39
+ export function viewMetaOf(node: React.ReactNode): ViewMeta | undefined {
40
+ if (!React.isValidElement(node)) return undefined;
41
+ return (node.type as { __dataView?: ViewMeta })?.__dataView;
42
+ }
43
+
44
+ export type ViewElement = React.ReactElement<{
45
+ id?: string;
46
+ label?: string;
47
+ icon?: React.ReactNode;
48
+ }>;
49
+
50
+ export function isViewElement(node: React.ReactNode): node is ViewElement {
51
+ return viewMetaOf(node) !== undefined;
52
+ }
53
+
54
+ // ─── Everything else the root positions ───────────────────────────────────────
55
+
56
+ export const markHeader = <P extends object>(c: React.ComponentType<P>) => mark(c, "__dvHeader");
57
+ export const isHeaderElement = (n: React.ReactNode) => isMarked(n, "__dvHeader");
58
+
59
+ export const markPanel = <P extends object>(c: React.ComponentType<P>) => mark(c, "__dvPanel");
60
+ export const isPanelElement = (n: React.ReactNode) => isMarked(n, "__dvPanel");
61
+
62
+
63
+
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { cn } from "../../utils/cn";
5
+ import { Skeleton } from "../Skeleton";
6
+
7
+ /**
8
+ * The shared parts of a loading state.
9
+ *
10
+ * There is deliberately no `Empty` here. When there is nothing to show, the view shows nothing —
11
+ * a table keeps its header band and has no rows, a board keeps its columns and has no cards. A
12
+ * centred sentence in place of the view threw the chrome away and made the layout jump twice on
13
+ * every query, and it could not tell "no results" apart from "not fetched yet", so the first load
14
+ * of every page announced that nothing matched before anything had been asked for.
15
+ *
16
+ * Loading is answered per view instead, because a skeleton is only useful if it is the shape of
17
+ * the thing that is coming. These are the pieces the four views share so they cannot drift; the
18
+ * shapes themselves live with the view that owns them.
19
+ */
20
+
21
+ /**
22
+ * One shimmer bar.
23
+ *
24
+ * Vary the width at the call site — a column of identical bars reads as a grid rather than as
25
+ * text that has not arrived.
26
+ */
27
+ export function SkeletonBar({ className }: { className?: string }) {
28
+ return <Skeleton className={cn("h-[14px] w-full rounded-[4px]", className)} />;
29
+ }
30
+
31
+ /**
32
+ * `n` keys to map over.
33
+ *
34
+ * Counts are the view's own constant, not `pageSize`: they exist to fill the fold, and a skeleton
35
+ * that promises exactly as many rows as are coming is a promise it cannot keep — the server
36
+ * decides that, and it has not answered yet.
37
+ */
38
+ export const skeletonKeys = (n: number) => Array.from({ length: n }, (_, i) => i);