@mostrom/app-shell 0.1.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/.claude/ralph-loop.local.md +9 -0
  2. package/README.md +172 -0
  3. package/bin/init.js +269 -0
  4. package/bun.lock +401 -0
  5. package/components.json +28 -0
  6. package/package.json +74 -0
  7. package/scripts/publish-npm.sh +202 -0
  8. package/src/AppShell.tsx +847 -0
  9. package/src/components/PageHeader.tsx +160 -0
  10. package/src/components/data-table/README.md +447 -0
  11. package/src/components/data-table/data-table-preferences.tsx +184 -0
  12. package/src/components/data-table/data-table-toolbar.tsx +118 -0
  13. package/src/components/data-table/data-table.tsx +37 -0
  14. package/src/components/data-table/index.ts +32 -0
  15. package/src/components/global-header/AllServicesButton.tsx +127 -0
  16. package/src/components/global-header/CategoriesButton.tsx +120 -0
  17. package/src/components/global-header/GlobalHeader.tsx +59 -0
  18. package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
  19. package/src/components/global-header/HeaderUtilities.tsx +243 -0
  20. package/src/components/global-header/ServicesMenu.tsx +246 -0
  21. package/src/components/layout/AppBreadcrumb.tsx +70 -0
  22. package/src/components/layout/AppFlashbar.tsx +95 -0
  23. package/src/components/layout/AppLayout.tsx +271 -0
  24. package/src/components/layout/AppNavigation.tsx +313 -0
  25. package/src/components/layout/AppSidebar.tsx +229 -0
  26. package/src/components/patterns/index.ts +14 -0
  27. package/src/components/patterns/p-alert-5.tsx +19 -0
  28. package/src/components/patterns/p-autocomplete-5.tsx +89 -0
  29. package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
  30. package/src/components/patterns/p-button-42.tsx +37 -0
  31. package/src/components/patterns/p-button-51.tsx +14 -0
  32. package/src/components/patterns/p-button-6.tsx +5 -0
  33. package/src/components/patterns/p-calendar-1.tsx +18 -0
  34. package/src/components/patterns/p-card-1.tsx +33 -0
  35. package/src/components/patterns/p-card-2.tsx +26 -0
  36. package/src/components/patterns/p-card-5.tsx +31 -0
  37. package/src/components/patterns/p-collapsible-7.tsx +121 -0
  38. package/src/components/patterns/p-command-6.tsx +113 -0
  39. package/src/components/patterns/p-dialog-1.tsx +56 -0
  40. package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
  41. package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
  42. package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
  43. package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
  44. package/src/components/patterns/p-empty-2.tsx +34 -0
  45. package/src/components/patterns/p-file-upload-1.tsx +72 -0
  46. package/src/components/patterns/p-filters-1.tsx +666 -0
  47. package/src/components/patterns/p-frame-2.tsx +26 -0
  48. package/src/components/patterns/p-tabs-2.tsx +129 -0
  49. package/src/components/reui/alert.tsx +92 -0
  50. package/src/components/reui/autocomplete.tsx +343 -0
  51. package/src/components/reui/badge.tsx +87 -0
  52. package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
  53. package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
  54. package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
  55. package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
  56. package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
  57. package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
  58. package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
  59. package/src/components/reui/data-grid/data-grid.tsx +209 -0
  60. package/src/components/reui/date-selector.tsx +1330 -0
  61. package/src/components/reui/filters.tsx +1869 -0
  62. package/src/components/reui/frame.tsx +134 -0
  63. package/src/components/reui/index.ts +17 -0
  64. package/src/components/reui/timeline.tsx +219 -0
  65. package/src/components/search/Autocomplete.tsx +183 -0
  66. package/src/components/search/AutocompleteClient.tsx +293 -0
  67. package/src/components/search/GlobalSearch.tsx +187 -0
  68. package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
  69. package/src/components/section-drawer/index.ts +19 -0
  70. package/src/components/section-drawer/section-drawer.css +665 -0
  71. package/src/components/section-drawer/section-drawer.tsx +467 -0
  72. package/src/components/sectioned-list-board/README.md +78 -0
  73. package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
  74. package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
  75. package/src/components/sectioned-list-board/index.ts +19 -0
  76. package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
  77. package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
  78. package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
  79. package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
  80. package/src/components/sectioned-list-board/types.ts +216 -0
  81. package/src/components/sectioned-list-table/README.md +80 -0
  82. package/src/components/sectioned-list-table/index.ts +14 -0
  83. package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
  84. package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
  85. package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
  86. package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
  87. package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
  88. package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
  89. package/src/components/sectioned-list-table/types.ts +120 -0
  90. package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
  91. package/src/components/ui/actions-dropdown.tsx +109 -0
  92. package/src/components/ui/assignee-selector.tsx +209 -0
  93. package/src/components/ui/avatar.tsx +107 -0
  94. package/src/components/ui/breadcrumb.tsx +109 -0
  95. package/src/components/ui/button-group.tsx +83 -0
  96. package/src/components/ui/button.tsx +64 -0
  97. package/src/components/ui/calendar.tsx +220 -0
  98. package/src/components/ui/card.tsx +92 -0
  99. package/src/components/ui/chart.tsx +376 -0
  100. package/src/components/ui/checkbox.tsx +30 -0
  101. package/src/components/ui/collapsible.tsx +33 -0
  102. package/src/components/ui/command.tsx +182 -0
  103. package/src/components/ui/context-menu.tsx +250 -0
  104. package/src/components/ui/create-button-group.tsx +128 -0
  105. package/src/components/ui/dialog.tsx +156 -0
  106. package/src/components/ui/drawer.tsx +133 -0
  107. package/src/components/ui/dropdown-menu.tsx +255 -0
  108. package/src/components/ui/empty.tsx +104 -0
  109. package/src/components/ui/field.tsx +248 -0
  110. package/src/components/ui/form.tsx +165 -0
  111. package/src/components/ui/index.ts +37 -0
  112. package/src/components/ui/input-group.tsx +168 -0
  113. package/src/components/ui/input.tsx +21 -0
  114. package/src/components/ui/kbd.tsx +28 -0
  115. package/src/components/ui/label.tsx +22 -0
  116. package/src/components/ui/navigation-menu.tsx +168 -0
  117. package/src/components/ui/page-header.tsx +80 -0
  118. package/src/components/ui/popover.tsx +87 -0
  119. package/src/components/ui/scroll-area.tsx +56 -0
  120. package/src/components/ui/select.tsx +190 -0
  121. package/src/components/ui/separator.tsx +26 -0
  122. package/src/components/ui/sheet.tsx +141 -0
  123. package/src/components/ui/sidebar.tsx +726 -0
  124. package/src/components/ui/skeleton.tsx +13 -0
  125. package/src/components/ui/sonner.tsx +38 -0
  126. package/src/components/ui/switch.tsx +33 -0
  127. package/src/components/ui/tabs.tsx +91 -0
  128. package/src/components/ui/textarea.tsx +18 -0
  129. package/src/components/ui/toggle-group.tsx +83 -0
  130. package/src/components/ui/toggle.tsx +45 -0
  131. package/src/components/ui/tooltip.tsx +57 -0
  132. package/src/hooks/use-copy-to-clipboard.ts +37 -0
  133. package/src/hooks/use-file-upload.ts +415 -0
  134. package/src/hooks/use-mobile.ts +19 -0
  135. package/src/index.ts +95 -0
  136. package/src/lib/utils.ts +6 -0
  137. package/src/styles.css +1859 -0
  138. package/src/urls.ts +83 -0
  139. package/src/vite.d.ts +22 -0
  140. package/src/vite.js +241 -0
  141. package/tsconfig.base.json +18 -0
  142. package/tsconfig.json +24 -0
@@ -0,0 +1,216 @@
1
+ import type { ReactNode } from "react";
2
+ import type { Assignee } from "@/components/ui/assignee-selector";
3
+
4
+ // Re-export Assignee from shared location
5
+ export type { Assignee };
6
+
7
+ export interface BoardSection<T> {
8
+ id: string;
9
+ name: string;
10
+ items: T[];
11
+ collapsed?: boolean;
12
+ }
13
+
14
+ export interface FilterOption {
15
+ id: string;
16
+ label: string;
17
+ value: string;
18
+ field: string;
19
+ }
20
+
21
+ export interface SortOption {
22
+ id: string;
23
+ label: string;
24
+ field: string;
25
+ }
26
+
27
+ export interface SortState {
28
+ field: string | null;
29
+ direction: "asc" | "desc" | null;
30
+ }
31
+
32
+ export interface FilterState {
33
+ field: string | null;
34
+ value: string | null;
35
+ }
36
+
37
+ export interface DateRangeState {
38
+ from: Date | undefined;
39
+ to: Date | undefined;
40
+ }
41
+
42
+ export interface DragState {
43
+ isDragging: boolean;
44
+ dragType: "section" | "card" | null;
45
+ activeId: string | null;
46
+ }
47
+
48
+ export type BoardTaskAction =
49
+ | "duplicate"
50
+ | "markComplete"
51
+ | "addSubtask"
52
+ | "openTaskDetails"
53
+ | "openInNewTab"
54
+ | "copyTaskLink"
55
+ | "delete";
56
+
57
+ /** Event fired when an item is opened (click, context menu, or keyboard) */
58
+ export interface ItemOpenEvent<T> {
59
+ item: T;
60
+ itemId: string;
61
+ sectionId: string;
62
+ source: "click" | "context-menu" | "keyboard";
63
+ }
64
+
65
+ /** Configuration for the built-in drawer */
66
+ export interface DrawerProps {
67
+ /** Which side the drawer opens from */
68
+ side?: "right" | "left";
69
+ /** Width of the drawer */
70
+ width?: number | string;
71
+ }
72
+
73
+ export type BoardBadgeColor = "red" | "blue" | "green" | "grey";
74
+
75
+ export interface BoardCardBadgeField<T> {
76
+ field: Extract<keyof T, string>;
77
+ colorMap?: Record<string, BoardBadgeColor>;
78
+ defaultColor?: BoardBadgeColor;
79
+ }
80
+
81
+ export interface BoardCardAvatarField<T> {
82
+ field: Extract<keyof T, string>;
83
+ /** Whether the assignee can be edited via command dialog */
84
+ editable?: boolean;
85
+ }
86
+
87
+ export interface BoardCardDateField<T> {
88
+ field: Extract<keyof T, string>;
89
+ editable?: boolean;
90
+ }
91
+
92
+ export interface BoardCardConfig<T> {
93
+ titleField: Extract<keyof T, string>;
94
+ badgeField?: BoardCardBadgeField<T>;
95
+ avatarField?: BoardCardAvatarField<T>;
96
+ dateField?: BoardCardDateField<T>;
97
+ }
98
+
99
+ export interface SectionedListBoardProps<T> {
100
+ /** Array of sections containing items */
101
+ sections: BoardSection<T>[];
102
+ /** Function to get unique ID from item */
103
+ getItemId: (item: T) => string;
104
+ /** Data-driven card configuration */
105
+ cardConfig: BoardCardConfig<T>;
106
+ /** Optional callback for updating non-title card fields */
107
+ onItemFieldChange?: (
108
+ sectionId: string,
109
+ itemId: string,
110
+ field: Extract<keyof T, string>,
111
+ value: unknown
112
+ ) => void;
113
+ /** Optional function to check if item is completed */
114
+ isItemCompleted?: (item: T) => boolean;
115
+
116
+ // Section handlers
117
+ onSectionReorder?: (sections: BoardSection<T>[]) => void;
118
+ onToggleSection?: (sectionId: string) => void;
119
+ onRenameSection?: (sectionId: string, name: string) => void;
120
+ onAddSection?: (afterSectionId?: string) => void;
121
+ onDeleteSection?: (sectionId: string) => void;
122
+
123
+ // Item handlers
124
+ onItemReorder?: (sectionId: string, items: T[]) => void;
125
+ onItemMoveToSection?: (
126
+ item: T,
127
+ fromSectionId: string,
128
+ toSectionId: string,
129
+ targetIndex?: number
130
+ ) => void;
131
+ onAddItem?: (sectionId: string) => void;
132
+ onItemTitleEdit?: (sectionId: string, itemId: string, newTitle: string) => void;
133
+ onItemComplete?: (sectionId: string, itemId: string, completed: boolean) => void;
134
+ onItemAction?: (
135
+ sectionId: string,
136
+ itemId: string,
137
+ action: BoardTaskAction
138
+ ) => void;
139
+
140
+ // Filter/Sort
141
+ filterOptions?: FilterOption[];
142
+ sortOptions?: SortOption[];
143
+ filter?: FilterState;
144
+ sort?: SortState;
145
+ onFilterChange?: (filter: FilterState) => void;
146
+ onSortChange?: (sort: SortState) => void;
147
+
148
+ // Date Range Filter
149
+ dateRange?: DateRangeState;
150
+ onDateRangeChange?: (dateRange: DateRangeState) => void;
151
+ /** Label for the date range filter button (defaults to "Date") */
152
+ dateRangeLabel?: string;
153
+
154
+ /** List of available assignees for the assignee selector */
155
+ assignees?: Assignee[];
156
+
157
+ // Item Details Drawer
158
+ /** Callback fired when an item is opened (click, context menu, or keyboard) */
159
+ onItemOpen?: (event: ItemOpenEvent<T>) => void;
160
+ /** Render function for item details - when provided, enables built-in drawer */
161
+ renderItemDetails?: (item: T) => ReactNode;
162
+ /** Configuration for the built-in drawer */
163
+ drawerProps?: DrawerProps;
164
+ }
165
+
166
+ export interface SortableBoardSectionProps<T> {
167
+ sortableId: string;
168
+ section: BoardSection<T>;
169
+ getItemId: (item: T) => string;
170
+ cardConfig: BoardCardConfig<T>;
171
+ onItemFieldChange?: (itemId: string, field: Extract<keyof T, string>, value: unknown) => void;
172
+ onToggle: () => void;
173
+ onRename: (name: string) => void;
174
+ onAddItem: () => void;
175
+ onDelete: () => void;
176
+ onItemTitleEdit?: (itemId: string, newTitle: string) => void;
177
+ onItemComplete?: (itemId: string, completed: boolean) => void;
178
+ onItemAction?: (itemId: string, action: BoardTaskAction) => void;
179
+ /** Callback fired when an item is opened */
180
+ onItemOpen?: (event: ItemOpenEvent<T>) => void;
181
+ isDragDisabled: boolean;
182
+ isItemCompleted?: (item: T) => boolean;
183
+ collapseDuringDrag?: boolean;
184
+ dropIndicatorPosition?: "before" | "after" | null;
185
+ cardDropIndicatorIndex?: number | null;
186
+ /** List of available assignees for the assignee selector */
187
+ assignees?: Assignee[];
188
+ }
189
+
190
+ export interface SortableBoardCardProps<T> {
191
+ item: T;
192
+ itemId: string;
193
+ index: number;
194
+ sectionId: string;
195
+ cardConfig: BoardCardConfig<T>;
196
+ isCompleted: boolean;
197
+ isDragDisabled: boolean;
198
+ onTitleEdit?: (newTitle: string) => void;
199
+ onComplete?: (completed: boolean) => void;
200
+ onTaskAction?: (action: BoardTaskAction) => void;
201
+ onFieldChange?: (field: Extract<keyof T, string>, value: unknown) => void;
202
+ /** Callback fired when the item is opened (click, context menu, or keyboard) */
203
+ onItemOpen?: (source: "click" | "context-menu" | "keyboard") => void;
204
+ /** List of available assignees for the assignee selector */
205
+ assignees?: Assignee[];
206
+ }
207
+
208
+ export interface BoardCardContentProps<T> {
209
+ item: T;
210
+ cardConfig: BoardCardConfig<T>;
211
+ titleContent?: ReactNode;
212
+ onFieldChange?: (field: Extract<keyof T, string>, value: unknown) => void;
213
+ interactive?: boolean;
214
+ /** List of available assignees for the assignee selector */
215
+ assignees?: Assignee[];
216
+ }
@@ -0,0 +1,80 @@
1
+ # Sectioned List Table
2
+
3
+ Data-driven, reusable sectioned table with draggable sections/rows and configurable columns.
4
+
5
+ ## Goal
6
+
7
+ `SectionedListTable` is **bring your own data only**:
8
+ - Consumers provide section/row data.
9
+ - Consumers describe columns with field metadata.
10
+ - Consumers do **not** provide custom cell render functions.
11
+
12
+ ## Usage
13
+
14
+ ```tsx
15
+ import {
16
+ SectionedListTable,
17
+ type ColumnDefinition,
18
+ type Section,
19
+ type SortState,
20
+ } from "~/components/shared/sectioned-list-table";
21
+
22
+ type Deal = {
23
+ id: string;
24
+ name: string;
25
+ assignee: string;
26
+ value: number;
27
+ status: string;
28
+ priority: "High" | "Medium" | "Low";
29
+ completed: boolean;
30
+ };
31
+
32
+ const columns: ColumnDefinition<Deal>[] = [
33
+ { id: "name", header: "Name", field: "name", type: "text", sortable: true, width: 250 },
34
+ { id: "assignee", header: "Assignee", field: "assignee", type: "avatar", editable: false, width: 100 },
35
+ { id: "value", header: "Value", field: "value", type: "currency", currencyCode: "USD", editable: false, width: 120 },
36
+ {
37
+ id: "priority",
38
+ header: "Priority",
39
+ field: "priority",
40
+ type: "badge",
41
+ badgeColorMap: { High: "red", Medium: "blue", Low: "green" },
42
+ editable: false,
43
+ width: 100,
44
+ },
45
+ ];
46
+
47
+ function ExampleTable({ sections }: { sections: Section<Deal>[] }) {
48
+ const [sort, setSort] = React.useState<SortState>({ columnId: null, direction: null });
49
+
50
+ return (
51
+ <SectionedListTable
52
+ columns={columns}
53
+ sections={sections}
54
+ getItemId={(item) => item.id}
55
+ isItemCompleted={(item) => item.completed}
56
+ sort={sort}
57
+ onSort={setSort}
58
+ onCellEdit={(sectionId, rowIndex, field, value) => {
59
+ // Persist data field update
60
+ }}
61
+ />
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## Column Model
67
+
68
+ - `field`: Data key used for display and editing.
69
+ - `type`:
70
+ - `text`: plain string rendering.
71
+ - `currency`: localized currency formatting.
72
+ - `badge`: Cloudscape badge with optional `badgeColorMap`.
73
+ - `avatar`: initials avatar from string field.
74
+ - `editable`: defaults to editable for `text`; set `false` to lock.
75
+
76
+ ## Notes
77
+
78
+ - Row drag is disabled when sort or filter text is active.
79
+ - Inline editing currently applies to `text` columns.
80
+ - For data consistency, keep `id` aligned with `field` unless you handle mapping in callbacks.
@@ -0,0 +1,14 @@
1
+ export { SectionedListTable } from "./sectioned-list-table";
2
+ export type {
3
+ Assignee,
4
+ ColumnDefinition,
5
+ TableColumnType,
6
+ TableBadgeColor,
7
+ TableTaskAction,
8
+ Section,
9
+ SortState,
10
+ SectionedListTableProps,
11
+ ColumnPreferences,
12
+ ItemOpenEvent,
13
+ DrawerProps,
14
+ } from "./types";