@tulip-systems/core 0.9.0 → 0.10.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.
Files changed (135) hide show
  1. package/dist/commands.d.mts +3 -2
  2. package/dist/commands.mjs +2 -1
  3. package/dist/components/client.d.mts +2 -1
  4. package/dist/components/client.mjs +2 -1
  5. package/dist/components/editor/extensions/file-handler/extension.d.mts +1 -1
  6. package/dist/components/editor/extensions/image/extension.d.mts +1 -1
  7. package/dist/components/editor/extensions/skeleton/extension.mjs +1 -1
  8. package/dist/components/editor/lib/constants.d.mts +1 -1
  9. package/dist/components/editor/lib/extensions.d.mts +1 -1
  10. package/dist/components/editor/lib/helpers.d.mts +5 -1
  11. package/dist/components/editor/lib/helpers.mjs +8 -1
  12. package/dist/components/layouts/root-layout.server.d.mts +3 -2
  13. package/dist/components/layouts/root-layout.server.mjs +1 -3
  14. package/dist/components/server.d.mts +2 -2
  15. package/dist/components/themes/color-theme-provider.client.d.mts +27 -0
  16. package/dist/components/themes/color-theme-provider.client.mjs +59 -0
  17. package/dist/components/themes/color-theme.d.mts +29 -0
  18. package/dist/components/themes/color-theme.mjs +32 -0
  19. package/dist/components/ui/badge.d.mts +1 -1
  20. package/dist/components/ui/button.d.mts +1 -1
  21. package/dist/components/ui/item.d.mts +1 -1
  22. package/dist/components.d.mts +3 -2
  23. package/dist/components.mjs +3 -2
  24. package/dist/data-tables/client.d.mts +2 -1
  25. package/dist/data-tables/client.mjs +2 -1
  26. package/dist/modules/auth/handler/create-client.client.d.mts +4 -4
  27. package/dist/modules/commands/components/menus/context-menu.client.d.mts +6 -7
  28. package/dist/modules/commands/components/menus/dropdown-menu.client.d.mts +6 -7
  29. package/dist/modules/commands/components/menus/floating-menu.client.d.mts +6 -7
  30. package/dist/modules/commands/components/menus/inline-menu.client.d.mts +6 -7
  31. package/dist/modules/commands/components/menus/responsive-menu.client.d.mts +5 -6
  32. package/dist/modules/commands/components/render-command.mjs +3 -5
  33. package/dist/modules/commands/lib/builder.d.mts +114 -18
  34. package/dist/modules/commands/lib/builder.mjs +42 -7
  35. package/dist/modules/commands/lib/registery.d.mts +47 -14
  36. package/dist/modules/commands/lib/registery.mjs +76 -16
  37. package/dist/modules/commands/lib/utils.d.mts +11 -0
  38. package/dist/modules/commands/lib/utils.mjs +14 -0
  39. package/dist/modules/data-tables/components/footer.mjs +3 -0
  40. package/dist/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  41. package/dist/modules/data-tables/lib/types.d.mts +3 -3
  42. package/dist/modules/data-tables/strategies/local/components.mjs +25 -0
  43. package/dist/modules/data-tables/strategies/local/strategy.d.mts +12 -0
  44. package/dist/modules/data-tables/strategies/local/strategy.mjs +31 -0
  45. package/dist/modules/inline/components/inputs/combobox-dropdown.client.d.mts +4 -3
  46. package/dist/modules/inline/components/inputs/combobox.client.d.mts +4 -3
  47. package/dist/modules/inline/components/inputs/date-time.client.d.mts +4 -3
  48. package/dist/modules/inline/components/inputs/editor.client.d.mts +4 -3
  49. package/dist/modules/inline/components/inputs/input.client.d.mts +7 -5
  50. package/dist/modules/inline/components/inputs/select.client.d.mts +4 -3
  51. package/dist/modules/inline/components/inputs/switch.client.d.mts +4 -3
  52. package/dist/modules/inline/lib/variants.d.mts +1 -1
  53. package/dist/modules/router/lib/query-client.d.mts +2 -1
  54. package/dist/modules/router/lib/query-client.mjs +2 -2
  55. package/dist/modules/storage/components/dropzone.client.d.mts +2 -2
  56. package/dist/modules/storage/lib/service.server.d.mts +21 -21
  57. package/dist/router.d.mts +2 -1
  58. package/dist/router.mjs +2 -1
  59. package/dist/src/components/editor/extensions/file-handler/extension.d.mts +1 -1
  60. package/dist/src/components/editor/extensions/image/extension.d.mts +1 -1
  61. package/dist/src/components/editor/extensions/skeleton/extension.mjs +1 -1
  62. package/dist/src/components/editor/lib/constants.d.mts +1 -1
  63. package/dist/src/components/editor/lib/extensions.d.mts +1 -1
  64. package/dist/src/components/editor/lib/helpers.d.mts +5 -1
  65. package/dist/src/components/editor/lib/helpers.mjs +8 -1
  66. package/dist/src/components/layouts/root-layout.server.d.mts +3 -2
  67. package/dist/src/components/layouts/root-layout.server.mjs +1 -3
  68. package/dist/src/components/themes/color-theme-provider.client.d.mts +27 -0
  69. package/dist/src/components/themes/color-theme-provider.client.mjs +59 -0
  70. package/dist/src/components/themes/color-theme.d.mts +29 -0
  71. package/dist/src/components/themes/color-theme.mjs +32 -0
  72. package/dist/src/components/ui/badge.d.mts +1 -1
  73. package/dist/src/components/ui/button-group.d.mts +1 -1
  74. package/dist/src/components/ui/button.d.mts +2 -2
  75. package/dist/src/components/ui/field.client.d.mts +1 -1
  76. package/dist/src/modules/commands/components/menus/context-menu.client.d.mts +6 -7
  77. package/dist/src/modules/commands/components/menus/dropdown-menu.client.d.mts +6 -7
  78. package/dist/src/modules/commands/components/menus/floating-menu.client.d.mts +6 -7
  79. package/dist/src/modules/commands/components/menus/inline-menu.client.d.mts +6 -7
  80. package/dist/src/modules/commands/components/menus/responsive-menu.client.d.mts +5 -6
  81. package/dist/src/modules/commands/components/render-command.mjs +3 -5
  82. package/dist/src/modules/commands/lib/builder.d.mts +114 -18
  83. package/dist/src/modules/commands/lib/builder.mjs +42 -7
  84. package/dist/src/modules/commands/lib/registery.d.mts +47 -14
  85. package/dist/src/modules/commands/lib/registery.mjs +76 -16
  86. package/dist/src/modules/commands/lib/utils.d.mts +11 -0
  87. package/dist/src/modules/commands/lib/utils.mjs +14 -0
  88. package/dist/src/modules/data-tables/components/footer.mjs +3 -0
  89. package/dist/src/modules/data-tables/hooks/use-context.client.d.mts +2 -2
  90. package/dist/src/modules/data-tables/lib/types.d.mts +3 -3
  91. package/dist/src/modules/data-tables/strategies/local/components.mjs +25 -0
  92. package/dist/src/modules/data-tables/strategies/local/strategy.d.mts +12 -0
  93. package/dist/src/modules/data-tables/strategies/local/strategy.mjs +31 -0
  94. package/dist/src/modules/inline/components/inputs/combobox-dropdown.client.d.mts +4 -3
  95. package/dist/src/modules/inline/components/inputs/combobox.client.d.mts +4 -3
  96. package/dist/src/modules/inline/components/inputs/date-time.client.d.mts +4 -3
  97. package/dist/src/modules/inline/components/inputs/editor.client.d.mts +4 -3
  98. package/dist/src/modules/inline/components/inputs/input.client.d.mts +7 -5
  99. package/dist/src/modules/inline/components/inputs/select.client.d.mts +4 -3
  100. package/dist/src/modules/inline/components/inputs/switch.client.d.mts +4 -3
  101. package/dist/src/modules/router/lib/query-client.d.mts +2 -1
  102. package/dist/src/modules/router/lib/query-client.mjs +2 -2
  103. package/package.json +1 -1
  104. package/src/components/editor/lib/helpers.ts +8 -0
  105. package/src/components/entry.client.ts +1 -1
  106. package/src/components/entry.ts +1 -1
  107. package/src/components/layouts/root-layout.server.tsx +4 -2
  108. package/src/components/themes/color-theme-provider.client.tsx +82 -0
  109. package/src/components/themes/color-theme.ts +32 -0
  110. package/src/modules/commands/components/menus/context-menu.client.tsx +11 -11
  111. package/src/modules/commands/components/menus/dropdown-menu.client.tsx +9 -10
  112. package/src/modules/commands/components/menus/floating-menu.client.tsx +9 -10
  113. package/src/modules/commands/components/menus/inline-menu.client.tsx +9 -10
  114. package/src/modules/commands/components/menus/responsive-menu.client.tsx +7 -8
  115. package/src/modules/commands/components/render-command.tsx +17 -13
  116. package/src/modules/commands/entry.ts +1 -0
  117. package/src/modules/commands/lib/builder.ts +216 -36
  118. package/src/modules/commands/lib/registery.ts +210 -47
  119. package/src/modules/commands/lib/utils.ts +10 -0
  120. package/src/modules/data-tables/components/footer.tsx +9 -0
  121. package/src/modules/data-tables/entry.client.ts +1 -0
  122. package/src/modules/data-tables/hooks/use-context.client.tsx +2 -2
  123. package/src/modules/data-tables/lib/types.ts +3 -3
  124. package/src/modules/data-tables/strategies/local/components.tsx +17 -0
  125. package/src/modules/data-tables/strategies/local/strategy.ts +33 -0
  126. package/src/modules/inline/components/inputs/combobox-dropdown.client.tsx +11 -5
  127. package/src/modules/inline/components/inputs/combobox.client.tsx +11 -5
  128. package/src/modules/inline/components/inputs/date-time.client.tsx +11 -5
  129. package/src/modules/inline/components/inputs/editor.client.tsx +11 -5
  130. package/src/modules/inline/components/inputs/input.client.tsx +21 -9
  131. package/src/modules/inline/components/inputs/select.client.tsx +11 -5
  132. package/src/modules/inline/components/inputs/switch.client.tsx +11 -5
  133. package/src/modules/router/entry.ts +1 -0
  134. package/src/modules/router/lib/query-client.ts +2 -2
  135. package/src/styles.css +317 -2
@@ -1,10 +1,66 @@
1
- import type z from "zod";
2
- import type { CommandDef } from "./builder";
1
+ import type { AnyCommandDef } from "./builder";
3
2
 
4
3
  /**
5
4
  * Command map type defining the shape of command definitions.
6
5
  */
7
- export type CommandMap = Record<string, CommandDef<z.ZodTypeAny, any>>;
6
+ export type CommandMap = Record<string, AnyCommandDef>;
7
+
8
+ /** Union of all values in a command map. */
9
+ type ValueOf<TCommands extends CommandMap> = TCommands[keyof TCommands];
10
+
11
+ // ─── Tag filter type helpers ───────────────────────────────────────────────────
12
+ // Each helper checks ONLY the `tags` field to avoid function-type variance issues.
13
+ // Commands with non-literal `string[]` tags are always kept (conservative fallback).
14
+
15
+ /** Keep commands that have **every** of `TFilterTags`. */
16
+ type FilterAllTags<
17
+ TCommand extends AnyCommandDef,
18
+ TFilterTags extends readonly string[],
19
+ > = TCommand extends { tags: infer CmdTags extends readonly string[] }
20
+ ? TFilterTags[number] extends CmdTags[number]
21
+ ? TCommand
22
+ : never
23
+ : TCommand;
24
+
25
+ /** Keep commands that have **some** of `TFilterTags`. */
26
+ type FilterAnyTags<
27
+ TCommand extends AnyCommandDef,
28
+ TFilterTags extends readonly string[],
29
+ > = TCommand extends { tags: infer CmdTags extends readonly string[] }
30
+ ? string[] extends CmdTags
31
+ ? TCommand
32
+ : Extract<CmdTags[number], TFilterTags[number]> extends never
33
+ ? never
34
+ : TCommand
35
+ : TCommand;
36
+
37
+ /** Keep commands whose tag set equals `TFilterTags` (order-insensitive). */
38
+ type FilterExactTags<
39
+ TCommand extends AnyCommandDef,
40
+ TFilterTags extends readonly string[],
41
+ > = TCommand extends { tags: infer CmdTags extends readonly string[] }
42
+ ? string[] extends CmdTags
43
+ ? TCommand
44
+ : [CmdTags[number]] extends [TFilterTags[number]]
45
+ ? [TFilterTags[number]] extends [CmdTags[number]]
46
+ ? TCommand
47
+ : never
48
+ : never
49
+ : TCommand;
50
+
51
+ /** Keep commands that have **without** of `TFilterTags`. */
52
+ type FilterWithoutTags<
53
+ TCommand extends AnyCommandDef,
54
+ TFilterTags extends readonly string[],
55
+ > = TCommand extends { tags: infer CmdTags extends readonly string[] }
56
+ ? string[] extends CmdTags
57
+ ? TCommand
58
+ : Extract<CmdTags[number], TFilterTags[number]> extends never
59
+ ? TCommand
60
+ : never
61
+ : TCommand;
62
+
63
+ // ─── Registry type definitions ─────────────────────────────────────────────────
8
64
 
9
65
  type CommandKey<TCommands extends CommandMap> = Extract<keyof TCommands, string>;
10
66
  type CommandFlags<TCommands extends CommandMap> = Partial<Record<CommandKey<TCommands>, boolean>>;
@@ -12,9 +68,7 @@ type CommandInput<TCommands extends CommandMap> =
12
68
  | readonly CommandKey<TCommands>[]
13
69
  | CommandFlags<TCommands>;
14
70
 
15
- type TruthyKeys<T> = {
16
- [K in keyof T]-?: T[K] extends true ? K : never;
17
- }[keyof T];
71
+ type TruthyKeys<T> = { [K in keyof T]-?: T[K] extends true ? K : never }[keyof T];
18
72
 
19
73
  type PickedCommands<
20
74
  TCommands extends CommandMap,
@@ -22,8 +76,8 @@ type PickedCommands<
22
76
  > = TInput extends readonly CommandKey<TCommands>[]
23
77
  ? Pick<TCommands, TInput[number]>
24
78
  : TInput extends CommandFlags<TCommands>
25
- ? Pick<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>>
26
- : never;
79
+ ? Pick<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>>
80
+ : never;
27
81
 
28
82
  type OmittedCommands<
29
83
  TCommands extends CommandMap,
@@ -31,8 +85,8 @@ type OmittedCommands<
31
85
  > = TInput extends readonly CommandKey<TCommands>[]
32
86
  ? Omit<TCommands, TInput[number]>
33
87
  : TInput extends CommandFlags<TCommands>
34
- ? Omit<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>>
35
- : never;
88
+ ? Omit<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>>
89
+ : never;
36
90
 
37
91
  type ExtendedCommands<TBase extends CommandMap, TExtension extends CommandMap> = Omit<
38
92
  TBase,
@@ -40,74 +94,183 @@ type ExtendedCommands<TBase extends CommandMap, TExtension extends CommandMap> =
40
94
  > &
41
95
  TExtension;
42
96
 
43
- /**
44
- * Command registry type with utility methods for picking, omitting, and converting to array.
45
- * This allows for flexible manipulation of command sets while maintaining type safety.
46
- */
97
+ type CommandTagFilterRegistry<TCommand extends AnyCommandDef> = {
98
+ every<const TTags extends string[]>(
99
+ ...tags: TTags
100
+ ): CommandTagFilterRegistry<FilterAllTags<TCommand, TTags>>;
101
+ some<const TTags extends string[]>(
102
+ ...tags: TTags
103
+ ): CommandTagFilterRegistry<FilterAnyTags<TCommand, TTags>>;
104
+ exact<const TTags extends string[]>(
105
+ ...tags: TTags
106
+ ): CommandTagFilterRegistry<FilterExactTags<TCommand, TTags>>;
107
+ without<const TTags extends string[]>(
108
+ ...tags: TTags
109
+ ): CommandTagFilterRegistry<FilterWithoutTags<TCommand, TTags>>;
110
+ toArray(): TCommand[];
111
+ };
112
+
113
+ type CommandKeyFilterRegistry<TCommands extends CommandMap> = {
114
+ pick<const TInput extends CommandInput<TCommands>>(
115
+ input: TInput,
116
+ ): CommandKeyFilterRegistry<PickedCommands<TCommands, TInput>>;
117
+ omit<const TInput extends CommandInput<TCommands>>(
118
+ input: TInput,
119
+ ): CommandKeyFilterRegistry<OmittedCommands<TCommands, TInput>>;
120
+ toArray(): ValueOf<TCommands>[];
121
+ };
122
+
47
123
  type CommandRegistry<TCommands extends CommandMap> = {
48
124
  commands: TCommands;
125
+ keys: CommandKeyFilterRegistry<TCommands>;
126
+ tags: CommandTagFilterRegistry<ValueOf<TCommands>>;
49
127
  extend<const TExtension extends CommandMap>(
50
128
  extension: TExtension,
51
129
  ): CommandRegistry<ExtendedCommands<TCommands, TExtension>>;
130
+ /** @deprecated Use `commands.keys.pick(...)` instead. */
52
131
  pick<const TInput extends CommandInput<TCommands>>(
53
132
  input: TInput,
54
133
  ): CommandRegistry<PickedCommands<TCommands, TInput>>;
134
+ /** @deprecated Use `commands.keys.omit(...)` instead. */
55
135
  omit<const TInput extends CommandInput<TCommands>>(
56
136
  input: TInput,
57
137
  ): CommandRegistry<OmittedCommands<TCommands, TInput>>;
58
- toArray(): Array<CommandDef<z.ZodTypeAny, any>>;
138
+ toArray(): ValueOf<TCommands>[];
59
139
  };
60
140
 
61
- function toKeys<TCommands extends CommandMap>(
141
+ // ─── Runtime helpers ───────────────────────────────────────────────────────────
142
+
143
+ function resolveKeys<TCommands extends CommandMap>(
62
144
  input: CommandInput<TCommands>,
63
- ): Array<CommandKey<TCommands>> {
145
+ ): CommandKey<TCommands>[] {
64
146
  if (Array.isArray(input)) return [...input];
65
-
66
- return (Object.entries(input) as Array<[CommandKey<TCommands>, boolean | undefined]>)
147
+ return Object.entries(input)
67
148
  .filter(([, enabled]) => enabled === true)
68
- .map(([key]) => key);
149
+ .map(([key]) => key as CommandKey<TCommands>);
69
150
  }
70
151
 
71
- /**
72
- * Internal function to create a command registry from a given command map.
73
- */
74
- function createRegistry<TCommands extends CommandMap>(
152
+ function dedupe(tags: readonly string[]) {
153
+ return [...new Set(tags)];
154
+ }
155
+
156
+ function hasEveryTag(command: AnyCommandDef, tags: readonly string[]) {
157
+ return tags.every((tag) => command.tags.includes(tag));
158
+ }
159
+
160
+ function hasSomeTag(command: AnyCommandDef, tags: readonly string[]) {
161
+ return tags.some((tag) => command.tags.includes(tag));
162
+ }
163
+
164
+ function hasExactTags(command: AnyCommandDef, tags: readonly string[]) {
165
+ const ctags = dedupe(command.tags);
166
+ const ftags = dedupe(tags);
167
+ return ctags.length === ftags.length && ftags.every((t) => ctags.includes(t));
168
+ }
169
+
170
+ function injectIds<TCommands extends CommandMap>(commands: TCommands): TCommands {
171
+ return Object.fromEntries(
172
+ Object.entries(commands).map(([id, c]) => [id, { ...c, id }]),
173
+ ) as TCommands;
174
+ }
175
+
176
+ function pickCommands<TCommands extends CommandMap, TInput extends CommandInput<TCommands>>(
75
177
  commands: TCommands,
76
- ): CommandRegistry<TCommands> {
178
+ input: TInput,
179
+ ): PickedCommands<TCommands, TInput> {
180
+ const keys = resolveKeys<TCommands>(input);
181
+ return Object.fromEntries(
182
+ keys.filter((k) => k in commands).map((k) => [k, commands[k]]),
183
+ ) as PickedCommands<TCommands, TInput>;
184
+ }
185
+
186
+ function omitCommands<TCommands extends CommandMap, TInput extends CommandInput<TCommands>>(
187
+ commands: TCommands,
188
+ input: TInput,
189
+ ): OmittedCommands<TCommands, TInput> {
190
+ const keys = new Set(resolveKeys<TCommands>(input));
191
+ return Object.fromEntries(
192
+ Object.entries(commands).filter(([k]) => !keys.has(k as CommandKey<TCommands>)),
193
+ ) as OmittedCommands<TCommands, TInput>;
194
+ }
195
+
196
+ function createTagFilterRegistry<TCommand extends AnyCommandDef>(
197
+ commands: TCommand[],
198
+ ): CommandTagFilterRegistry<TCommand> {
199
+ function apply<const TTags extends string[]>(
200
+ predicate: (cmd: AnyCommandDef, tags: readonly string[]) => boolean,
201
+ ) {
202
+ return (...tags: TTags) => {
203
+ const filtered = commands.filter((c) => predicate(c, tags));
204
+ return createTagFilterRegistry(filtered) as CommandTagFilterRegistry<any>;
205
+ };
206
+ }
207
+
77
208
  return {
78
- commands,
79
- extend<const TExtension extends CommandMap>(extension: TExtension) {
80
- const next = { ...commands, ...extension } as ExtendedCommands<TCommands, TExtension>;
81
- return createRegistry(next);
209
+ every: apply(hasEveryTag) as CommandTagFilterRegistry<TCommand>["every"],
210
+ some: apply(hasSomeTag) as CommandTagFilterRegistry<TCommand>["some"],
211
+ exact: apply(hasExactTags) as CommandTagFilterRegistry<TCommand>["exact"],
212
+ without: apply((c, ts) => !hasSomeTag(c, ts)) as CommandTagFilterRegistry<TCommand>["without"],
213
+ toArray() {
214
+ return commands;
82
215
  },
83
- pick<const TInput extends CommandInput<TCommands>>(input: TInput) {
84
- const keys = toKeys<TCommands>(input);
85
- const next = Object.fromEntries(
86
- keys.flatMap((key) => (key in commands ? [[key, commands[key]]] : [])),
87
- ) as PickedCommands<TCommands, TInput>;
216
+ };
217
+ }
88
218
 
89
- return createRegistry(next);
219
+ function createKeyFilterRegistry<TCommands extends CommandMap>(
220
+ commands: TCommands,
221
+ ): CommandKeyFilterRegistry<TCommands> {
222
+ return {
223
+ pick<const TInput extends CommandInput<TCommands>>(input: TInput) {
224
+ return createKeyFilterRegistry(pickCommands(commands, input));
90
225
  },
91
226
  omit<const TInput extends CommandInput<TCommands>>(input: TInput) {
92
- const keys = toKeys<TCommands>(input);
93
- const blacklist = new Set(keys);
94
- const next = Object.fromEntries(
95
- Object.entries(commands).filter(([key]) => !blacklist.has(key as CommandKey<TCommands>)),
96
- ) as OmittedCommands<TCommands, TInput>;
97
-
98
- return createRegistry(next);
227
+ return createKeyFilterRegistry(omitCommands(commands, input));
99
228
  },
100
229
  toArray() {
101
- return Object.values(commands) as Array<CommandDef<z.ZodTypeAny, any>>;
230
+ return Object.values(commands) as ValueOf<TCommands>[];
102
231
  },
103
232
  };
104
233
  }
105
234
 
235
+ function createRegistry<TCommands extends CommandMap>(
236
+ commands: TCommands,
237
+ ): CommandRegistry<TCommands> {
238
+ const withIds = injectIds(commands);
239
+ const keys = createKeyFilterRegistry(withIds);
240
+
241
+ return {
242
+ commands: withIds,
243
+ keys,
244
+ tags: createTagFilterRegistry(Object.values(withIds) as ValueOf<TCommands>[]),
245
+ extend<const TExtension extends CommandMap>(extension: TExtension) {
246
+ return createRegistry({ ...withIds, ...extension } as ExtendedCommands<
247
+ TCommands,
248
+ TExtension
249
+ >);
250
+ },
251
+ pick(input) {
252
+ return createRegistry(pickCommands(withIds, input as CommandInput<TCommands>));
253
+ },
254
+ omit(input) {
255
+ return createRegistry(omitCommands(withIds, input as CommandInput<TCommands>));
256
+ },
257
+ toArray() {
258
+ return Object.values(withIds) as ValueOf<TCommands>[];
259
+ },
260
+ } as CommandRegistry<TCommands>;
261
+ }
262
+
106
263
  /**
107
- * Public function to define commands. It takes a command map and returns a command registry with utility methods.
108
- * This is the main entry point for users to create their command sets.
109
- * @param commands - A map of command definitions keyed by command name.
110
- * @returns A command registry with the defined commands and utility methods.
264
+ * Define commands from a map of name `CommandDef`.
265
+ *
266
+ * Returns a `CommandRegistry` with key-based and tag-based filtering, plus
267
+ * `extend` for composition. Filter chains are immutable each call produces
268
+ * a new registry.
269
+ *
270
+ * @example
271
+ * const registry = defineCommands({ edit, delete, archive });
272
+ * registry.tags.every("danger", "table").toArray();
273
+ * registry.keys.omit({ delete: true }).toArray();
111
274
  */
112
275
  export function defineCommands<TCommands extends CommandMap>(
113
276
  commands: TCommands,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Normalizes command data into an array.
3
+ *
4
+ * Use this as a command transform when the same command should support both
5
+ * single-item and bulk contexts. After `.transform(ensureArray)`, command
6
+ * callbacks can safely use array helpers like `.map` and `.every`.
7
+ */
8
+ export function ensureArray<T>(data: T | T[]) {
9
+ return Array.isArray(data) ? data : [data];
10
+ }
@@ -3,6 +3,7 @@
3
3
  import type { Table } from "@tanstack/react-table";
4
4
  import type { TableMeta } from "../lib/types";
5
5
  import { InfinteTableBottombar, InfinteTableFooter } from "../strategies/infinite/components";
6
+ import { LocalTableFooter } from "../strategies/local/components";
6
7
  import { PaginationTableFooter } from "../strategies/pagination/components";
7
8
 
8
9
  /**
@@ -19,6 +20,10 @@ export function TableFooter<TData>({ table }: { table: Table<TData> }) {
19
20
  return <InfinteTableFooter table={table} />;
20
21
  }
21
22
 
23
+ if (meta.strategy.name === "local") {
24
+ return null;
25
+ }
26
+
22
27
  throw new Error(`Unknown strategy: ${meta.strategy.name}`);
23
28
  }
24
29
 
@@ -36,5 +41,9 @@ export function TableBottombar<TData>({ table }: { table: Table<TData> }) {
36
41
  return <InfinteTableBottombar table={table} />;
37
42
  }
38
43
 
44
+ if (meta.strategy.name === "local") {
45
+ return <LocalTableFooter table={table} />;
46
+ }
47
+
39
48
  throw new Error(`Unknown strategy: ${meta.strategy.name}`);
40
49
  }
@@ -19,6 +19,7 @@ export * from "./hooks/use-table.client";
19
19
  * Strategies
20
20
  */
21
21
  export * from "./strategies/infinite/strategy";
22
+ export * from "./strategies/local/strategy";
22
23
  export * from "./strategies/pagination/strategy";
23
24
  /**
24
25
  * Data tables
@@ -10,7 +10,7 @@ import {
10
10
  useEffect,
11
11
  useState,
12
12
  } from "react";
13
- import type { CommandDef } from "@/modules/commands/lib/builder";
13
+ import type { CommandFor } from "@/modules/commands/lib/builder";
14
14
  import type { TableFiltersResult } from "../lib/filters/config";
15
15
  import type { Selection, TableColumnDef, TableMetaInput, TableStrategy } from "../lib/types";
16
16
 
@@ -34,7 +34,7 @@ export type TableConfigContextValue<
34
34
  strategy: TableStrategy;
35
35
  selection?: Selection;
36
36
  columnVisibility?: VisibilityState;
37
- commands?: CommandDef[];
37
+ commands?: CommandFor<TData[], TMeta>[];
38
38
  meta?: TMeta;
39
39
  where?: TFilters;
40
40
  };
@@ -8,7 +8,7 @@ import type {
8
8
  } from "@tanstack/react-table";
9
9
  import type { UseQueryStatesKeysMap } from "nuqs";
10
10
  import type { Permission } from "@/modules/auth/lib/permissions";
11
- import type { CommandDef } from "@/modules/commands/lib/builder";
11
+ import type { CommandFor } from "@/modules/commands/lib/builder";
12
12
 
13
13
  /**
14
14
  * Data table meta
@@ -18,7 +18,7 @@ export type TableMetaInput<TData> = TanStackTableMeta<TData>;
18
18
  export type TableMeta<TData, TMeta = TableMetaInput<TData>> = {
19
19
  strategy: TableStrategy;
20
20
  selectedData: TData[];
21
- commands?: CommandDef[];
21
+ commands?: CommandFor<TData[], TMeta>[];
22
22
  };
23
23
 
24
24
  /**
@@ -33,7 +33,7 @@ export type TableColumnDef<TData> = ColumnDef<TData> & {
33
33
  * Data table strategy
34
34
  */
35
35
  export type TableStrategy = {
36
- name: "pagination" | "infinite";
36
+ name: "pagination" | "infinite" | "local";
37
37
  // Pagination
38
38
  rowCount?: number;
39
39
  paginationState?: PaginationState;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ import type { Table } from "@tanstack/react-table";
4
+
5
+ export function LocalTableFooter<TData>({ table }: { table: Table<TData> }) {
6
+ const selected = Object.keys(table.getState().rowSelection).length;
7
+ const total = table.getRowCount();
8
+
9
+ return (
10
+ <div className="flex flex-col gap-y-2 px-2 md:flex-row md:items-center md:justify-between">
11
+ <div className="text-muted-foreground text-sm">
12
+ {selected ?? table.getFilteredSelectedRowModel().rows.length} of{" "}
13
+ {total ?? table.getRowModel().rows.length} row(s) selected.
14
+ </div>
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,33 @@
1
+ "use client";
2
+
3
+ import type { SortingState } from "@tanstack/react-table";
4
+ import { useQueryStates } from "nuqs";
5
+ import { tableSearchParams } from "../../entry";
6
+ import type { TableStrategy } from "../../lib/types";
7
+ import { handleSortingChange } from "../helpers/sorting";
8
+
9
+ export type UseLocalStrategyInput = {
10
+ total?: number;
11
+ };
12
+
13
+ export type LocalStrategy = typeof useLocalStrategy;
14
+
15
+ export function useLocalStrategy({ total }: UseLocalStrategyInput = {}): TableStrategy {
16
+ const [query, setQuery] = useQueryStates(tableSearchParams);
17
+
18
+ const sortingState: SortingState = query.sort
19
+ ? [{ id: query.sort, desc: query.order === "desc" }]
20
+ : [];
21
+
22
+ const resetCursor = () => null;
23
+
24
+ return {
25
+ name: "local",
26
+ rowCount: total,
27
+ resetCursor,
28
+ tableSearchParams,
29
+ sortingState,
30
+ manualSorting: true,
31
+ onSortingChange: handleSortingChange({ sortingState, setQuery, resetCursor }),
32
+ };
33
+ }
@@ -19,15 +19,20 @@ import { type InlineEditVariantsProps, inlineEditVariants } from "../../lib/vari
19
19
 
20
20
  type ComboboxDropdownInlineTrigger = "commit";
21
21
 
22
+ type InlineStringSchema =
23
+ | z.ZodType<string | null, string | null>
24
+ | z.ZodType<string, string>;
25
+
22
26
  export type InlineEditComboboxDropdownProps<
23
27
  TValue extends ComboboxDropdownItem,
28
+ TSchema extends InlineStringSchema = z.ZodType<string | null, string | null>,
24
29
  TData = unknown,
25
30
  TError = DefaultError,
26
31
  TVariables = unknown,
27
32
  TOnMutateResult = unknown,
28
33
  > = useInlineEditOptions<
29
34
  ComboboxDropdownInlineTrigger,
30
- z.ZodString | z.ZodNullable<z.ZodString>,
35
+ TSchema,
31
36
  TData,
32
37
  TError,
33
38
  TVariables,
@@ -40,6 +45,7 @@ export type InlineEditComboboxDropdownProps<
40
45
 
41
46
  export function InlineComboboxDropdown<
42
47
  TValue extends ComboboxDropdownItem,
48
+ TSchema extends InlineStringSchema = z.ZodType<string | null, string | null>,
43
49
  TData = unknown,
44
50
  TError = DefaultError,
45
51
  TVariables = unknown,
@@ -56,11 +62,11 @@ export function InlineComboboxDropdown<
56
62
  strategy,
57
63
  disabled,
58
64
  ...props
59
- }: InlineEditComboboxDropdownProps<TValue, TData, TError, TVariables, TOnMutateResult>) {
65
+ }: InlineEditComboboxDropdownProps<TValue, TSchema, TData, TError, TVariables, TOnMutateResult>) {
60
66
  const state = useInlineState<string | null>(initialValue);
61
67
  const access = useInlineAccess({ permission, disabled });
62
68
  const mutations = useInlineMutation<
63
- z.ZodString | z.ZodNullable<z.ZodString>,
69
+ TSchema,
64
70
  string | null,
65
71
  TData,
66
72
  TError,
@@ -75,7 +81,7 @@ export function InlineComboboxDropdown<
75
81
 
76
82
  const { handleTrigger } = useInlineStrategy<
77
83
  ComboboxDropdownInlineTrigger,
78
- z.ZodString | z.ZodNullable<z.ZodString>,
84
+ TSchema,
79
85
  string | null
80
86
  >({
81
87
  strategy,
@@ -83,7 +89,7 @@ export function InlineComboboxDropdown<
83
89
  mutations,
84
90
  parser: {
85
91
  decode: (value) => value,
86
- encode: (value) => value,
92
+ encode: (value) => value as z.input<TSchema>,
87
93
  },
88
94
  });
89
95
 
@@ -13,15 +13,20 @@ import {
13
13
 
14
14
  type ComboboxInlineTrigger = "commit";
15
15
 
16
+ type InlineStringSchema =
17
+ | z.ZodType<string | null, string | null>
18
+ | z.ZodType<string, string>;
19
+
16
20
  export type InlineComboboxProps<
17
21
  TValue,
22
+ TSchema extends InlineStringSchema = z.ZodType<string | null, string | null>,
18
23
  TData = unknown,
19
24
  TError = DefaultError,
20
25
  TVariables = unknown,
21
26
  TOnMutateResult = unknown,
22
27
  > = useInlineEditOptions<
23
28
  ComboboxInlineTrigger,
24
- z.ZodString | z.ZodNullable<z.ZodString>,
29
+ TSchema,
25
30
  TData,
26
31
  TError,
27
32
  TVariables,
@@ -31,6 +36,7 @@ export type InlineComboboxProps<
31
36
 
32
37
  export function InlineCombobox<
33
38
  TValue,
39
+ TSchema extends InlineStringSchema = z.ZodType<string | null, string | null>,
34
40
  TData = unknown,
35
41
  TError = DefaultError,
36
42
  TVariables = unknown,
@@ -45,11 +51,11 @@ export function InlineCombobox<
45
51
  disabled,
46
52
  strategy,
47
53
  ...props
48
- }: InlineComboboxProps<TValue, TData, TError, TVariables, TOnMutateResult>) {
54
+ }: InlineComboboxProps<TValue, TSchema, TData, TError, TVariables, TOnMutateResult>) {
49
55
  const state = useInlineState<string | null>(initialValue);
50
56
  const access = useInlineAccess({ permission, disabled });
51
57
  const mutations = useInlineMutation<
52
- z.ZodString | z.ZodNullable<z.ZodString>,
58
+ TSchema,
53
59
  string | null,
54
60
  TData,
55
61
  TError,
@@ -64,7 +70,7 @@ export function InlineCombobox<
64
70
 
65
71
  const { handleTrigger } = useInlineStrategy<
66
72
  ComboboxInlineTrigger,
67
- z.ZodString | z.ZodNullable<z.ZodString>,
73
+ TSchema,
68
74
  string | null
69
75
  >({
70
76
  strategy,
@@ -72,7 +78,7 @@ export function InlineCombobox<
72
78
  mutations,
73
79
  parser: {
74
80
  decode: (value) => value,
75
- encode: (value) => value,
81
+ encode: (value) => value as z.input<TSchema>,
76
82
  },
77
83
  });
78
84
 
@@ -17,7 +17,12 @@ import { type InlineEditVariantsProps, inlineEditVariants } from "../../lib/vari
17
17
 
18
18
  type DateTimeInlineTrigger = "change" | "blur";
19
19
 
20
+ type InlineDateTimeSchema =
21
+ | z.ZodType<Date | null, Date | null>
22
+ | z.ZodType<Date, Date>;
23
+
20
24
  type InlineDateTimeInputProps<
25
+ TSchema extends InlineDateTimeSchema = z.ZodType<Date | null, Date | null>,
21
26
  TData = unknown,
22
27
  TError = DefaultError,
23
28
  TVariables = unknown,
@@ -25,7 +30,7 @@ type InlineDateTimeInputProps<
25
30
  > = ComponentProps<typeof DateTimeInput> &
26
31
  useInlineEditOptions<
27
32
  DateTimeInlineTrigger,
28
- z.ZodDate | z.ZodNullable<z.ZodDate>,
33
+ TSchema,
29
34
  TData,
30
35
  TError,
31
36
  TVariables,
@@ -34,6 +39,7 @@ type InlineDateTimeInputProps<
34
39
  InlineEditVariantsProps;
35
40
 
36
41
  export function InlineDateTimeInput<
42
+ TSchema extends InlineDateTimeSchema = z.ZodType<Date | null, Date | null>,
37
43
  TData = unknown,
38
44
  TError = DefaultError,
39
45
  TVariables = unknown,
@@ -48,13 +54,13 @@ export function InlineDateTimeInput<
48
54
  strategy,
49
55
  disabled,
50
56
  ...props
51
- }: InlineDateTimeInputProps<TData, TError, TVariables, TOnMutateResult>) {
57
+ }: InlineDateTimeInputProps<TSchema, TData, TError, TVariables, TOnMutateResult>) {
52
58
  const state = useInlineState<string>(
53
59
  initialValue ? format(initialValue, "yyyy-MM-dd'T'HH:mm") : "",
54
60
  );
55
61
  const access = useInlineAccess({ permission, disabled });
56
62
  const mutations = useInlineMutation<
57
- z.ZodDate | z.ZodNullable<z.ZodDate>,
63
+ TSchema,
58
64
  string,
59
65
  TData,
60
66
  TError,
@@ -69,7 +75,7 @@ export function InlineDateTimeInput<
69
75
 
70
76
  const { handleTrigger } = useInlineStrategy<
71
77
  DateTimeInlineTrigger,
72
- z.ZodDate | z.ZodNullable<z.ZodDate>,
78
+ TSchema,
73
79
  string
74
80
  >({
75
81
  strategy,
@@ -77,7 +83,7 @@ export function InlineDateTimeInput<
77
83
  mutations,
78
84
  parser: {
79
85
  decode: (date) => (date ? format(date, "yyyy-MM-dd'T'HH:mm") : ""),
80
- encode: (dateTime) => (dateTime ? new Date(dateTime) : null),
86
+ encode: (dateTime) => (dateTime ? new Date(dateTime) : null) as z.input<TSchema>,
81
87
  },
82
88
  });
83
89