torch-glare 2.4.5 → 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 (142) 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 +7 -6
  41. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +7 -7
  42. package/apps/lib/components/FormBuilder/fields/TableField.tsx +1 -1
  43. package/apps/lib/components/FormBuilder/fields/TextField.tsx +9 -9
  44. package/apps/lib/components/FormBuilder/form-builder.tsx +55 -3
  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/TabSwitch.tsx +16 -4
  48. package/apps/lib/components/TreeFolder/TreeFolder.tsx +6 -3
  49. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +16 -14
  50. package/apps/lib/components/TreeFolder/index.ts +1 -1
  51. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +70 -207
  52. package/apps/lib/hooks/useDragDrop.tsx +365 -0
  53. package/apps/lib/hooks/useInfiniteScroll.ts +108 -0
  54. package/apps/lib/registry.json +159 -4
  55. package/apps/lib/tsconfig.tsbuildinfo +1 -1
  56. package/apps/lib/utils/dataViews/path.ts +67 -0
  57. package/apps/lib/utils/dataViews/query.ts +73 -0
  58. package/apps/lib/utils/dataViews/types.ts +187 -0
  59. package/docs/components/breadcrumb.md +1 -1
  60. package/docs/components/button-group.md +1 -1
  61. package/docs/components/button.md +1 -1
  62. package/docs/components/card.md +1 -1
  63. package/docs/components/checkbox.md +1 -1
  64. package/docs/components/data-views/backend-response.md +324 -0
  65. package/docs/components/data-views/examples/a11y-rtl.md +250 -0
  66. package/docs/components/data-views/examples/api-orders-route.md +130 -0
  67. package/docs/components/data-views/examples/fields.md +362 -0
  68. package/docs/components/data-views/examples/filters.md +308 -0
  69. package/docs/components/data-views/examples/inbox-routing.md +218 -0
  70. package/docs/components/data-views/examples/index.md +29 -0
  71. package/docs/components/data-views/examples/overview.md +244 -0
  72. package/docs/components/data-views/examples/panel.md +212 -0
  73. package/docs/components/data-views/examples/scale.md +231 -0
  74. package/docs/components/data-views/examples/server-side.md +210 -0
  75. package/docs/components/data-views/examples/state.md +250 -0
  76. package/docs/components/data-views/examples/tree-custom.md +388 -0
  77. package/docs/components/data-views/examples/view-registry.md +313 -0
  78. package/docs/components/data-views/examples/views.md +534 -0
  79. package/docs/components/data-views/guide.md +405 -0
  80. package/docs/components/data-views/index.md +1504 -0
  81. package/docs/components/data-views/migration.md +79 -0
  82. package/docs/components/date-picker.md +0 -1
  83. package/docs/components/form-builder.md +3 -2
  84. package/docs/components/form-renderer.md +2 -1
  85. package/docs/components/form.md +1 -1
  86. package/docs/components/input-field.md +1 -1
  87. package/docs/components/input-otp.md +1 -1
  88. package/docs/components/input.md +1 -1
  89. package/docs/components/labeled-check-box.md +1 -1
  90. package/docs/components/labeled-radio.md +1 -1
  91. package/docs/components/radio-card.md +1 -1
  92. package/docs/components/radio.md +1 -1
  93. package/docs/components/search-field.md +1 -1
  94. package/docs/components/section-block.md +1 -1
  95. package/docs/components/select.md +1 -1
  96. package/docs/components/simple-select.md +1 -1
  97. package/docs/components/switch.md +1 -1
  98. package/docs/components/tab-switch.md +1 -1
  99. package/docs/components/table.md +1 -1
  100. package/docs/components/text-editor.md +1 -1
  101. package/docs/components/textarea.md +1 -1
  102. package/docs/components/toggle-button.md +1 -1
  103. package/docs/components/toggle.md +1 -1
  104. package/docs/components/tree-folder.md +110 -0
  105. package/docs/how-to/forms-with-form-builder.md +2 -2
  106. package/docs/reference/components.md +16 -6
  107. package/docs/tutorials/component-composition.md +11 -13
  108. package/package.json +3 -2
  109. package/apps/lib/components/DataViews/DataViewRadio.tsx +0 -49
  110. package/apps/lib/components/DataViews/DataViewsConfigPanel.tsx +0 -393
  111. package/apps/lib/components/DataViews/DataViewsHeader.tsx +0 -207
  112. package/apps/lib/components/DataViews/DataViewsLayout.tsx +0 -332
  113. package/apps/lib/components/DataViews/FilterPanel.tsx +0 -493
  114. package/apps/lib/components/DataViews/HeaderSearch.tsx +0 -93
  115. package/apps/lib/components/DataViews/InboxView.tsx +0 -463
  116. package/apps/lib/components/DataViews/InboxViewCard.tsx +0 -127
  117. package/apps/lib/components/DataViews/KanbanView.tsx +0 -336
  118. package/apps/lib/components/DataViews/PanelControls.tsx +0 -39
  119. package/apps/lib/components/DataViews/SettingsPanel.tsx +0 -279
  120. package/apps/lib/components/DataViews/TableView.tsx +0 -212
  121. package/apps/lib/components/DataViews/TreeView.tsx +0 -364
  122. package/apps/lib/components/DataViews/fieldRenderers.tsx +0 -299
  123. package/apps/lib/components/DataViews/filters/DatePickerRangeFilter.tsx +0 -87
  124. package/apps/lib/components/DataViews/filters/DateRangePopover.tsx +0 -120
  125. package/apps/lib/components/DataViews/filters/PresetChips.tsx +0 -45
  126. package/apps/lib/components/DataViews/filters/RangeSliderWithInputs.tsx +0 -165
  127. package/apps/lib/components/DataViews/tree/TreeDrawer.tsx +0 -50
  128. package/apps/lib/components/DataViews/tree/TreeSidebar.tsx +0 -74
  129. package/apps/lib/hooks/useDataViewsState.ts +0 -175
  130. package/apps/lib/utils/dataViews/columnUtils.ts +0 -132
  131. package/apps/lib/utils/dataViews/fieldUtils.ts +0 -197
  132. package/apps/lib/utils/dataViews/nestedDataUtils.tsx +0 -371
  133. package/apps/lib/utils/dataViews/pathUtils.ts +0 -139
  134. package/apps/lib/utils/dataViews/rangeUtils.ts +0 -234
  135. package/apps/lib/utils/dataViews/treeUtils.ts +0 -396
  136. package/docs/components/data-views-config-panel.md +0 -208
  137. package/docs/components/data-views-layout.md +0 -291
  138. package/docs/components/inbox-view.md +0 -170
  139. package/docs/components/kanban-view.md +0 -135
  140. package/docs/components/table-view.md +0 -141
  141. package/docs/components/tree-view.md +0 -147
  142. 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);