@dosgato/templating 0.0.38 → 0.0.41

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,16 @@ 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<C extends Component = Component> {
235
+ component: C;
236
+ html: string;
237
+ }
192
238
  export declare abstract class Page<DataType extends PageData = any, FetchedType = any, RenderContextType extends ContextBase = any> extends Component<DataType, FetchedType, RenderContextType> {
193
239
  pagePath: string;
194
240
  /**
@@ -198,5 +244,5 @@ export declare abstract class Page<DataType extends PageData = any, FetchedType
198
244
  */
199
245
  headContent: string;
200
246
  protected passError(e: Error, path: string): void;
201
- constructor(page: PageRecord<DataType>);
247
+ constructor(page: PageRecord<DataType>, editMode: boolean);
202
248
  }
package/dist/component.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { isNotBlank } from 'txstate-utils';
2
- import { editBar, newBar } from './editbar.js';
3
2
  import { ResourceProvider } from './provider.js';
4
3
  /**
5
4
  * This is the primary templating class to build your templates. Subclass it and provide
@@ -11,11 +10,12 @@ import { ResourceProvider } from './provider.js';
11
10
  export class Component extends ResourceProvider {
12
11
  // the constructor is part of the recursive hydration mechanism: constructing
13
12
  // a Component will also construct/hydrate all its child components
14
- constructor(data, path, parent) {
13
+ constructor(data, path, parent, editMode) {
15
14
  super();
16
15
  // properties for use during hydration, you do not have to provide these when
17
16
  // building a template, but you can use them in the functions you do provide
18
17
  this.areas = new Map();
18
+ this.editMode = editMode;
19
19
  this.parent = parent;
20
20
  const { areas, ...ownData } = data;
21
21
  this.data = ownData;
@@ -42,7 +42,7 @@ export class Component extends ResourceProvider {
42
42
  * you may add them to your this.areas map, e.g.
43
43
  * `this.areas.get('myarea').push(new Component(inheritedData, this.path + '/myarea/inherit1', this))`
44
44
  */
45
- async fetch(editMode) {
45
+ async fetch() {
46
46
  return undefined;
47
47
  }
48
48
  /**
@@ -60,7 +60,7 @@ export class Component extends ResourceProvider {
60
60
  * the context received from the parent, but use it sparingly since it will stall the process.
61
61
  * Try to do all asynchronous work in the fetch phase.
62
62
  */
63
- setContext(renderCtxFromParent, editMode) {
63
+ setContext(renderCtxFromParent) {
64
64
  return renderCtxFromParent;
65
65
  }
66
66
  /**
@@ -91,6 +91,11 @@ export class Component extends ResourceProvider {
91
91
  passError(e, path) {
92
92
  this.parent?.passError(e, path);
93
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.component.inheritedFrom && opts?.hideInheritBars ? [c.html] : [c.component.editBar(), c.html]).join('');
98
+ }
94
99
  /**
95
100
  * During rendering, each component should determine the CSS blocks that it needs. This may
96
101
  * change depending on the data. For instance, if you need some CSS to style up an image, but
@@ -111,13 +116,12 @@ export class Component extends ResourceProvider {
111
116
  }
112
117
  /**
113
118
  * Components may override this function to give their edit bars a custom
114
- * label instead of using the templateName property
119
+ * label instead of using the template name
115
120
  *
116
121
  * For instance, you could return this.data.title
117
122
  */
118
123
  editLabel() {
119
- const This = this.constructor;
120
- return This.templateName;
124
+ return undefined;
121
125
  }
122
126
  /**
123
127
  * Components may override this function to give their edit bars a custom
@@ -134,7 +138,7 @@ export class Component extends ResourceProvider {
134
138
  * return "Add Layout"
135
139
  */
136
140
  newLabel(areaName) {
137
- return 'Add Content';
141
+ return undefined;
138
142
  }
139
143
  /**
140
144
  * Components may override this function to give their new bars a custom
@@ -149,9 +153,11 @@ export class Component extends ResourceProvider {
149
153
  * Generally should not be overridden - override editLabel and editClass instead
150
154
  */
151
155
  editBar(opts = {}) {
152
- opts.label ?? (opts.label = this.editLabel());
156
+ opts.label ?? (opts.label = this.editLabel() ?? this.autoLabel());
153
157
  opts.extraClass ?? (opts.extraClass = this.editClass());
154
- 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);
155
161
  }
156
162
  /**
157
163
  * Components may override this function to provide a custom new bar
@@ -159,14 +165,16 @@ export class Component extends ResourceProvider {
159
165
  * Generally should not be overridden - override newLabel and newClass instead
160
166
  */
161
167
  newBar(areaName, opts = {}) {
162
- opts.label ?? (opts.label = this.newLabel(areaName));
168
+ opts.label ?? (opts.label = this.newLabel(areaName) ?? this.autoNewLabel());
163
169
  opts.extraClass ?? (opts.extraClass = this.newClass(areaName));
164
- return newBar([this.path, 'areas', areaName].filter(isNotBlank).join('.'), 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);
165
173
  }
166
174
  }
167
175
  export class Page extends Component {
168
- constructor(page) {
169
- super(page.data, '', undefined);
176
+ constructor(page, editMode) {
177
+ super(page.data, '', undefined, editMode);
170
178
  this.pagePath = page.path;
171
179
  }
172
180
  passError(e, path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dosgato/templating",
3
- "version": "0.0.38",
3
+ "version": "0.0.41",
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
- }