@shamar/core 0.1.6 → 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 (71) hide show
  1. package/README.md +35 -5
  2. package/dist/actions.d.ts +9 -2
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +21 -8
  5. package/dist/actions.js.map +1 -1
  6. package/dist/content-max-width.d.ts +11 -4
  7. package/dist/content-max-width.d.ts.map +1 -1
  8. package/dist/content-max-width.js +27 -7
  9. package/dist/content-max-width.js.map +1 -1
  10. package/dist/default-per-page.d.ts +16 -0
  11. package/dist/default-per-page.d.ts.map +1 -0
  12. package/dist/default-per-page.js +31 -0
  13. package/dist/default-per-page.js.map +1 -0
  14. package/dist/field-registry.d.ts +55 -0
  15. package/dist/field-registry.d.ts.map +1 -0
  16. package/dist/field-registry.js +393 -0
  17. package/dist/field-registry.js.map +1 -0
  18. package/dist/field-values.d.ts +28 -0
  19. package/dist/field-values.d.ts.map +1 -0
  20. package/dist/field-values.js +193 -0
  21. package/dist/field-values.js.map +1 -0
  22. package/dist/fields.d.ts +112 -0
  23. package/dist/fields.d.ts.map +1 -0
  24. package/dist/fields.js +251 -0
  25. package/dist/fields.js.map +1 -0
  26. package/dist/form.d.ts +57 -9
  27. package/dist/form.d.ts.map +1 -1
  28. package/dist/form.js +122 -2
  29. package/dist/form.js.map +1 -1
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +8 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/infolist.d.ts +3 -1
  35. package/dist/infolist.d.ts.map +1 -1
  36. package/dist/infolist.js +44 -0
  37. package/dist/infolist.js.map +1 -1
  38. package/dist/media.d.ts +110 -0
  39. package/dist/media.d.ts.map +1 -0
  40. package/dist/media.js +6 -0
  41. package/dist/media.js.map +1 -0
  42. package/dist/page-content.d.ts +92 -0
  43. package/dist/page-content.d.ts.map +1 -0
  44. package/dist/page-content.js +128 -0
  45. package/dist/page-content.js.map +1 -0
  46. package/dist/page-registry.d.ts +16 -0
  47. package/dist/page-registry.d.ts.map +1 -0
  48. package/dist/page-registry.js +45 -0
  49. package/dist/page-registry.js.map +1 -0
  50. package/dist/page.d.ts +132 -0
  51. package/dist/page.d.ts.map +1 -0
  52. package/dist/page.js +222 -0
  53. package/dist/page.js.map +1 -0
  54. package/dist/panel.d.ts +46 -2
  55. package/dist/panel.d.ts.map +1 -1
  56. package/dist/panel.js +65 -2
  57. package/dist/panel.js.map +1 -1
  58. package/dist/registry.d.ts.map +1 -1
  59. package/dist/registry.js +2 -0
  60. package/dist/registry.js.map +1 -1
  61. package/dist/resource.d.ts +8 -1
  62. package/dist/resource.d.ts.map +1 -1
  63. package/dist/resource.js +10 -1
  64. package/dist/resource.js.map +1 -1
  65. package/dist/types.d.ts +77 -8
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/validation.d.ts +5 -0
  68. package/dist/validation.d.ts.map +1 -1
  69. package/dist/validation.js +91 -10
  70. package/dist/validation.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ import { form } from './form.js';
2
+ import { infolist } from './infolist.js';
3
+ import { table } from './table.js';
4
+ import type { FieldConfig, FormSchema, InfolistSchema, ListQuery, ResourceMeta, ResourceModel, TableSchema } from './types.js';
5
+ import type { PageRequestContext, PageSaveResult } from './page.js';
6
+ export type PageSectionKind = 'edge' | 'form' | 'table' | 'infolist';
7
+ export type PageSectionData = Record<string, unknown> | ((ctx: PageRequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>);
8
+ export type PageSectionRecord = Record<string, unknown> | ((ctx: PageRequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>);
9
+ export interface PageSectionMeta {
10
+ key: string;
11
+ title?: string;
12
+ kind: PageSectionKind;
13
+ /** Edge view name (edge sections). */
14
+ view?: string;
15
+ /** Form schema (form sections). */
16
+ form?: FormSchema;
17
+ fields?: FieldConfig[];
18
+ /** Resource-shaped meta for table sections. */
19
+ listResource?: ResourceMeta;
20
+ /** Resource-shaped meta for infolist sections. */
21
+ infolistResource?: ResourceMeta;
22
+ defaultPerPage?: number;
23
+ }
24
+ export interface PageEdgeSectionHandlers {
25
+ data?: PageSectionData;
26
+ }
27
+ export interface PageFormSectionHandlers {
28
+ fill?: (ctx: PageRequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
29
+ save: (data: Record<string, unknown>, ctx: PageRequestContext) => PageSaveResult | void | Promise<PageSaveResult | void>;
30
+ }
31
+ export interface PageTableSectionHandlers {
32
+ query?: Partial<ListQuery> | ((ctx: PageRequestContext) => Partial<ListQuery> | Promise<Partial<ListQuery>>);
33
+ }
34
+ export interface PageInfolistSectionHandlers {
35
+ record: PageSectionRecord;
36
+ }
37
+ export interface PageSectionHandlers {
38
+ edge?: PageEdgeSectionHandlers;
39
+ form?: PageFormSectionHandlers;
40
+ table?: PageTableSectionHandlers;
41
+ infolist?: PageInfolistSectionHandlers;
42
+ }
43
+ export declare class PageSectionDefinition {
44
+ readonly meta: PageSectionMeta;
45
+ readonly handlers: PageSectionHandlers;
46
+ constructor(meta: PageSectionMeta, handlers: PageSectionHandlers);
47
+ }
48
+ export interface PageEdgeSectionOptions {
49
+ title?: string;
50
+ view: string;
51
+ data?: PageSectionData;
52
+ }
53
+ export interface PageFormSectionOptions {
54
+ title?: string;
55
+ form: FormSchema | (() => FormSchema);
56
+ fill?: (ctx: PageRequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
57
+ save: (data: Record<string, unknown>, ctx: PageRequestContext) => PageSaveResult | void | Promise<PageSaveResult | void>;
58
+ }
59
+ export interface PageTableSectionOptions {
60
+ title?: string;
61
+ model: ResourceModel;
62
+ connection?: string;
63
+ recordTitleField?: string;
64
+ softDelete?: boolean | {
65
+ field?: string;
66
+ };
67
+ defaultPerPage?: number;
68
+ /** Resource slug used for row links (defaults to section key). */
69
+ linkResourceSlug?: string;
70
+ table: TableSchema | (() => TableSchema);
71
+ query?: PageTableSectionHandlers['query'];
72
+ }
73
+ export interface PageInfolistSectionOptions {
74
+ title?: string;
75
+ infolist: InfolistSchema | (() => InfolistSchema);
76
+ record: PageSectionRecord;
77
+ }
78
+ export declare class PageContentBuilder {
79
+ private readonly sections;
80
+ edge(key: string, options: PageEdgeSectionOptions): this;
81
+ form(key: string, options: PageFormSectionOptions): this;
82
+ table(key: string, options: PageTableSectionOptions): this;
83
+ infolist(key: string, options: PageInfolistSectionOptions): this;
84
+ build(): PageSectionDefinition[];
85
+ }
86
+ /**
87
+ * Declarative page sections — forms, tables, infolists, and Edge blocks on one `Page`.
88
+ */
89
+ export declare function pageContent(fn: (builder: PageContentBuilder) => void): PageSectionDefinition[];
90
+ /** Convenience re-exports for section builders. */
91
+ export { form, table, infolist };
92
+ //# sourceMappingURL=page-content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-content.d.ts","sourceRoot":"","sources":["../src/page-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAErE,MAAM,MAAM,eAAe,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CAAC,GAAG,EAAE,kBAAkB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAE9F,MAAM,MAAM,iBAAiB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,CAAC,CAAC,GAAG,EAAE,kBAAkB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,CACL,GAAG,EAAE,kBAAkB,KACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,IAAI,EAAE,CACJ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,kBAAkB,KACpB,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EACF,OAAO,CAAC,SAAS,CAAC,GAClB,CAAC,CAAC,GAAG,EAAE,kBAAkB,KAAK,OAAO,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;CACrF;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,uBAAuB,CAAC;IAC/B,IAAI,CAAC,EAAE,uBAAuB,CAAC;IAC/B,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC,QAAQ,CAAC,EAAE,2BAA2B,CAAC;CACxC;AAED,qBAAa,qBAAqB;IAE9B,QAAQ,CAAC,IAAI,EAAE,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB;gBAD7B,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,mBAAmB;CAEzC;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,CAAC,MAAM,UAAU,CAAC,CAAC;IACtC,IAAI,CAAC,EAAE,CACL,GAAG,EAAE,kBAAkB,KACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChE,IAAI,EAAE,CACJ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,kBAAkB,KACpB,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,aAAa,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,wBAAwB,CAAC,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,cAAc,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC;IAClD,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAkED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+B;IAExD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI;IAexD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,IAAI;IAsBxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAuB,GAAG,IAAI;IAmB1D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,GAAG,IAAI;IAkBhE,KAAK,IAAI,qBAAqB,EAAE;CAGjC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,qBAAqB,EAAE,CAI9F;AAED,mDAAmD;AACnD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,128 @@
1
+ import { form } from './form.js';
2
+ import { infolist } from './infolist.js';
3
+ import { table } from './table.js';
4
+ export class PageSectionDefinition {
5
+ meta;
6
+ handlers;
7
+ constructor(meta, handlers) {
8
+ this.meta = meta;
9
+ this.handlers = handlers;
10
+ }
11
+ }
12
+ function resolveFormSchema(value) {
13
+ return typeof value === 'function' ? value() : value;
14
+ }
15
+ function resolveTableSchema(value) {
16
+ return typeof value === 'function' ? value() : value;
17
+ }
18
+ function resolveInfolistSchema(value) {
19
+ return typeof value === 'function' ? value() : value;
20
+ }
21
+ function buildTableListResource(sectionKey, options, tableSchema) {
22
+ const searchableFields = tableSchema.columns
23
+ .filter((column) => column.searchable)
24
+ .map((column) => column.name);
25
+ return {
26
+ slug: options.linkResourceSlug ?? sectionKey,
27
+ label: options.title ?? sectionKey,
28
+ singularLabel: options.title ?? sectionKey,
29
+ model: options.model,
30
+ connection: options.connection,
31
+ recordTitleField: options.recordTitleField ?? 'name',
32
+ fields: [],
33
+ form: { fields: [], sections: [], schema: [] },
34
+ columns: tableSchema.columns,
35
+ infolist: { entries: [], sections: [], schema: [] },
36
+ hasExplicitInfolist: false,
37
+ actions: [],
38
+ searchableFields: [...new Set(searchableFields)],
39
+ defaultSort: tableSchema.defaultSort,
40
+ defaultFilters: tableSchema.defaultFilters,
41
+ defaultGroupBy: tableSchema.defaultGroupBy,
42
+ softDelete: options.softDelete,
43
+ defaultPerPage: options.defaultPerPage,
44
+ };
45
+ }
46
+ function buildInfolistResource(sectionKey, options, schema) {
47
+ return {
48
+ slug: sectionKey,
49
+ label: options.title ?? sectionKey,
50
+ singularLabel: options.title ?? sectionKey,
51
+ model: 'PageRecord',
52
+ recordTitleField: 'id',
53
+ fields: [],
54
+ form: { fields: [], sections: [], schema: [] },
55
+ columns: [],
56
+ infolist: schema,
57
+ hasExplicitInfolist: true,
58
+ actions: [],
59
+ searchableFields: [],
60
+ };
61
+ }
62
+ export class PageContentBuilder {
63
+ sections = [];
64
+ edge(key, options) {
65
+ this.sections.push(new PageSectionDefinition({
66
+ key,
67
+ title: options.title,
68
+ kind: 'edge',
69
+ view: options.view,
70
+ }, { edge: { data: options.data } }));
71
+ return this;
72
+ }
73
+ form(key, options) {
74
+ const formSchema = resolveFormSchema(options.form);
75
+ this.sections.push(new PageSectionDefinition({
76
+ key,
77
+ title: options.title,
78
+ kind: 'form',
79
+ form: formSchema,
80
+ fields: formSchema.fields,
81
+ }, {
82
+ form: {
83
+ fill: options.fill,
84
+ save: options.save,
85
+ },
86
+ }));
87
+ return this;
88
+ }
89
+ table(key, options) {
90
+ const tableSchema = resolveTableSchema(options.table);
91
+ this.sections.push(new PageSectionDefinition({
92
+ key,
93
+ title: options.title,
94
+ kind: 'table',
95
+ listResource: buildTableListResource(key, options, tableSchema),
96
+ defaultPerPage: options.defaultPerPage,
97
+ }, {
98
+ table: options.query ? { query: options.query } : undefined,
99
+ }));
100
+ return this;
101
+ }
102
+ infolist(key, options) {
103
+ const schema = resolveInfolistSchema(options.infolist);
104
+ this.sections.push(new PageSectionDefinition({
105
+ key,
106
+ title: options.title,
107
+ kind: 'infolist',
108
+ infolistResource: buildInfolistResource(key, options, schema),
109
+ }, {
110
+ infolist: { record: options.record },
111
+ }));
112
+ return this;
113
+ }
114
+ build() {
115
+ return [...this.sections];
116
+ }
117
+ }
118
+ /**
119
+ * Declarative page sections — forms, tables, infolists, and Edge blocks on one `Page`.
120
+ */
121
+ export function pageContent(fn) {
122
+ const builder = new PageContentBuilder();
123
+ fn(builder);
124
+ return builder.build();
125
+ }
126
+ /** Convenience re-exports for section builders. */
127
+ export { form, table, infolist };
128
+ //# sourceMappingURL=page-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-content.js","sourceRoot":"","sources":["../src/page-content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { PageMeta } from './page.js';
2
+ import type { Page, PageClass } from './page.js';
3
+ export declare class PageRegistry {
4
+ private readonly meta;
5
+ private readonly classes;
6
+ constructor(pageClasses?: PageClass[]);
7
+ all(): PageMeta[];
8
+ get(slug: string): PageMeta | undefined;
9
+ require(slug: string): PageMeta;
10
+ pageClass(slug: string): PageClass | undefined;
11
+ has(slug: string): boolean;
12
+ /** Pages visible in panel navigation. */
13
+ navigationItems(): PageMeta[];
14
+ }
15
+ export type { Page };
16
+ //# sourceMappingURL=page-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-registry.d.ts","sourceRoot":"","sources":["../src/page-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEjD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;gBAE5C,WAAW,GAAE,SAAS,EAAO;IAWzC,GAAG,IAAI,QAAQ,EAAE;IAIjB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ;IAM/B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,yCAAyC;IACzC,eAAe,IAAI,QAAQ,EAAE;CAU9B;AAED,YAAY,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,45 @@
1
+ export class PageRegistry {
2
+ meta = new Map();
3
+ classes = new Map();
4
+ constructor(pageClasses = []) {
5
+ for (const PageClass of pageClasses) {
6
+ const configured = PageClass.configure();
7
+ if (this.meta.has(configured.slug)) {
8
+ throw new Error(`Duplicate Shamar page slug: ${configured.slug}`);
9
+ }
10
+ this.meta.set(configured.slug, configured);
11
+ this.classes.set(configured.slug, PageClass);
12
+ }
13
+ }
14
+ all() {
15
+ return [...this.meta.values()];
16
+ }
17
+ get(slug) {
18
+ return this.meta.get(slug);
19
+ }
20
+ require(slug) {
21
+ const meta = this.get(slug);
22
+ if (!meta)
23
+ throw new Error(`Unknown Shamar page: ${slug}`);
24
+ return meta;
25
+ }
26
+ pageClass(slug) {
27
+ return this.classes.get(slug);
28
+ }
29
+ has(slug) {
30
+ return this.meta.has(slug);
31
+ }
32
+ /** Pages visible in panel navigation. */
33
+ navigationItems() {
34
+ return this.all()
35
+ .filter((page) => !page.navigationHidden)
36
+ .sort((a, b) => {
37
+ const sortA = a.navigationSort ?? Number.POSITIVE_INFINITY;
38
+ const sortB = b.navigationSort ?? Number.POSITIVE_INFINITY;
39
+ if (sortA !== sortB)
40
+ return sortA - sortB;
41
+ return a.label.localeCompare(b.label);
42
+ });
43
+ }
44
+ }
45
+ //# sourceMappingURL=page-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-registry.js","sourceRoot":"","sources":["../src/page-registry.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,YAAY;IACN,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACnC,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAExD,YAAY,cAA2B,EAAE;QACvC,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC;YACzC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,+BAA+B,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,yCAAyC;IACzC,eAAe;QACb,OAAO,IAAI,CAAC,GAAG,EAAE;aACd,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;aACxC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACb,MAAM,KAAK,GAAG,CAAC,CAAC,cAAc,IAAI,MAAM,CAAC,iBAAiB,CAAC;YAC3D,MAAM,KAAK,GAAG,CAAC,CAAC,cAAc,IAAI,MAAM,CAAC,iBAAiB,CAAC;YAC3D,IAAI,KAAK,KAAK,KAAK;gBAAE,OAAO,KAAK,GAAG,KAAK,CAAC;YAC1C,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACP,CAAC;CACF"}
package/dist/page.d.ts ADDED
@@ -0,0 +1,132 @@
1
+ import { form } from './form.js';
2
+ import { infolist } from './infolist.js';
3
+ import { table } from './table.js';
4
+ import type { PageSectionDefinition, PageSectionMeta } from './page-content.js';
5
+ import type { ActionConfig, FieldConfig, FormSchema, ResourceMeta, ResourceModel, ShamarUser } from './types.js';
6
+ export type PageKind = 'custom' | 'form' | 'list' | 'composite';
7
+ /** Minimal request context passed into page lifecycle hooks. */
8
+ export interface PageRequestContext {
9
+ user?: ShamarUser | null;
10
+ panelId?: string;
11
+ /** Raw request body / query bag when available. */
12
+ input?: Record<string, unknown>;
13
+ }
14
+ export interface PageSaveResult {
15
+ message?: string;
16
+ /** Redirect path after save (absolute or panel-relative). */
17
+ redirectTo?: string;
18
+ }
19
+ export interface PageActionResult {
20
+ message?: string;
21
+ redirectTo?: string;
22
+ }
23
+ /**
24
+ * Configured page metadata for routing, nav, and rendering.
25
+ */
26
+ export interface PageMeta {
27
+ kind: PageKind;
28
+ slug: string;
29
+ label: string;
30
+ navigationGroup?: string;
31
+ navigationSubGroup?: string;
32
+ navigationSort?: number;
33
+ navigationHidden?: boolean;
34
+ icon?: string;
35
+ /** Edge view override (defaults by kind). */
36
+ view?: string;
37
+ contentMaxWidth?: string;
38
+ defaultPerPage?: number;
39
+ actions: ActionConfig[];
40
+ /** Form schema when kind is `form`. */
41
+ form?: FormSchema;
42
+ fields?: FieldConfig[];
43
+ /**
44
+ * Resource-shaped meta for list pages so adapter.list + index UI can be reused.
45
+ * Only set when kind is `list`.
46
+ */
47
+ listResource?: ResourceMeta;
48
+ /** Declarative sections when kind is `composite`. */
49
+ sections?: PageSectionMeta[];
50
+ }
51
+ /**
52
+ * Filament-style panel page — custom view, declarative sections, or header actions.
53
+ * Use {@link FormPage} or {@link ListPage} for a single form or table shortcut.
54
+ */
55
+ export declare abstract class Page {
56
+ static slug: string;
57
+ static label: string;
58
+ static navigationGroup?: string;
59
+ static navigationSubGroup?: string;
60
+ static navigationSort?: number;
61
+ static navigationHidden?: boolean;
62
+ static icon?: string;
63
+ /** Edge view name (e.g. `pages/welcome`). Defaults to `shamar::page`. */
64
+ static view?: string;
65
+ static contentMaxWidth?: string;
66
+ static canAccess(_user: ShamarUser | null | undefined): boolean;
67
+ static headerActions(): ActionConfig[];
68
+ /**
69
+ * Extra locals for the page view (merged after fill for form pages).
70
+ */
71
+ static mount(_ctx: PageRequestContext): Record<string, unknown> | Promise<Record<string, unknown>>;
72
+ static handleAction(_name: string, _ctx: PageRequestContext): PageActionResult | null | undefined | Promise<PageActionResult | null | undefined>;
73
+ /**
74
+ * Declarative sections (forms, tables, infolists, Edge blocks).
75
+ * When non-empty, the page renders as a composite layout (`shamar::page-sections`).
76
+ */
77
+ static content(): PageSectionDefinition[];
78
+ static configure(): PageMeta;
79
+ }
80
+ /**
81
+ * Filament-style settings / form page — mount/fill data, save on POST.
82
+ */
83
+ export declare abstract class FormPage extends Page {
84
+ static form(): ReturnType<typeof form>;
85
+ /**
86
+ * Initial form state. Defaults to `{}`; override to load a singleton / settings row.
87
+ */
88
+ static fill(_ctx: PageRequestContext): Record<string, unknown> | Promise<Record<string, unknown>>;
89
+ /**
90
+ * Persist form data. Required for useful form pages.
91
+ */
92
+ static save(_data: Record<string, unknown>, _ctx: PageRequestContext): PageSaveResult | void | Promise<PageSaveResult | void>;
93
+ static configure(): PageMeta;
94
+ }
95
+ /**
96
+ * Canonical singleton settings page for panels (branding, prefs, app config).
97
+ * Same API as {@link FormPage}; defaults to a `7xl` content column so every
98
+ * project’s settings screen shares one layout.
99
+ */
100
+ export declare abstract class SettingsPage extends FormPage {
101
+ static contentMaxWidth: string;
102
+ static view: string;
103
+ }
104
+ /**
105
+ * Standalone list page — reuses Resource table builders + list UI without full CRUD routes.
106
+ */
107
+ export declare abstract class ListPage extends Page {
108
+ static model: ResourceModel;
109
+ static connection?: string;
110
+ static recordTitleField: string;
111
+ static softDelete?: boolean | {
112
+ field?: string;
113
+ };
114
+ static defaultPerPage?: number;
115
+ static table(): ReturnType<typeof table>;
116
+ /**
117
+ * Optional show/infolist for row clicks (`GET /:slug/:id`).
118
+ * Defaults to entries derived from {@link table} columns.
119
+ */
120
+ static infolist(): ReturnType<typeof infolist> | undefined;
121
+ static listActions(): ActionConfig[];
122
+ static configure(): PageMeta;
123
+ }
124
+ export type PageClass = typeof Page;
125
+ /** True when `value` is FormPage or a subclass (including {@link SettingsPage}). */
126
+ export declare function isFormPage(value: PageClass): value is typeof FormPage;
127
+ /** True when `value` is SettingsPage or a subclass. */
128
+ export declare function isSettingsPage(value: PageClass): value is typeof SettingsPage;
129
+ export declare function isListPage(value: PageClass): value is typeof ListPage;
130
+ /** True when the page declares one or more {@link Page.content} sections. */
131
+ export declare function hasPageSections(value: PageClass): boolean;
132
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"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"}
package/dist/page.js ADDED
@@ -0,0 +1,222 @@
1
+ import { form } from './form.js';
2
+ import { columnsToInfolistSchema } from './infolist.js';
3
+ import { table } from './table.js';
4
+ /**
5
+ * Filament-style panel page — custom view, declarative sections, or header actions.
6
+ * Use {@link FormPage} or {@link ListPage} for a single form or table shortcut.
7
+ */
8
+ export class Page {
9
+ static slug = 'page';
10
+ static label = 'Page';
11
+ static navigationGroup;
12
+ static navigationSubGroup;
13
+ static navigationSort;
14
+ static navigationHidden;
15
+ static icon;
16
+ /** Edge view name (e.g. `pages/welcome`). Defaults to `shamar::page`. */
17
+ static view;
18
+ static contentMaxWidth;
19
+ static canAccess(_user) {
20
+ return true;
21
+ }
22
+ static headerActions() {
23
+ return [];
24
+ }
25
+ /**
26
+ * Extra locals for the page view (merged after fill for form pages).
27
+ */
28
+ static mount(_ctx) {
29
+ return {};
30
+ }
31
+ static handleAction(_name, _ctx) {
32
+ return null;
33
+ }
34
+ /**
35
+ * Declarative sections (forms, tables, infolists, Edge blocks).
36
+ * When non-empty, the page renders as a composite layout (`shamar::page-sections`).
37
+ */
38
+ static content() {
39
+ return [];
40
+ }
41
+ static configure() {
42
+ const definitions = this.content();
43
+ if (definitions.length > 0) {
44
+ const sections = definitions.map((section) => {
45
+ const meta = { ...section.meta };
46
+ if (meta.kind === 'infolist' && meta.infolistResource) {
47
+ meta.infolistResource = {
48
+ ...meta.infolistResource,
49
+ slug: `${this.slug}__${meta.key}`,
50
+ };
51
+ }
52
+ return meta;
53
+ });
54
+ return {
55
+ kind: 'composite',
56
+ slug: this.slug,
57
+ label: this.label,
58
+ navigationGroup: this.navigationGroup,
59
+ navigationSubGroup: this.navigationSubGroup,
60
+ navigationSort: this.navigationSort,
61
+ navigationHidden: this.navigationHidden,
62
+ icon: this.icon,
63
+ view: this.view ?? 'shamar::page-sections',
64
+ contentMaxWidth: this.contentMaxWidth,
65
+ actions: this.headerActions(),
66
+ sections,
67
+ };
68
+ }
69
+ return {
70
+ kind: 'custom',
71
+ slug: this.slug,
72
+ label: this.label,
73
+ navigationGroup: this.navigationGroup,
74
+ navigationSubGroup: this.navigationSubGroup,
75
+ navigationSort: this.navigationSort,
76
+ navigationHidden: this.navigationHidden,
77
+ icon: this.icon,
78
+ view: this.view,
79
+ contentMaxWidth: this.contentMaxWidth,
80
+ actions: this.headerActions(),
81
+ };
82
+ }
83
+ }
84
+ /**
85
+ * Filament-style settings / form page — mount/fill data, save on POST.
86
+ */
87
+ export class FormPage extends Page {
88
+ static form() {
89
+ return form(() => undefined);
90
+ }
91
+ /**
92
+ * Initial form state. Defaults to `{}`; override to load a singleton / settings row.
93
+ */
94
+ static fill(_ctx) {
95
+ return {};
96
+ }
97
+ /**
98
+ * Persist form data. Required for useful form pages.
99
+ */
100
+ static save(_data, _ctx) {
101
+ return undefined;
102
+ }
103
+ static configure() {
104
+ const formSchema = this.form();
105
+ return {
106
+ ...super.configure(),
107
+ kind: 'form',
108
+ form: formSchema,
109
+ fields: formSchema.fields,
110
+ view: this.view ?? 'shamar::page-form',
111
+ };
112
+ }
113
+ }
114
+ /**
115
+ * Canonical singleton settings page for panels (branding, prefs, app config).
116
+ * Same API as {@link FormPage}; defaults to a `7xl` content column so every
117
+ * project’s settings screen shares one layout.
118
+ */
119
+ export class SettingsPage extends FormPage {
120
+ static contentMaxWidth = '7xl';
121
+ static view = 'shamar::page-form';
122
+ }
123
+ /**
124
+ * Standalone list page — reuses Resource table builders + list UI without full CRUD routes.
125
+ */
126
+ export class ListPage extends Page {
127
+ static model = 'Record';
128
+ static connection;
129
+ static recordTitleField = 'name';
130
+ static softDelete;
131
+ static defaultPerPage;
132
+ static table() {
133
+ return table(() => undefined);
134
+ }
135
+ /**
136
+ * Optional show/infolist for row clicks (`GET /:slug/:id`).
137
+ * Defaults to entries derived from {@link table} columns.
138
+ */
139
+ static infolist() {
140
+ return undefined;
141
+ }
142
+ static listActions() {
143
+ // Read-only list by default — no create/edit/delete chrome.
144
+ return [];
145
+ }
146
+ static configure() {
147
+ const tableSchema = this.table();
148
+ const actionList = this.listActions();
149
+ const explicitInfolist = this.infolist();
150
+ const infolistSchema = explicitInfolist ?? columnsToInfolistSchema(tableSchema.columns);
151
+ const searchableFields = tableSchema.columns
152
+ .filter((column) => column.searchable)
153
+ .map((column) => column.name);
154
+ const listResource = {
155
+ slug: this.slug,
156
+ label: this.label,
157
+ singularLabel: this.label,
158
+ model: this.model,
159
+ connection: this.connection,
160
+ navigationGroup: this.navigationGroup,
161
+ navigationSubGroup: this.navigationSubGroup,
162
+ navigationSort: this.navigationSort,
163
+ recordTitleField: this.recordTitleField,
164
+ icon: this.icon,
165
+ fields: [],
166
+ form: { fields: [], sections: [], schema: [] },
167
+ columns: tableSchema.columns,
168
+ infolist: infolistSchema,
169
+ hasExplicitInfolist: explicitInfolist !== undefined,
170
+ actions: actionList,
171
+ searchableFields: [...new Set(searchableFields)],
172
+ defaultSort: tableSchema.defaultSort,
173
+ defaultFilters: tableSchema.defaultFilters,
174
+ defaultGroupBy: tableSchema.defaultGroupBy,
175
+ softDelete: this.softDelete,
176
+ contentMaxWidth: this.contentMaxWidth,
177
+ defaultPerPage: this.defaultPerPage,
178
+ };
179
+ return {
180
+ ...super.configure(),
181
+ kind: 'list',
182
+ defaultPerPage: this.defaultPerPage,
183
+ actions: [...this.headerActions(), ...actionList.filter((a) => a.placement === 'header')],
184
+ view: this.view ?? 'shamar::page-list',
185
+ listResource,
186
+ };
187
+ }
188
+ }
189
+ /** True when `value` is FormPage or a subclass (including {@link SettingsPage}). */
190
+ export function isFormPage(value) {
191
+ let current = value;
192
+ while (typeof current === 'function') {
193
+ if (current === FormPage || current === SettingsPage)
194
+ return true;
195
+ current = Object.getPrototypeOf(current);
196
+ }
197
+ return false;
198
+ }
199
+ /** True when `value` is SettingsPage or a subclass. */
200
+ export function isSettingsPage(value) {
201
+ let current = value;
202
+ while (typeof current === 'function') {
203
+ if (current === SettingsPage)
204
+ return true;
205
+ current = Object.getPrototypeOf(current);
206
+ }
207
+ return false;
208
+ }
209
+ export function isListPage(value) {
210
+ let current = value;
211
+ while (typeof current === 'function') {
212
+ if (current === ListPage)
213
+ return true;
214
+ current = Object.getPrototypeOf(current);
215
+ }
216
+ return false;
217
+ }
218
+ /** True when the page declares one or more {@link Page.content} sections. */
219
+ export function hasPageSections(value) {
220
+ return value.content().length > 0;
221
+ }
222
+ //# sourceMappingURL=page.js.map