@modufolio/panel 0.1.0 → 0.2.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 (73) hide show
  1. package/README.md +1 -1
  2. package/dist/Builder/dragHandle.d.ts +10 -0
  3. package/dist/Builder/schema.d.ts +1 -1
  4. package/dist/Components/Actions/Action.vue.d.ts +1 -1
  5. package/dist/Components/Columns/CellActions.vue.d.ts +7 -7
  6. package/dist/Components/Columns/SelectColumn.vue.d.ts +8 -2
  7. package/dist/Components/Core/Button.vue.d.ts +2 -2
  8. package/dist/Components/Core/FlashMessages.vue.d.ts +6 -1
  9. package/dist/Components/Core/SelectInput.vue.d.ts +1 -1
  10. package/dist/Components/Core/TextInput.vue.d.ts +1 -1
  11. package/dist/Components/Dialogs/Dialog.vue.d.ts +1 -1
  12. package/dist/Components/Drawer/Drawer.vue.d.ts +1 -1
  13. package/dist/Components/Drawer/DrawerLink.vue.d.ts +4 -3
  14. package/dist/Components/Drawer/DrawerRelationList.vue.d.ts +12 -10
  15. package/dist/Components/Drawer/DrawerStack.vue.d.ts +14 -14
  16. package/dist/Components/Drawer/useDrawerDirtyGuard.d.ts +17 -0
  17. package/dist/Components/Drawer/useDrawerStack.d.ts +1 -1
  18. package/dist/Components/Drawer/useIsDrawer.d.ts +8 -0
  19. package/dist/Components/Fields/BelongsToSelect.vue.d.ts +14 -9
  20. package/dist/Components/Fields/CheckboxField.vue.d.ts +2 -2
  21. package/dist/Components/Fields/ColorPickerField.vue.d.ts +3 -3
  22. package/dist/Components/Fields/DataField.vue.d.ts +55 -0
  23. package/dist/Components/Fields/DatePickerField.vue.d.ts +2 -2
  24. package/dist/Components/Fields/DateRangePickerField.vue.d.ts +2 -2
  25. package/dist/Components/Fields/DateTimePickerField.vue.d.ts +2 -2
  26. package/dist/Components/Fields/EmbedField.vue.d.ts +95 -0
  27. package/dist/Components/Fields/FieldDescription.vue.d.ts +25 -0
  28. package/dist/Components/Fields/FieldLabel.vue.d.ts +67 -0
  29. package/dist/Components/Fields/FieldMessage.vue.d.ts +25 -0
  30. package/dist/Components/Fields/FieldPrimitive.vue.d.ts +127 -0
  31. package/dist/Components/Fields/FileUploadField.vue.d.ts +2 -2
  32. package/dist/Components/Fields/HiddenField.vue.d.ts +28 -0
  33. package/dist/Components/Fields/MultiSelectField.vue.d.ts +3 -3
  34. package/dist/Components/Fields/ProseMirrorBuilderField.vue.d.ts +1 -1
  35. package/dist/Components/Fields/RangeField.vue.d.ts +2 -2
  36. package/dist/Components/Fields/RepeaterField.vue.d.ts +29 -15
  37. package/dist/Components/Fields/RichTextEditorField.vue.d.ts +3 -3
  38. package/dist/Components/Fields/SelectField.vue.d.ts +6 -5
  39. package/dist/Components/Fields/SetField.vue.d.ts +77 -0
  40. package/dist/Components/Fields/TagsField.vue.d.ts +3 -3
  41. package/dist/Components/Fields/TextField.vue.d.ts +4 -4
  42. package/dist/Components/Fields/TextareaField.vue.d.ts +3 -3
  43. package/dist/Components/Fields/TimePickerField.vue.d.ts +2 -2
  44. package/dist/Components/Fields/ToggleButtonsField.vue.d.ts +10 -5
  45. package/dist/Components/Fields/ToggleField.vue.d.ts +2 -2
  46. package/dist/Components/Fields/fieldsFromSpec.d.ts +10 -2
  47. package/dist/Components/Fields/useBlueprint.d.ts +4 -0
  48. package/dist/Components/Filters/DateRangeFilter.vue.d.ts +1 -1
  49. package/dist/Components/Filters/MultiSelectFilter.vue.d.ts +2 -2
  50. package/dist/Components/Filters/NumberFilter.vue.d.ts +1 -1
  51. package/dist/Components/Filters/SelectFilter.vue.d.ts +2 -2
  52. package/dist/Components/Filters/TernaryFilter.vue.d.ts +1 -1
  53. package/dist/Components/Layout/AppLayout.vue.d.ts +5 -3
  54. package/dist/Components/Layout/Breadcrumbs.vue.d.ts +2 -2
  55. package/dist/Components/Layout/Sidebar.vue.d.ts +22 -3
  56. package/dist/Components/Relations/RelationManager.vue.d.ts +4 -3
  57. package/dist/Components/Table/ExportButton.vue.d.ts +3 -3
  58. package/dist/Components/Table/SchemaCell.d.ts +7 -7
  59. package/dist/Components/Table/SchemaTable.vue.d.ts +21 -20
  60. package/dist/Components/Table/schemaLinks.d.ts +11 -3
  61. package/dist/Components/Table/tableSchema.d.ts +28 -11
  62. package/dist/Components/Table/tableTypes.d.ts +14 -1
  63. package/dist/Components/Table/useSchemaActions.d.ts +9 -26
  64. package/dist/Composables/useDeleteConfirmation.d.ts +11 -31
  65. package/dist/Composables/useNestedDrawerForm.d.ts +7 -0
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +6 -1
  68. package/dist/index.js +9951 -9770
  69. package/dist/index.js.map +1 -1
  70. package/docs/custom-fields.md +54 -2
  71. package/docs/drawer-protocol.md +27 -1
  72. package/docs/table-schema.md +5 -0
  73. package/package.json +2 -2
@@ -1,10 +1,4 @@
1
- /**
2
- * Client-side shape of a server-authored table schema.
3
- *
4
- * Mirrors App\Table\TableSchema / App\Table\Column. Declared in a .ts file
5
- * rather than inside the SFC so pages can import the types (`<script setup>`
6
- * cannot export type declarations).
7
- */
1
+ import { TableRecord } from './tableTypes';
8
2
  export type SchemaColumnType = 'text' | 'select' | 'money' | 'numeric' | 'badge' | 'date' | 'boolean' | 'image' | 'icon' | 'color';
9
3
  /**
10
4
  * An action button rendered inside every cell of a column.
@@ -174,7 +168,7 @@ export interface TableSchema {
174
168
  * Returns undefined rather than throwing when any segment is missing, so a
175
169
  * null relation renders the column's placeholder instead of blowing up.
176
170
  */
177
- export declare function getPath(record: Record<string, any>, path: string): unknown;
171
+ export declare function getPath(record: TableRecord, path: string): unknown;
178
172
  /**
179
173
  * The cell actions that apply to one row, after `hiddenWhen` is resolved
180
174
  * against it. Dot-paths are supported, matching every other field reference.
@@ -210,16 +204,39 @@ export interface SchemaBulkAction {
210
204
  confirm?: boolean;
211
205
  confirmMessage?: string;
212
206
  }
207
+ /**
208
+ * The callbacks a page injects for what a schema can declare but not do.
209
+ *
210
+ * A schema is serialised Inertia data, so it can say a column is editable or
211
+ * that an action exists, but never how to persist or perform it; these are
212
+ * the shapes the page's handler maps take.
213
+ */
214
+ /** Saves an in-place edit: the row, the column key, and the new value. */
215
+ export type CellHandler = {
216
+ bivarianceHack(record: TableRecord, column: string, value: unknown): unknown;
217
+ }['bivarianceHack'];
218
+ /** Services a column's cell action for one row. */
219
+ export type CellActionHandler = {
220
+ bivarianceHack(record: TableRecord, action: SchemaColumnAction): unknown;
221
+ }['bivarianceHack'];
222
+ /** Services a row action the table cannot perform itself. */
223
+ export type RowActionHandler = {
224
+ bivarianceHack(record: TableRecord, action: SchemaRowAction): unknown;
225
+ }['bivarianceHack'];
226
+ /** Services a bulk action over the selection. */
227
+ export type BulkActionHandler = {
228
+ bivarianceHack(records: TableRecord[], action: SchemaBulkAction): unknown;
229
+ }['bivarianceHack'];
213
230
  /** The actions this row offers, after its own visibility fields are read. */
214
- export declare function visibleRowActions(actions: SchemaRowAction[] | undefined, record: Record<string, any>): SchemaRowAction[];
215
- export declare function visibleCellActions(column: SchemaColumn, record: Record<string, any>): SchemaColumnAction[];
231
+ export declare function visibleRowActions(actions: SchemaRowAction[] | undefined, record: TableRecord): SchemaRowAction[];
232
+ export declare function visibleCellActions(column: SchemaColumn, record: TableRecord): SchemaColumnAction[];
216
233
  /**
217
234
  * Fill a URL template from a record: `/panel/contacts/{id}/organization/{organization.id}`.
218
235
  *
219
236
  * Returns null when a placeholder cannot be resolved — a link containing a
220
237
  * literal `{organization.id}` would 404, so no link is the better answer.
221
238
  */
222
- export declare function resolveRecordUrl(template: string | null | undefined, record: Record<string, any>): string | null;
239
+ export declare function resolveRecordUrl(template: string | null | undefined, record: TableRecord): string | null;
223
240
  /**
224
241
  * Empty form value per filter type — the shape useListFilters needs so it can
225
242
  * tell "unset" from "set", and so reset() restores the right type.
@@ -8,6 +8,19 @@ export interface TableColumn {
8
8
  headerClass?: string;
9
9
  cellClass?: string;
10
10
  }
11
- export type TableRecord = Record<string, any>;
11
+ /**
12
+ * One row of table data, as the server sends it. Cell values are `unknown`
13
+ * and narrowed where they are read — the schema, not the row type, says what
14
+ * each column holds.
15
+ */
16
+ export type TableRecord = Record<string, unknown>;
17
+ /** A row's primary key, as the server sends it. */
18
+ export type RecordId = string | number;
19
+ /**
20
+ * The row's `id`, for the calls that key a request on it (a delete, a bulk
21
+ * post). Rows arrive untyped, so the shape is asserted here — once — rather
22
+ * than at every call site.
23
+ */
24
+ export declare function recordId(record: TableRecord): RecordId;
12
25
  /** Columns carry either `key` or `name`; every lookup goes through this. */
13
26
  export declare function columnName(column: TableColumn): string | undefined;
@@ -1,14 +1,15 @@
1
- import { SchemaRowAction, SchemaBulkAction, TableSchema } from './tableSchema';
2
- type Row = Record<string, any>;
1
+ import { SchemaRowAction, SchemaBulkAction, TableSchema, RowActionHandler, BulkActionHandler } from './tableSchema';
2
+ import { TableRecord } from './tableTypes';
3
+ type Row = TableRecord;
3
4
  interface ActionOptions {
4
5
  schema: () => TableSchema;
5
6
  /** Appended to record links so a drawer preserves the current list state. */
6
- queryParams: () => Record<string, any>;
7
+ queryParams: () => Record<string, unknown>;
7
8
  /** The resource's own word for one record ('movie'), for the delete dialog. */
8
9
  recordLabel: () => string | undefined;
9
10
  /** Row actions the schema declares but the table cannot service itself. */
10
- rowActionHandlers: () => Record<string, (record: Row, action: SchemaRowAction) => unknown>;
11
- bulkActionHandlers: () => Record<string, (records: Row[], action: SchemaBulkAction) => unknown>;
11
+ rowActionHandlers: () => Record<string, RowActionHandler>;
12
+ bulkActionHandlers: () => Record<string, BulkActionHandler>;
12
13
  }
13
14
  /**
14
15
  * Runs the actions a schema declares, and owns the confirmations they need.
@@ -20,29 +21,11 @@ export declare function useSchemaActions(options: ActionOptions): {
20
21
  rowActionsFor: (record: Row) => SchemaRowAction[];
21
22
  runRowAction: (action: SchemaRowAction, record: Row) => void;
22
23
  deletion: import('vue').ComputedRef<{
23
- state: {
24
- open: boolean;
25
- record: Row | null;
26
- plan: {
27
- blocked: boolean;
28
- protected?: string[] | undefined;
29
- nested?: {
30
- label: string;
31
- type?: string | undefined;
32
- children?: any[] | undefined;
33
- }[] | undefined;
34
- counts?: Record<string, number> | undefined;
35
- linkCounts?: Record<string, number> | undefined;
36
- soft?: boolean | undefined;
37
- } | null;
38
- loading: boolean;
39
- deleting: boolean;
40
- error: string | null;
41
- };
42
- request: (record: Row) => Promise<void>;
24
+ state: import('../..').DeleteConfirmationState<TableRecord>;
25
+ request: (record: TableRecord) => Promise<void>;
43
26
  confirm: () => Promise<void>;
44
27
  close: () => void;
45
- message: ((record: Row) => string) | undefined;
28
+ message: ((record: TableRecord) => string) | undefined;
46
29
  }>;
47
30
  deleteLabel: import('vue').ComputedRef<string>;
48
31
  runBulkAction: (action: SchemaBulkAction, records: Row[]) => void;
@@ -10,22 +10,24 @@
10
10
  * Deliberately not tied to generated resources: most pages that delete things
11
11
  * are hand-written, and they are the ones still calling confirm().
12
12
  */
13
+ /** One record in the tree of what a delete would take with it. */
14
+ export interface DeletionNode {
15
+ label: string;
16
+ type?: string;
17
+ children?: DeletionNode[];
18
+ }
13
19
  export interface DeletionPlan {
14
20
  blocked: boolean;
15
21
  /** Human labels of the records standing in the way. */
16
22
  protected?: string[];
17
23
  /** Nested tree of what would be deleted. */
18
- nested?: Array<{
19
- label: string;
20
- type?: string;
21
- children?: any[];
22
- }>;
24
+ nested?: DeletionNode[];
23
25
  counts?: Record<string, number>;
24
26
  linkCounts?: Record<string, number>;
25
27
  /** True when the record is only trashed, so nothing is at stake. */
26
28
  soft?: boolean;
27
29
  }
28
- export interface DeleteConfirmationState<T = any> {
30
+ export interface DeleteConfirmationState<T = unknown> {
29
31
  open: boolean;
30
32
  record: T | null;
31
33
  plan: DeletionPlan | null;
@@ -35,7 +37,7 @@ export interface DeleteConfirmationState<T = any> {
35
37
  deleting: boolean;
36
38
  error: string | null;
37
39
  }
38
- export interface UseDeleteConfirmationOptions<T = any> {
40
+ export interface UseDeleteConfirmationOptions<T = unknown> {
39
41
  /**
40
42
  * Where to ask what deleting `record` would do. Omit for a plain
41
43
  * confirmation.
@@ -55,30 +57,8 @@ export interface UseDeleteConfirmationOptions<T = any> {
55
57
  /** Shown when there is no preview to describe consequences with. */
56
58
  message?: (record: T) => string;
57
59
  }
58
- export declare function useDeleteConfirmation<T = any>(options: UseDeleteConfirmationOptions<T>): {
59
- state: {
60
- open: boolean;
61
- record: import('vue').UnwrapRef<T> | null;
62
- plan: {
63
- blocked: boolean;
64
- protected?: string[]
65
- /** Nested tree of what would be deleted. */
66
- | undefined;
67
- nested?: {
68
- label: string;
69
- type?: string | undefined;
70
- children?: any[] | undefined;
71
- }[] | undefined;
72
- counts?: Record<string, number> | undefined;
73
- linkCounts?: Record<string, number>
74
- /** True when the record is only trashed, so nothing is at stake. */
75
- | undefined;
76
- soft?: boolean | undefined;
77
- } | null;
78
- loading: boolean;
79
- deleting: boolean;
80
- error: string | null;
81
- };
60
+ export declare function useDeleteConfirmation<T = unknown>(options: UseDeleteConfirmationOptions<T>): {
61
+ state: DeleteConfirmationState<T>;
82
62
  request: (record: T) => Promise<void>;
83
63
  confirm: () => Promise<void>;
84
64
  close: () => void;
@@ -40,6 +40,13 @@ export interface UseNestedDrawerFormReturn {
40
40
  closeForm: () => void;
41
41
  resetForm: () => void;
42
42
  getFormData: () => Record<string, unknown>;
43
+ /**
44
+ * Only the fields whose value differs from what the form opened with —
45
+ * what an edit submit should PATCH. Smaller conflict surface, honest audit
46
+ * trails: an untouched field cannot clobber a concurrent change. Create
47
+ * mode has no meaningful baseline, so callers use getFormData() there.
48
+ */
49
+ getChangedData: () => Record<string, unknown>;
43
50
  /**
44
51
  * True while the open form holds edits that have not been submitted —
45
52
  * what an "unsaved changes" guard asks before letting a navigation
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .ui-toggle-button[data-v-5920d123]{display:inline-flex;align-items:center;justify-content:center}.pm-editor .ProseMirror{outline:none;min-height:8rem}.pm-editor .ProseMirror>*+*{margin-top:.75rem}.pm-editor .ProseMirror h2{font-size:1.25rem;font-weight:600}.pm-editor .ProseMirror h3{font-size:1.125rem;font-weight:600}.pm-editor .ProseMirror h4,.pm-editor .ProseMirror h5,.pm-editor .ProseMirror h6,.pm-editor .ProseMirror strong{font-weight:600}.pm-editor .ProseMirror em{font-style:italic}.pm-editor .ProseMirror a{text-decoration:underline;color:inherit}.pm-editor .ProseMirror code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.875em;background:#f3f4f6;padding:.1em .3em;border-radius:.25rem}.pm-editor .ProseMirror pre{background:#f3f4f6;padding:.75rem;border-radius:.375rem;overflow-x:auto}.pm-editor .ProseMirror pre code{background:none;padding:0}.pm-editor .ProseMirror blockquote{border-left:3px solid rgb(209 213 219);padding-left:.75rem;color:#4b5563}.pm-editor .ProseMirror ul{list-style:disc;padding-left:1.25rem}.pm-editor .ProseMirror ol{list-style:decimal;padding-left:1.25rem}.pm-editor .ProseMirror li>*+*{margin-top:.25rem}.pm-editor .pm-placeholder:before{content:attr(data-placeholder);color:#d1d5db;float:left;height:0;pointer-events:none}.pm-editor .ProseMirror-selectednode{outline:2px solid rgb(59 130 246);border-radius:.375rem}.pm-editor .pm-dropcursor{background:#3b82f6}.pm-drag-handle{position:absolute;left:.5rem;display:none;align-items:center;justify-content:center;width:1rem;padding:.125rem;border-radius:.25rem;color:#d1d5db;cursor:grab;-webkit-user-select:none;user-select:none}.pm-drag-handle:hover{color:#6b7280;background:#f3f4f6}.pm-drag-handle:active{cursor:grabbing}.pm-drag-handle svg{width:.75rem;height:1.25rem;pointer-events:none}
1
+ .ui-toggle-button[data-v-92aa6553]{display:inline-flex;align-items:center;justify-content:center}.pm-editor .ProseMirror{outline:none;min-height:8rem}.pm-editor .ProseMirror>*+*{margin-top:.75rem}.pm-editor .ProseMirror h2{font-size:1.25rem;font-weight:600}.pm-editor .ProseMirror h3{font-size:1.125rem;font-weight:600}.pm-editor .ProseMirror h4,.pm-editor .ProseMirror h5,.pm-editor .ProseMirror h6,.pm-editor .ProseMirror strong{font-weight:600}.pm-editor .ProseMirror em{font-style:italic}.pm-editor .ProseMirror a{text-decoration:underline;color:inherit}.pm-editor .ProseMirror code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.875em;background:#f3f4f6;padding:.1em .3em;border-radius:.25rem}.pm-editor .ProseMirror pre{background:#f3f4f6;padding:.75rem;border-radius:.375rem;overflow-x:auto}.pm-editor .ProseMirror pre code{background:none;padding:0}.pm-editor .ProseMirror blockquote{border-left:3px solid rgb(209 213 219);padding-left:.75rem;color:#4b5563}.pm-editor .ProseMirror ul{list-style:disc;padding-left:1.25rem}.pm-editor .ProseMirror ol{list-style:decimal;padding-left:1.25rem}.pm-editor .ProseMirror li>*+*{margin-top:.25rem}.pm-editor .pm-placeholder:before{content:attr(data-placeholder);color:#d1d5db;float:left;height:0;pointer-events:none}.pm-editor .ProseMirror-selectednode{outline:2px solid rgb(59 130 246);border-radius:.375rem}.pm-editor .pm-dropcursor{background:#3b82f6}.pm-drag-handle{position:absolute;left:.5rem;display:none;align-items:center;justify-content:center;width:1rem;padding:.125rem;border-radius:.25rem;color:#d1d5db;cursor:grab;-webkit-user-select:none;user-select:none}.pm-drag-handle:hover{color:#6b7280;background:#f3f4f6}.pm-drag-handle:active{cursor:grabbing}.pm-drag-handle svg{width:.75rem;height:1.25rem;pointer-events:none}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @modufolio/panel — schema-driven admin panel components for
3
3
  * Inertia.js + Vue 3.
4
4
  */
5
- export declare const VERSION = "0.1.0";
5
+ export declare const VERSION = "0.2.0";
6
6
  export { createPanel, type CreatePanelOptions } from './plugin';
7
7
  export { apiFetch, ApiError, type ApiFetchOptions } from './Utils/apiFetch';
8
8
  export * from './Utils/optimistic';
@@ -31,6 +31,7 @@ export * from './Composables/useDragReorder';
31
31
  export * from './Composables/useLocalStoragePersistence';
32
32
  export * from './Composables/useUnsavedChangesWarning';
33
33
  export * from './Composables/useNestedDrawerForm';
34
+ export { useDrawerDirtyGuard } from './Components/Drawer/useDrawerDirtyGuard';
34
35
  export * from './Composables/useDeleteConfirmation';
35
36
  export * from './Composables/useReconciled';
36
37
  export { default as Button } from './Components/Core/Button.vue';
@@ -86,6 +87,10 @@ export { default as FieldGrid } from './Components/Fields/FieldGrid.vue';
86
87
  export { fieldsFromSpec, initialValues } from './Components/Fields/fieldsFromSpec';
87
88
  export { default as BlueprintForm } from './Components/Fields/BlueprintForm.vue';
88
89
  export { useFieldWidth, fieldWidthProp } from './Components/Fields/useFieldWidth';
90
+ export { default as FieldPrimitive } from './Components/Fields/FieldPrimitive.vue';
91
+ export { default as FieldLabel } from './Components/Fields/FieldLabel.vue';
92
+ export { default as FieldDescription } from './Components/Fields/FieldDescription.vue';
93
+ export { default as FieldMessage } from './Components/Fields/FieldMessage.vue';
89
94
  export { useBlueprint, defineBlueprint, evaluateCondition, registerFieldType, resolveFieldComponent, type FieldDef, type FieldType, type BuiltinFieldType, type OptionItem, type Condition, type ConditionTuple, type ConditionOperator, } from './Components/Fields/useBlueprint';
90
95
  export { required, min, max, email, url, pattern, integer, same, firstError, rulesFromSpec, type ValidationRule, } from './Components/Fields/validation';
91
96
  export { default as TextField } from './Components/Fields/TextField.vue';