@dosgato/templating 0.0.37 → 0.0.40

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.
@@ -1,4 +1,3 @@
1
- import { EditBarOpts } from './editbar.js';
2
1
  import { ResourceProvider } from './provider.js';
3
2
  import { APIClient } from './render.js';
4
3
  /**
@@ -9,8 +8,15 @@ import { APIClient } from './render.js';
9
8
  * parent and child components linked.
10
9
  */
11
10
  export declare abstract class Component<DataType extends ComponentData = any, FetchedType = any, RenderContextType extends ContextBase = any> extends ResourceProvider {
11
+ /**
12
+ * Provide this when you create a template to identify what you are defining.
13
+ */
12
14
  static templateKey: string;
13
- static templateName: string;
15
+ /**
16
+ * These functions will be provided by the rendering server
17
+ */
18
+ static editBar: (path: string, opts: EditBarOpts) => string;
19
+ static newBar: (path: string, opts: EditBarOpts) => string;
14
20
  areas: Map<string, Component<any, any, any>[]>;
15
21
  data: Omit<DataType, 'areas'>;
16
22
  fetched: FetchedType;
@@ -19,6 +25,26 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
19
25
  parent?: Component;
20
26
  page?: Page;
21
27
  hadError: boolean;
28
+ autoLabel: () => string;
29
+ autoNewLabel: () => string;
30
+ /**
31
+ * When hydrating an inherited component, the renderer will set this to the id of the page it
32
+ * came from. You may use this information in any of the phases to alter your behavior if needed.
33
+ *
34
+ * For instance, you may decide that your fetch function needs some extra information from the
35
+ * originating page instead of the page you're being inherited into (your `this.page` will
36
+ * be the page currently being rendered, NOT the page the inheritable component came from).
37
+ *
38
+ * This property is also used to alter the edit bar. Inherited components may never be edited
39
+ * except on their original page, so the edit bar will render with a link to the original page.
40
+ */
41
+ inheritedFrom?: string;
42
+ /**
43
+ * This property will be set during page render and you may refer to it at any time to
44
+ * determine whether you are doing your work in edit mode or regular rendering mode.
45
+ * The editBar and newBar methods will automatically use it to blank out the editing UI.
46
+ */
47
+ editMode: boolean;
22
48
  /**
23
49
  * The rendering server will provide an instance of the APIClient interface so that
24
50
  * you can run any API GraphQL query you like in your `fetch` function. There are also
@@ -49,6 +75,13 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
49
75
  * other Component instances.
50
76
  */
51
77
  getAncestorPageData: () => Promise<PageData[]>;
78
+ /**
79
+ * Some components may be inheritable to subpages within the same site. For instance, a site's
80
+ * social media links may appear on every page's footer. To accomplish this in your template,
81
+ * you need to fetch the data in your fetch phase and then call this function within your fetch
82
+ * to let the renderer know it needs to hydrate them and include them in the render.
83
+ */
84
+ registerInherited: (area: string, components: ComponentData[], top?: true) => void;
52
85
  /**
53
86
  * The first phase of rendering a component is the fetch phase. Each component may
54
87
  * provide a fetch method that looks up data it needs from external sources. This step
@@ -63,7 +96,7 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
63
96
  * you may add them to your this.areas map, e.g.
64
97
  * `this.areas.get('myarea').push(new Component(inheritedData, this.path + '/myarea/inherit1', this))`
65
98
  */
66
- fetch(editMode: boolean): Promise<FetchedType>;
99
+ fetch(): Promise<FetchedType>;
67
100
  /**
68
101
  * The second phase of rendering a component is the context phase. This step is TOP-DOWN and
69
102
  * NON-MUTATING. Each component will receive the parent component's context and then pass a
@@ -79,14 +112,14 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
79
112
  * the context received from the parent, but use it sparingly since it will stall the process.
80
113
  * Try to do all asynchronous work in the fetch phase.
81
114
  */
82
- setContext(renderCtxFromParent: RenderContextType, editMode: boolean): RenderContextType | Promise<RenderContextType>;
115
+ setContext(renderCtxFromParent: RenderContextType): RenderContextType | Promise<RenderContextType>;
83
116
  /**
84
117
  * The final phase of rendering a component is the render phase. This step is BOTTOM-UP -
85
118
  * components at the bottom of the hierarchy will be rendered first, and the result of the
86
119
  * render will be passed to parent components so that the HTML can be included during the
87
120
  * render of the parent component.
88
121
  */
89
- abstract render(renderedAreas: Map<string, string[]>, editMode: boolean): string;
122
+ abstract render(renderedAreas: Map<string, RenderedComponent[]>): string;
90
123
  /**
91
124
  * Sometimes pages are requested with an alternate extension like .rss or .ics. In these
92
125
  * situations, each component should consider whether it should output anything. For
@@ -100,7 +133,7 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
100
133
  * will be skipped.
101
134
  */
102
135
  renderVariation(extension: string, renderedAreas: Map<string, string>): string;
103
- constructor(data: DataType, path: string, parent: Component | undefined);
136
+ constructor(data: DataType, path: string, parent: Component | undefined, editMode: boolean);
104
137
  /**
105
138
  * For logging errors during rendering without crashing the render. If your fetch, setContext,
106
139
  * render, or renderVariation functions throw, the error will be logged but the page render will
@@ -108,6 +141,9 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
108
141
  */
109
142
  logError(e: Error): void;
110
143
  protected passError(e: Error, path: string): void;
144
+ renderComponents(components?: RenderedComponent[], opts?: {
145
+ hideInheritBars?: boolean;
146
+ }): string;
111
147
  /**
112
148
  * During rendering, each component should determine the CSS blocks that it needs. This may
113
149
  * change depending on the data. For instance, if you need some CSS to style up an image, but
@@ -124,11 +160,11 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
124
160
  jsBlocks(): string[];
125
161
  /**
126
162
  * Components may override this function to give their edit bars a custom
127
- * label instead of using the templateName property
163
+ * label instead of using the template name
128
164
  *
129
165
  * For instance, you could return this.data.title
130
166
  */
131
- editLabel(): string;
167
+ editLabel(): undefined;
132
168
  /**
133
169
  * Components may override this function to give their edit bars a custom
134
170
  * CSS class
@@ -141,7 +177,7 @@ export declare abstract class Component<DataType extends ComponentData = any, Fe
141
177
  * For instance, an area that only accepts 'layout' components could
142
178
  * return "Add Layout"
143
179
  */
144
- newLabel(areaName: string): string;
180
+ newLabel(areaName: string): undefined;
145
181
  /**
146
182
  * Components may override this function to give their new bars a custom
147
183
  * CSS class
@@ -189,6 +225,17 @@ export interface ContextBase {
189
225
  */
190
226
  headerLevel: number;
191
227
  }
228
+ export interface EditBarOpts {
229
+ extraClass?: string;
230
+ label?: string;
231
+ editMode?: boolean;
232
+ inheritedFrom?: string;
233
+ }
234
+ export interface RenderedComponent {
235
+ inherited: boolean;
236
+ html: string;
237
+ editbar: string;
238
+ }
192
239
  export declare abstract class Page<DataType extends PageData = any, FetchedType = any, RenderContextType extends ContextBase = any> extends Component<DataType, FetchedType, RenderContextType> {
193
240
  pagePath: string;
194
241
  /**
@@ -198,5 +245,5 @@ export declare abstract class Page<DataType extends PageData = any, FetchedType
198
245
  */
199
246
  headContent: string;
200
247
  protected passError(e: Error, path: string): void;
201
- constructor(page: PageRecord<DataType>);
248
+ constructor(page: PageRecord<DataType>, editMode: boolean);
202
249
  }
package/dist/component.js CHANGED
@@ -1,4 +1,4 @@
1
- import { editBar, newBar } from './editbar.js';
1
+ import { isNotBlank } from 'txstate-utils';
2
2
  import { ResourceProvider } from './provider.js';
3
3
  /**
4
4
  * This is the primary templating class to build your templates. Subclass it and provide
@@ -10,11 +10,12 @@ import { ResourceProvider } from './provider.js';
10
10
  export class Component extends ResourceProvider {
11
11
  // the constructor is part of the recursive hydration mechanism: constructing
12
12
  // a Component will also construct/hydrate all its child components
13
- constructor(data, path, parent) {
13
+ constructor(data, path, parent, editMode) {
14
14
  super();
15
15
  // properties for use during hydration, you do not have to provide these when
16
16
  // building a template, but you can use them in the functions you do provide
17
17
  this.areas = new Map();
18
+ this.editMode = editMode;
18
19
  this.parent = parent;
19
20
  const { areas, ...ownData } = data;
20
21
  this.data = ownData;
@@ -41,7 +42,7 @@ export class Component extends ResourceProvider {
41
42
  * you may add them to your this.areas map, e.g.
42
43
  * `this.areas.get('myarea').push(new Component(inheritedData, this.path + '/myarea/inherit1', this))`
43
44
  */
44
- async fetch(editMode) {
45
+ async fetch() {
45
46
  return undefined;
46
47
  }
47
48
  /**
@@ -59,7 +60,7 @@ export class Component extends ResourceProvider {
59
60
  * the context received from the parent, but use it sparingly since it will stall the process.
60
61
  * Try to do all asynchronous work in the fetch phase.
61
62
  */
62
- setContext(renderCtxFromParent, editMode) {
63
+ setContext(renderCtxFromParent) {
63
64
  return renderCtxFromParent;
64
65
  }
65
66
  /**
@@ -90,6 +91,11 @@ export class Component extends ResourceProvider {
90
91
  passError(e, path) {
91
92
  this.parent?.passError(e, path);
92
93
  }
94
+ // helper function to help you print an area, but you can also override this if you
95
+ // need to do something advanced like wrap each component in a div
96
+ renderComponents(components = [], opts) {
97
+ return components.flatMap(c => c.inherited && opts?.hideInheritBars ? [c.html] : [c.editbar, c.html]).join('');
98
+ }
93
99
  /**
94
100
  * During rendering, each component should determine the CSS blocks that it needs. This may
95
101
  * change depending on the data. For instance, if you need some CSS to style up an image, but
@@ -110,13 +116,12 @@ export class Component extends ResourceProvider {
110
116
  }
111
117
  /**
112
118
  * Components may override this function to give their edit bars a custom
113
- * label instead of using the templateName property
119
+ * label instead of using the template name
114
120
  *
115
121
  * For instance, you could return this.data.title
116
122
  */
117
123
  editLabel() {
118
- const This = this.constructor;
119
- return This.templateName;
124
+ return undefined;
120
125
  }
121
126
  /**
122
127
  * Components may override this function to give their edit bars a custom
@@ -133,7 +138,7 @@ export class Component extends ResourceProvider {
133
138
  * return "Add Layout"
134
139
  */
135
140
  newLabel(areaName) {
136
- return 'Add Content';
141
+ return undefined;
137
142
  }
138
143
  /**
139
144
  * Components may override this function to give their new bars a custom
@@ -148,9 +153,11 @@ export class Component extends ResourceProvider {
148
153
  * Generally should not be overridden - override editLabel and editClass instead
149
154
  */
150
155
  editBar(opts = {}) {
151
- opts.label ?? (opts.label = this.editLabel());
156
+ opts.label ?? (opts.label = this.editLabel() ?? this.autoLabel());
152
157
  opts.extraClass ?? (opts.extraClass = this.editClass());
153
- return editBar(this.path, opts);
158
+ opts.editMode ?? (opts.editMode = this.editMode);
159
+ opts.inheritedFrom ?? (opts.inheritedFrom = this.inheritedFrom);
160
+ return Component.editBar(this.path, opts);
154
161
  }
155
162
  /**
156
163
  * Components may override this function to provide a custom new bar
@@ -158,14 +165,16 @@ export class Component extends ResourceProvider {
158
165
  * Generally should not be overridden - override newLabel and newClass instead
159
166
  */
160
167
  newBar(areaName, opts = {}) {
161
- opts.label ?? (opts.label = this.newLabel(areaName));
168
+ opts.label ?? (opts.label = this.newLabel(areaName) ?? this.autoNewLabel());
162
169
  opts.extraClass ?? (opts.extraClass = this.newClass(areaName));
163
- return newBar(this.path + '.' + areaName, opts);
170
+ opts.editMode ?? (opts.editMode = this.editMode);
171
+ opts.inheritedFrom ?? (opts.inheritedFrom = this.inheritedFrom);
172
+ return Component.newBar([this.path, 'areas', areaName].filter(isNotBlank).join('.'), opts);
164
173
  }
165
174
  }
166
175
  export class Page extends Component {
167
- constructor(page) {
168
- super(page.data, '/', undefined);
176
+ constructor(page, editMode) {
177
+ super(page.data, '', undefined, editMode);
169
178
  this.pagePath = page.path;
170
179
  }
171
180
  passError(e, path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosgato/templating",
3
- "version": "0.0.37",
3
+ "version": "0.0.40",
4
4
  "description": "A library to support building templates for dosgato CMS.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -18,7 +18,7 @@
18
18
  "txstate-utils": "^1.7.1"
19
19
  },
20
20
  "devDependencies": {
21
- "eslint-config-standard-with-typescript": "^21.0.1",
21
+ "eslint-config-standard-with-typescript": "^22.0.0",
22
22
  "typescript": "^4.4.2"
23
23
  },
24
24
  "repository": {
package/dist/editbar.d.ts DELETED
@@ -1,11 +0,0 @@
1
- export interface EditBarOpts {
2
- extraClass?: string;
3
- label?: string;
4
- editMode?: boolean;
5
- }
6
- export declare function editBar(path: string, opts: EditBarOpts & {
7
- label: string;
8
- }): string;
9
- export declare function newBar(path: string, opts: EditBarOpts & {
10
- label: string;
11
- }): string;
package/dist/editbar.js DELETED
@@ -1,23 +0,0 @@
1
- import { htmlEncode, randomid } from 'txstate-utils';
2
- export function editBar(path, opts) {
3
- if (!opts.editMode)
4
- return '';
5
- const id = randomid();
6
- return `
7
- <div class="dg-edit-bar ${opts.extraClass ?? ''}" data-path="${htmlEncode(path)}" draggable="true" ondragstart="window.dgEditing.drag(event)" ondragover="window.dgEditing.over(event)" ondragend="window.dgEditing.drop(event)">
8
- <span id="${id}" class="dg-edit-bar-label">${htmlEncode(opts.label)}</span>
9
- <button onclick="window.dgEditing.edit(event)" aria-describedby="${id}">Edit</button>
10
- <button onclick="window.dgEditing.move(event)" aria-describedby="${id}">Move</button>
11
- <button onclick="window.dgEditing.del(event)" aria-describedby="${id}">Trash</button>
12
- </div>
13
- `.trim();
14
- }
15
- export function newBar(path, opts) {
16
- if (!opts.editMode)
17
- return '';
18
- return `
19
- <div role="button" onclick="window.dgEditing.create(event)" class="dg-new-bar ${opts.extraClass ?? ''}" data-path="${htmlEncode(path)}">
20
- ${htmlEncode(opts.label)}
21
- </div>
22
- `.trim();
23
- }