@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,19 +1,18 @@
1
- import { CommandDef } from "../../lib/builder.mjs";
1
+ import { AnyCommandDef, ExtractCommandData } from "../../lib/builder.mjs";
2
2
  import { CommandContextLifecycle } from "../../hooks/use-command.client.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { HTMLAttributes, ReactNode } from "react";
5
- import z from "zod";
6
5
 
7
6
  //#region src/modules/commands/components/menus/floating-menu.client.d.ts
8
- type FloatingCommandMenuProps<TSchema extends z.ZodTypeAny, TMeta> = HTMLAttributes<HTMLElement> & CommandContextLifecycle & {
9
- data: unknown;
10
- commands: Array<CommandDef<z.ZodTypeAny, TMeta>>;
7
+ type FloatingCommandMenuProps<TCommand extends AnyCommandDef, TMeta = object> = HTMLAttributes<HTMLElement> & CommandContextLifecycle & {
8
+ data: NoInfer<ExtractCommandData<TCommand>>;
9
+ commands: TCommand[];
11
10
  meta?: TMeta;
12
11
  fallback?: ReactNode;
13
12
  state: "open" | "closed";
14
13
  emptyLabel?: string;
15
14
  };
16
- declare function FloatingCommandMenu<TSchema extends z.ZodTypeAny, TMeta>({
15
+ declare function FloatingCommandMenu<TCommand extends AnyCommandDef, TMeta = object>({
17
16
  data,
18
17
  commands,
19
18
  meta,
@@ -25,6 +24,6 @@ declare function FloatingCommandMenu<TSchema extends z.ZodTypeAny, TMeta>({
25
24
  className,
26
25
  emptyLabel,
27
26
  ...props
28
- }: FloatingCommandMenuProps<TSchema, TMeta>): react_jsx_runtime0.JSX.Element;
27
+ }: FloatingCommandMenuProps<TCommand, TMeta>): react_jsx_runtime0.JSX.Element;
29
28
  //#endregion
30
29
  export { FloatingCommandMenu, FloatingCommandMenuProps };
@@ -1,17 +1,16 @@
1
- import { CommandDef } from "../../lib/builder.mjs";
1
+ import { AnyCommandDef, ExtractCommandData } from "../../lib/builder.mjs";
2
2
  import { CommandContextLifecycle } from "../../hooks/use-command.client.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { ComponentProps, HTMLAttributes, ReactNode } from "react";
5
- import z from "zod";
6
5
 
7
6
  //#region src/modules/commands/components/menus/inline-menu.client.d.ts
8
- type InlineCommandMenuProps<TSchema extends z.ZodTypeAny, TMeta> = HTMLAttributes<HTMLElement> & CommandContextLifecycle & {
9
- data: unknown;
10
- commands: Array<CommandDef<z.ZodTypeAny, TMeta>>;
7
+ type InlineCommandMenuProps<TCommand extends AnyCommandDef, TMeta = object> = HTMLAttributes<HTMLElement> & CommandContextLifecycle & {
8
+ data: NoInfer<ExtractCommandData<TCommand>>;
9
+ commands: TCommand[];
11
10
  meta?: TMeta;
12
11
  fallback?: ReactNode;
13
12
  };
14
- declare function InlineCommandMenu<TSchema extends z.ZodTypeAny, TMeta>({
13
+ declare function InlineCommandMenu<TCommand extends AnyCommandDef, TMeta = object>({
15
14
  data,
16
15
  meta,
17
16
  commands,
@@ -21,7 +20,7 @@ declare function InlineCommandMenu<TSchema extends z.ZodTypeAny, TMeta>({
21
20
  onSettled,
22
21
  className,
23
22
  ...props
24
- }: InlineCommandMenuProps<TSchema, TMeta>): react_jsx_runtime0.JSX.Element | null;
23
+ }: InlineCommandMenuProps<TCommand, TMeta>): react_jsx_runtime0.JSX.Element | null;
25
24
  declare function InlineCommandMenuLoading({
26
25
  className,
27
26
  ...props
@@ -1,17 +1,16 @@
1
- import { CommandDef } from "../../lib/builder.mjs";
1
+ import { AnyCommandDef, ExtractCommandData } from "../../lib/builder.mjs";
2
2
  import { CommandContextLifecycle } from "../../hooks/use-command.client.mjs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { ReactNode } from "react";
5
- import z from "zod";
6
5
 
7
6
  //#region src/modules/commands/components/menus/responsive-menu.client.d.ts
8
- type ResponsiveCommandMenuProps<TSchema extends z.ZodTypeAny, TMeta> = CommandContextLifecycle & {
9
- data: unknown;
10
- commands: Array<CommandDef<z.ZodTypeAny, TMeta>>;
7
+ type ResponsiveCommandMenuProps<TCommand extends AnyCommandDef, TMeta = object> = CommandContextLifecycle & {
8
+ data: NoInfer<ExtractCommandData<TCommand>>;
9
+ commands: TCommand[];
11
10
  meta?: TMeta;
12
11
  fallback?: ReactNode;
13
12
  };
14
- declare function ResponsiveCommandMenu<TSchema extends z.ZodTypeAny, TMeta>(props: ResponsiveCommandMenuProps<TSchema, TMeta>): react_jsx_runtime0.JSX.Element;
13
+ declare function ResponsiveCommandMenu<TCommand extends AnyCommandDef, TMeta = object>(props: ResponsiveCommandMenuProps<TCommand, TMeta>): react_jsx_runtime0.JSX.Element;
15
14
  declare function ResponsiveCommandMenuLoading(): react_jsx_runtime0.JSX.Element;
16
15
  //#endregion
17
16
  export { ResponsiveCommandMenu, ResponsiveCommandMenuLoading, ResponsiveCommandMenuProps };
@@ -8,13 +8,11 @@ import { Fragment, jsx } from "react/jsx-runtime";
8
8
  /**
9
9
  * Command renderer.
10
10
  *
11
- * It validates input data through the command schema, applies optional visibility/disabled rules,
12
- * exposes command status via context, and then renders the command content.
11
+ * It applies cheap visibility/disabled rules, exposes command status via context,
12
+ * and then renders the command content. Validation belongs at execution boundaries.
13
13
  */
14
14
  function RenderCommand(props) {
15
- const parsedData = props.command.schema.safeParse(props.data);
16
- if (!parsedData.success) return /* @__PURE__ */ jsx(Fragment, { children: props.fallback });
17
- const data = parsedData.data;
15
+ const data = props.command.transform ? props.command.transform(props.data) : props.data;
18
16
  const meta = props.meta ?? {};
19
17
  const visibleResult = props.command.visibleWhen?.({
20
18
  data,
@@ -1,39 +1,135 @@
1
1
  import { Permission } from "../../auth/lib/permissions.mjs";
2
- import { z as z$1 } from "zod";
3
2
 
4
3
  //#region src/modules/commands/lib/builder.d.ts
4
+ /**
5
+ * The different UI contexts in which a command can be rendered.
6
+ */
5
7
  type CommandUI = "dropdown" | "context" | "table" | "inline" | "custom";
8
+ /**
9
+ * The status of a command, which can be used to style the command's UI appropriately.
10
+ */
6
11
  type CommandStatus = "idle" | "pending" | "success" | "error";
7
- type CommandCondition<TSchema extends z$1.ZodTypeAny, TMeta> = (params: {
8
- data: z$1.output<TSchema>;
12
+ /**
13
+ * A string tag that can be added to a command for organizational and filtering purposes.
14
+ */
15
+ type CommandTag = string;
16
+ /**
17
+ * Represents a command definition with its associated types and properties.
18
+ */
19
+ type AnyCommandDef = CommandDef<any, any, any, any>;
20
+ /**
21
+ * Condition function that determines whether a command is visible or disabled based on the provided data and meta.
22
+ */
23
+ type CommandCondition<TData, TMeta> = (params: {
24
+ data: TData;
9
25
  meta: TMeta;
10
26
  }) => boolean | boolean[];
11
- type CommandRender<TSchema extends z$1.ZodTypeAny, TMeta> = (params: {
12
- data: z$1.output<TSchema>;
27
+ /**
28
+ * Render function that defines how a command should be rendered based on the provided data, meta, and UI context.
29
+ */
30
+ type CommandRender<TData, TMeta> = (params: {
31
+ data: TData;
13
32
  meta: TMeta;
14
33
  ui: CommandUI;
15
34
  }) => React.ReactNode;
16
35
  /**
17
36
  * Final command definition (without name).
18
37
  * Name is derived from the object key in `defineCommands`.
38
+ *
39
+ * @typeParam TData - The type `visibleWhen`, `disabledWhen`, and `render` receive.
40
+ * When `.transform()` is used this is the post-transform type.
41
+ * @typeParam TMeta - Shared contextual metadata passed alongside data.
42
+ * @typeParam TTags - Literal tuple of tag strings preserved from the builder's
43
+ * `.tags()` call. Enables compile-time tag filtering in the
44
+ * registry. Defaults to `string[]` when not provided.
45
+ * @typeParam TInput - The type that menus must pass as `data`. Equals `TData`
46
+ * when no `.transform()` is used. When `.transform(fn)` is
47
+ * called, `TInput` captures the pre-transform type so menus
48
+ * remain correctly typed while `render` sees the transformed type.
49
+ * Structurally expressed as the parameter of `transform`.
19
50
  */
20
- type CommandDef<TSchema extends z$1.ZodTypeAny = z$1.ZodTypeAny, TMeta = object> = {
51
+ type CommandDef<TData = unknown, TMeta = object, TTags extends readonly string[] = string[], TInput = TData> = {
52
+ id?: string;
53
+ tags: TTags;
54
+ transform?: (data: TInput) => TData;
21
55
  permission?: Permission;
22
- schema: TSchema;
23
- visibleWhen?: CommandCondition<TSchema, TMeta>;
24
- disabledWhen?: CommandCondition<TSchema, TMeta>;
25
- render: CommandRender<TSchema, TMeta>;
56
+ visibleWhen?: CommandCondition<TData, TMeta>;
57
+ disabledWhen?: CommandCondition<TData, TMeta>;
58
+ render: CommandRender<TData, TMeta>;
26
59
  };
27
- type ReadyCommandBuilder<TSchema extends z$1.ZodTypeAny, TMeta> = {
28
- permission(permission: Permission): ReadyCommandBuilder<TSchema, TMeta>;
29
- visibleWhen(condition: CommandCondition<TSchema, TMeta>): ReadyCommandBuilder<TSchema, TMeta>;
30
- disabledWhen(condition: CommandCondition<TSchema, TMeta>): ReadyCommandBuilder<TSchema, TMeta>;
31
- render(render: CommandRender<TSchema, TMeta>): CommandDef<TSchema, TMeta>;
60
+ /**
61
+ * Command typed by accepted input data.
62
+ *
63
+ * Useful for APIs that only care about what can be passed in (e.g. table
64
+ * providers that always pass selected rows), not the command's normalized
65
+ * render-time shape.
66
+ */
67
+ type CommandFor<TInput, TMeta = object> = CommandDef<any, TMeta, any, TInput>;
68
+ /**
69
+ * Extracts the **input** data type of a command — what menus must pass as `data`.
70
+ *
71
+ * - When `.transform()` is used, this is the pre-transform type (e.g. `Template | Template[]`).
72
+ * - Otherwise it equals the render-time type.
73
+ *
74
+ * The type is read from the `transform` field's parameter when present, and
75
+ * falls back to the `render` parameter when there is no transformer.
76
+ */
77
+ type ExtractCommandData<TCommand extends AnyCommandDef> = [NonNullable<TCommand["transform"]>] extends [never] ? TCommand extends {
78
+ render: (params: {
79
+ data: infer TData;
80
+ meta: any;
81
+ ui: any;
82
+ }) => any;
83
+ } ? TData : never : NonNullable<TCommand["transform"]> extends ((data: infer TInput) => any) ? TInput : never;
84
+ /**
85
+ * Builder state after `.$type<TData>()` has been called (or at initial state
86
+ * with `TData = unknown`).
87
+ *
88
+ * @typeParam TData - Current render-time data type (post-transform if used).
89
+ * @typeParam TTags - Literal tag tuple, carried through every method so that
90
+ * the final `CommandDef` retains the literal type.
91
+ * @typeParam TInput - Input data type for menus. Starts equal to `TData`.
92
+ * Freezes to the value of `TData` at the moment `.transform()`
93
+ * is called, while `TData` changes to the transformer's output.
94
+ */
95
+ type ReadyCommandBuilder<TData, TMeta, TTags extends readonly string[] = string[], TInput = TData> = {
96
+ /** Override the data (and optionally meta) type. Resets `TInput` to the new `TData`. */$type<TNewData, TNewMeta = TMeta>(): ReadyCommandBuilder<TNewData, TNewMeta, TTags, TNewData>;
97
+ /**
98
+ * Set the command's tags as a literal tuple. The `const` modifier infers
99
+ * string arguments as literals, enabling compile-time tag narrowing in the registry.
100
+ */
101
+ tags<const TNewTags extends string[]>(...tags: TNewTags): ReadyCommandBuilder<TData, TMeta, TNewTags, TInput>;
102
+ permission(permission: Permission): ReadyCommandBuilder<TData, TMeta, TTags, TInput>;
103
+ /**
104
+ * Declares a lightweight transformer that runs once per render — before `visibleWhen`,
105
+ * `disabledWhen`, and `render` — collapsing the input type into a single usable shape.
106
+ *
107
+ * Freezes `TInput` to the current `TData` (the pre-transform type) and advances
108
+ * `TData` to `TNewData` (the transformer's return type). This ensures:
109
+ * - Menus are typed against the union input (e.g. `Template | Template[]`)
110
+ * - `visibleWhen` and `render` work with the clean transformed type (e.g. `Template[]`)
111
+ *
112
+ * @example
113
+ * commandBuilder
114
+ * .$type<Template | Template[]>()
115
+ * .transform((data) => (Array.isArray(data) ? data : [data]))
116
+ * .visibleWhen(({ data }) => data.length > 0) // data: Template[]
117
+ * .render(({ data }) => <ArchiveCommand ids={data.map(i => i.id)} />)
118
+ */
119
+ transform<TNewData>(fn: (data: TData) => TNewData): ReadyCommandBuilder<TNewData, TMeta, TTags, TData>;
120
+ visibleWhen(condition: CommandCondition<TData, TMeta>): ReadyCommandBuilder<TData, TMeta, TTags, TInput>;
121
+ disabledWhen(condition: CommandCondition<TData, TMeta>): ReadyCommandBuilder<TData, TMeta, TTags, TInput>; /** Terminal — produces the final `CommandDef`. */
122
+ render(render: CommandRender<TData, TMeta>): CommandDef<TData, TMeta, TTags, TInput>;
32
123
  };
33
- type InitialCommandBuilder<TMeta> = {
34
- input<TNextSchema extends z$1.ZodTypeAny>(schema: TNextSchema): ReadyCommandBuilder<TNextSchema, TMeta>;
124
+ /**
125
+ * The initial builder returned by `commandBuilder` / `createCommandBuilder()`.
126
+ * Before `.$type<>()` is called, `TData` is `unknown`.
127
+ */
128
+ type InitialCommandBuilder<TMeta> = ReadyCommandBuilder<unknown, TMeta, string[], unknown> & {
129
+ $type<TNewData, TNewMeta = TMeta>(): ReadyCommandBuilder<TNewData, TNewMeta, string[], TNewData>;
130
+ tags<const TNewTags extends string[]>(...tags: TNewTags): ReadyCommandBuilder<unknown, TMeta, TNewTags, unknown>;
35
131
  };
36
132
  declare function createCommandBuilder<TMeta = object>(): InitialCommandBuilder<TMeta>;
37
133
  declare const commandBuilder: InitialCommandBuilder<object>;
38
134
  //#endregion
39
- export { CommandCondition, CommandDef, CommandRender, CommandStatus, CommandUI, commandBuilder, createCommandBuilder };
135
+ export { AnyCommandDef, CommandCondition, CommandDef, CommandFor, CommandRender, CommandStatus, CommandTag, CommandUI, ExtractCommandData, commandBuilder, createCommandBuilder };
@@ -1,20 +1,54 @@
1
1
  //#region src/modules/commands/lib/builder.ts
2
2
  function createInitialBuilder(state) {
3
- return { input(schema) {
4
- return createReadyBuilder({
5
- permission: state.permission,
6
- schema
7
- });
8
- } };
3
+ return {
4
+ ...createReadyBuilder({
5
+ tags: state.tags ?? [],
6
+ permission: state.permission
7
+ }),
8
+ tags(...tags) {
9
+ return createReadyBuilder({
10
+ tags,
11
+ permission: state.permission
12
+ });
13
+ },
14
+ $type() {
15
+ return createReadyBuilder({
16
+ tags: state.tags ?? [],
17
+ permission: state.permission
18
+ });
19
+ }
20
+ };
9
21
  }
10
22
  function createReadyBuilder(state) {
11
23
  return {
24
+ $type() {
25
+ return createReadyBuilder({
26
+ tags: state.tags,
27
+ permission: state.permission
28
+ });
29
+ },
30
+ tags(...tags) {
31
+ return createReadyBuilder({
32
+ tags,
33
+ permission: state.permission,
34
+ transform: state.transform,
35
+ visibleWhen: state.visibleWhen,
36
+ disabledWhen: state.disabledWhen
37
+ });
38
+ },
12
39
  permission(permission) {
13
40
  return createReadyBuilder({
14
41
  ...state,
15
42
  permission
16
43
  });
17
44
  },
45
+ transform(fn) {
46
+ return createReadyBuilder({
47
+ tags: state.tags,
48
+ permission: state.permission,
49
+ transform: fn
50
+ });
51
+ },
18
52
  visibleWhen(condition) {
19
53
  return createReadyBuilder({
20
54
  ...state,
@@ -29,8 +63,9 @@ function createReadyBuilder(state) {
29
63
  },
30
64
  render(render) {
31
65
  return {
66
+ tags: state.tags,
67
+ transform: state.transform,
32
68
  permission: state.permission,
33
- schema: state.schema,
34
69
  visibleWhen: state.visibleWhen,
35
70
  disabledWhen: state.disabledWhen,
36
71
  render
@@ -1,11 +1,28 @@
1
- import { CommandDef } from "./builder.mjs";
2
- import z from "zod";
1
+ import { AnyCommandDef } from "./builder.mjs";
3
2
 
4
3
  //#region src/modules/commands/lib/registery.d.ts
5
4
  /**
6
5
  * Command map type defining the shape of command definitions.
7
6
  */
8
- type CommandMap = Record<string, CommandDef<z.ZodTypeAny, any>>;
7
+ type CommandMap = Record<string, AnyCommandDef>;
8
+ /** Union of all values in a command map. */
9
+ type ValueOf<TCommands extends CommandMap> = TCommands[keyof TCommands];
10
+ /** Keep commands that have **every** of `TFilterTags`. */
11
+ type FilterAllTags<TCommand extends AnyCommandDef, TFilterTags extends readonly string[]> = TCommand extends {
12
+ tags: infer CmdTags extends readonly string[];
13
+ } ? TFilterTags[number] extends CmdTags[number] ? TCommand : never : TCommand;
14
+ /** Keep commands that have **some** of `TFilterTags`. */
15
+ type FilterAnyTags<TCommand extends AnyCommandDef, TFilterTags extends readonly string[]> = TCommand extends {
16
+ tags: infer CmdTags extends readonly string[];
17
+ } ? string[] extends CmdTags ? TCommand : Extract<CmdTags[number], TFilterTags[number]> extends never ? never : TCommand : TCommand;
18
+ /** Keep commands whose tag set equals `TFilterTags` (order-insensitive). */
19
+ type FilterExactTags<TCommand extends AnyCommandDef, TFilterTags extends readonly string[]> = TCommand extends {
20
+ tags: infer CmdTags extends readonly string[];
21
+ } ? string[] extends CmdTags ? TCommand : [CmdTags[number]] extends [TFilterTags[number]] ? [TFilterTags[number]] extends [CmdTags[number]] ? TCommand : never : never : TCommand;
22
+ /** Keep commands that have **without** of `TFilterTags`. */
23
+ type FilterWithoutTags<TCommand extends AnyCommandDef, TFilterTags extends readonly string[]> = TCommand extends {
24
+ tags: infer CmdTags extends readonly string[];
25
+ } ? string[] extends CmdTags ? TCommand : Extract<CmdTags[number], TFilterTags[number]> extends never ? TCommand : never : TCommand;
9
26
  type CommandKey<TCommands extends CommandMap> = Extract<keyof TCommands, string>;
10
27
  type CommandFlags<TCommands extends CommandMap> = Partial<Record<CommandKey<TCommands>, boolean>>;
11
28
  type CommandInput<TCommands extends CommandMap> = readonly CommandKey<TCommands>[] | CommandFlags<TCommands>;
@@ -13,22 +30,38 @@ type TruthyKeys<T> = { [K in keyof T]-?: T[K] extends true ? K : never }[keyof T
13
30
  type PickedCommands<TCommands extends CommandMap, TInput extends CommandInput<TCommands>> = TInput extends readonly CommandKey<TCommands>[] ? Pick<TCommands, TInput[number]> : TInput extends CommandFlags<TCommands> ? Pick<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>> : never;
14
31
  type OmittedCommands<TCommands extends CommandMap, TInput extends CommandInput<TCommands>> = TInput extends readonly CommandKey<TCommands>[] ? Omit<TCommands, TInput[number]> : TInput extends CommandFlags<TCommands> ? Omit<TCommands, Extract<TruthyKeys<TInput>, keyof TCommands>> : never;
15
32
  type ExtendedCommands<TBase extends CommandMap, TExtension extends CommandMap> = Omit<TBase, keyof TExtension> & TExtension;
16
- /**
17
- * Command registry type with utility methods for picking, omitting, and converting to array.
18
- * This allows for flexible manipulation of command sets while maintaining type safety.
19
- */
33
+ type CommandTagFilterRegistry<TCommand extends AnyCommandDef> = {
34
+ every<const TTags extends string[]>(...tags: TTags): CommandTagFilterRegistry<FilterAllTags<TCommand, TTags>>;
35
+ some<const TTags extends string[]>(...tags: TTags): CommandTagFilterRegistry<FilterAnyTags<TCommand, TTags>>;
36
+ exact<const TTags extends string[]>(...tags: TTags): CommandTagFilterRegistry<FilterExactTags<TCommand, TTags>>;
37
+ without<const TTags extends string[]>(...tags: TTags): CommandTagFilterRegistry<FilterWithoutTags<TCommand, TTags>>;
38
+ toArray(): TCommand[];
39
+ };
40
+ type CommandKeyFilterRegistry<TCommands extends CommandMap> = {
41
+ pick<const TInput extends CommandInput<TCommands>>(input: TInput): CommandKeyFilterRegistry<PickedCommands<TCommands, TInput>>;
42
+ omit<const TInput extends CommandInput<TCommands>>(input: TInput): CommandKeyFilterRegistry<OmittedCommands<TCommands, TInput>>;
43
+ toArray(): ValueOf<TCommands>[];
44
+ };
20
45
  type CommandRegistry<TCommands extends CommandMap> = {
21
46
  commands: TCommands;
22
- extend<const TExtension extends CommandMap>(extension: TExtension): CommandRegistry<ExtendedCommands<TCommands, TExtension>>;
23
- pick<const TInput extends CommandInput<TCommands>>(input: TInput): CommandRegistry<PickedCommands<TCommands, TInput>>;
47
+ keys: CommandKeyFilterRegistry<TCommands>;
48
+ tags: CommandTagFilterRegistry<ValueOf<TCommands>>;
49
+ extend<const TExtension extends CommandMap>(extension: TExtension): CommandRegistry<ExtendedCommands<TCommands, TExtension>>; /** @deprecated Use `commands.keys.pick(...)` instead. */
50
+ pick<const TInput extends CommandInput<TCommands>>(input: TInput): CommandRegistry<PickedCommands<TCommands, TInput>>; /** @deprecated Use `commands.keys.omit(...)` instead. */
24
51
  omit<const TInput extends CommandInput<TCommands>>(input: TInput): CommandRegistry<OmittedCommands<TCommands, TInput>>;
25
- toArray(): Array<CommandDef<z.ZodTypeAny, any>>;
52
+ toArray(): ValueOf<TCommands>[];
26
53
  };
27
54
  /**
28
- * Public function to define commands. It takes a command map and returns a command registry with utility methods.
29
- * This is the main entry point for users to create their command sets.
30
- * @param commands - A map of command definitions keyed by command name.
31
- * @returns A command registry with the defined commands and utility methods.
55
+ * Define commands from a map of name `CommandDef`.
56
+ *
57
+ * Returns a `CommandRegistry` with key-based and tag-based filtering, plus
58
+ * `extend` for composition. Filter chains are immutable each call produces
59
+ * a new registry.
60
+ *
61
+ * @example
62
+ * const registry = defineCommands({ edit, delete, archive });
63
+ * registry.tags.every("danger", "table").toArray();
64
+ * registry.keys.omit({ delete: true }).toArray();
32
65
  */
33
66
  declare function defineCommands<TCommands extends CommandMap>(commands: TCommands): CommandRegistry<TCommands>;
34
67
  //#endregion
@@ -1,39 +1,99 @@
1
1
  //#region src/modules/commands/lib/registery.ts
2
- function toKeys(input) {
2
+ function resolveKeys(input) {
3
3
  if (Array.isArray(input)) return [...input];
4
4
  return Object.entries(input).filter(([, enabled]) => enabled === true).map(([key]) => key);
5
5
  }
6
- /**
7
- * Internal function to create a command registry from a given command map.
8
- */
6
+ function dedupe(tags) {
7
+ return [...new Set(tags)];
8
+ }
9
+ function hasEveryTag(command, tags) {
10
+ return tags.every((tag) => command.tags.includes(tag));
11
+ }
12
+ function hasSomeTag(command, tags) {
13
+ return tags.some((tag) => command.tags.includes(tag));
14
+ }
15
+ function hasExactTags(command, tags) {
16
+ const ctags = dedupe(command.tags);
17
+ const ftags = dedupe(tags);
18
+ return ctags.length === ftags.length && ftags.every((t) => ctags.includes(t));
19
+ }
20
+ function injectIds(commands) {
21
+ return Object.fromEntries(Object.entries(commands).map(([id, c]) => [id, {
22
+ ...c,
23
+ id
24
+ }]));
25
+ }
26
+ function pickCommands(commands, input) {
27
+ const keys = resolveKeys(input);
28
+ return Object.fromEntries(keys.filter((k) => k in commands).map((k) => [k, commands[k]]));
29
+ }
30
+ function omitCommands(commands, input) {
31
+ const keys = new Set(resolveKeys(input));
32
+ return Object.fromEntries(Object.entries(commands).filter(([k]) => !keys.has(k)));
33
+ }
34
+ function createTagFilterRegistry(commands) {
35
+ function apply(predicate) {
36
+ return (...tags) => {
37
+ return createTagFilterRegistry(commands.filter((c) => predicate(c, tags)));
38
+ };
39
+ }
40
+ return {
41
+ every: apply(hasEveryTag),
42
+ some: apply(hasSomeTag),
43
+ exact: apply(hasExactTags),
44
+ without: apply((c, ts) => !hasSomeTag(c, ts)),
45
+ toArray() {
46
+ return commands;
47
+ }
48
+ };
49
+ }
50
+ function createKeyFilterRegistry(commands) {
51
+ return {
52
+ pick(input) {
53
+ return createKeyFilterRegistry(pickCommands(commands, input));
54
+ },
55
+ omit(input) {
56
+ return createKeyFilterRegistry(omitCommands(commands, input));
57
+ },
58
+ toArray() {
59
+ return Object.values(commands);
60
+ }
61
+ };
62
+ }
9
63
  function createRegistry(commands) {
64
+ const withIds = injectIds(commands);
10
65
  return {
11
- commands,
66
+ commands: withIds,
67
+ keys: createKeyFilterRegistry(withIds),
68
+ tags: createTagFilterRegistry(Object.values(withIds)),
12
69
  extend(extension) {
13
70
  return createRegistry({
14
- ...commands,
71
+ ...withIds,
15
72
  ...extension
16
73
  });
17
74
  },
18
75
  pick(input) {
19
- const keys = toKeys(input);
20
- return createRegistry(Object.fromEntries(keys.flatMap((key) => key in commands ? [[key, commands[key]]] : [])));
76
+ return createRegistry(pickCommands(withIds, input));
21
77
  },
22
78
  omit(input) {
23
- const keys = toKeys(input);
24
- const blacklist = new Set(keys);
25
- return createRegistry(Object.fromEntries(Object.entries(commands).filter(([key]) => !blacklist.has(key))));
79
+ return createRegistry(omitCommands(withIds, input));
26
80
  },
27
81
  toArray() {
28
- return Object.values(commands);
82
+ return Object.values(withIds);
29
83
  }
30
84
  };
31
85
  }
32
86
  /**
33
- * Public function to define commands. It takes a command map and returns a command registry with utility methods.
34
- * This is the main entry point for users to create their command sets.
35
- * @param commands - A map of command definitions keyed by command name.
36
- * @returns A command registry with the defined commands and utility methods.
87
+ * Define commands from a map of name `CommandDef`.
88
+ *
89
+ * Returns a `CommandRegistry` with key-based and tag-based filtering, plus
90
+ * `extend` for composition. Filter chains are immutable each call produces
91
+ * a new registry.
92
+ *
93
+ * @example
94
+ * const registry = defineCommands({ edit, delete, archive });
95
+ * registry.tags.every("danger", "table").toArray();
96
+ * registry.keys.omit({ delete: true }).toArray();
37
97
  */
38
98
  function defineCommands(commands) {
39
99
  return createRegistry(commands);
@@ -0,0 +1,11 @@
1
+ //#region src/modules/commands/lib/utils.d.ts
2
+ /**
3
+ * Normalizes command data into an array.
4
+ *
5
+ * Use this as a command transform when the same command should support both
6
+ * single-item and bulk contexts. After `.transform(ensureArray)`, command
7
+ * callbacks can safely use array helpers like `.map` and `.every`.
8
+ */
9
+ declare function ensureArray<T>(data: T | T[]): T[];
10
+ //#endregion
11
+ export { ensureArray };
@@ -0,0 +1,14 @@
1
+ //#region src/modules/commands/lib/utils.ts
2
+ /**
3
+ * Normalizes command data into an array.
4
+ *
5
+ * Use this as a command transform when the same command should support both
6
+ * single-item and bulk contexts. After `.transform(ensureArray)`, command
7
+ * callbacks can safely use array helpers like `.map` and `.every`.
8
+ */
9
+ function ensureArray(data) {
10
+ return Array.isArray(data) ? data : [data];
11
+ }
12
+
13
+ //#endregion
14
+ export { ensureArray };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { InfinteTableBottombar, InfinteTableFooter } from "../strategies/infinite/components.mjs";
4
+ import { LocalTableFooter } from "../strategies/local/components.mjs";
4
5
  import { PaginationTableFooter } from "../strategies/pagination/components.mjs";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
 
@@ -12,6 +13,7 @@ function TableFooter({ table }) {
12
13
  const meta = table.options.meta;
13
14
  if (meta.strategy.name === "pagination") return null;
14
15
  if (meta.strategy.name === "infinite") return /* @__PURE__ */ jsx(InfinteTableFooter, { table });
16
+ if (meta.strategy.name === "local") return null;
15
17
  throw new Error(`Unknown strategy: ${meta.strategy.name}`);
16
18
  }
17
19
  /**
@@ -21,6 +23,7 @@ function TableBottombar({ table }) {
21
23
  const meta = table.options.meta;
22
24
  if (meta.strategy.name === "pagination") return /* @__PURE__ */ jsx(PaginationTableFooter, { table });
23
25
  if (meta.strategy.name === "infinite") return /* @__PURE__ */ jsx(InfinteTableBottombar, { table });
26
+ if (meta.strategy.name === "local") return /* @__PURE__ */ jsx(LocalTableFooter, { table });
24
27
  throw new Error(`Unknown strategy: ${meta.strategy.name}`);
25
28
  }
26
29
 
@@ -1,4 +1,4 @@
1
- import { CommandDef } from "../../commands/lib/builder.mjs";
1
+ import { CommandFor } from "../../commands/lib/builder.mjs";
2
2
  import { TableFiltersResult } from "../lib/filters/config.mjs";
3
3
  import { Selection, TableColumnDef, TableMetaInput, TableStrategy } from "../lib/types.mjs";
4
4
  import * as react_jsx_runtime0 from "react/jsx-runtime";
@@ -21,7 +21,7 @@ type TableConfigContextValue<TData, TFilters extends TableFiltersResult = TableF
21
21
  strategy: TableStrategy;
22
22
  selection?: Selection;
23
23
  columnVisibility?: VisibilityState;
24
- commands?: CommandDef[];
24
+ commands?: CommandFor<TData[], TMeta>[];
25
25
  meta?: TMeta;
26
26
  where?: TFilters;
27
27
  };
@@ -1,5 +1,5 @@
1
1
  import { Permission } from "../../auth/lib/permissions.mjs";
2
- import { CommandDef } from "../../commands/lib/builder.mjs";
2
+ import { CommandFor } from "../../commands/lib/builder.mjs";
3
3
  import { UseQueryStatesKeysMap } from "nuqs";
4
4
  import { ColumnDef, PaginationState, RowSelectionState, SortingState, TableMeta, Updater } from "@tanstack/react-table";
5
5
 
@@ -11,7 +11,7 @@ type TableMetaInput<TData> = TableMeta<TData>;
11
11
  type TableMeta$1<TData, TMeta = TableMetaInput<TData>> = {
12
12
  strategy: TableStrategy;
13
13
  selectedData: TData[];
14
- commands?: CommandDef[];
14
+ commands?: CommandFor<TData[], TMeta>[];
15
15
  };
16
16
  /**
17
17
  * Table column definition
@@ -24,7 +24,7 @@ type TableColumnDef<TData> = ColumnDef<TData> & {
24
24
  * Data table strategy
25
25
  */
26
26
  type TableStrategy = {
27
- name: "pagination" | "infinite";
27
+ name: "pagination" | "infinite" | "local";
28
28
  rowCount?: number;
29
29
  paginationState?: PaginationState;
30
30
  manualPagination?: boolean;
@@ -0,0 +1,25 @@
1
+ "use client";
2
+
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+
5
+ //#region src/modules/data-tables/strategies/local/components.tsx
6
+ function LocalTableFooter({ table }) {
7
+ const selected = Object.keys(table.getState().rowSelection).length;
8
+ const total = table.getRowCount();
9
+ return /* @__PURE__ */ jsx("div", {
10
+ className: "flex flex-col gap-y-2 px-2 md:flex-row md:items-center md:justify-between",
11
+ children: /* @__PURE__ */ jsxs("div", {
12
+ className: "text-muted-foreground text-sm",
13
+ children: [
14
+ selected ?? table.getFilteredSelectedRowModel().rows.length,
15
+ " of",
16
+ " ",
17
+ total ?? table.getRowModel().rows.length,
18
+ " row(s) selected."
19
+ ]
20
+ })
21
+ });
22
+ }
23
+
24
+ //#endregion
25
+ export { LocalTableFooter };