@silexlabs/silex 3.4.0 → 3.5.1-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 (91) hide show
  1. package/.silex.js +0 -3
  2. package/dist/client/client/config.d.ts +8 -0
  3. package/dist/client/client/grapesjs/cms/DataSource.d.ts +43 -0
  4. package/dist/client/client/grapesjs/cms/blocks.d.ts +2 -0
  5. package/dist/client/client/grapesjs/cms/buttons.d.ts +2 -0
  6. package/dist/client/client/grapesjs/cms/collection-pages.d.ts +4 -0
  7. package/dist/client/client/grapesjs/cms/config.d.ts +9 -0
  8. package/dist/client/client/grapesjs/cms/filters.d.ts +3 -0
  9. package/dist/client/client/grapesjs/cms/index.d.ts +44 -0
  10. package/dist/client/client/grapesjs/cms/liquid.d.ts +59 -0
  11. package/dist/client/client/grapesjs/cms/page-settings.d.ts +6 -0
  12. package/dist/client/client/grapesjs/cms/publication.d.ts +60 -0
  13. package/dist/client/client/grapesjs/cms/site-settings.d.ts +3 -0
  14. package/dist/client/client/grapesjs/cms/states.d.ts +8 -0
  15. package/dist/client/client/grapesjs/cms/traits.d.ts +8 -0
  16. package/dist/client/client/grapesjs/settings.d.ts +12 -3
  17. package/dist/client/client/grapesjs/view-buttons.d.ts +2 -1
  18. package/dist/client/client/utils.d.ts +1 -0
  19. package/dist/client/constants.d.ts +2 -1
  20. package/dist/client/css/admin.css +88 -14
  21. package/dist/client/index.html +2 -2
  22. package/dist/client/js/main.js +1968 -264
  23. package/dist/client/types.d.ts +3 -1
  24. package/dist/plugins/client/plugins/client/template.js +1 -1
  25. package/dist/plugins/client/plugins/client/template.js.map +1 -1
  26. package/dist/plugins/client/types.d.ts +3 -1
  27. package/dist/plugins/client/types.js +11 -8
  28. package/dist/plugins/client/types.js.map +1 -1
  29. package/dist/plugins/server/constants.d.ts +2 -1
  30. package/dist/plugins/server/constants.js +3 -2
  31. package/dist/plugins/server/constants.js.map +1 -1
  32. package/dist/plugins/server/plugins/server/FtpConnector.js +15 -20
  33. package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
  34. package/dist/plugins/server/plugins/server/GitlabConnector.js +34 -7
  35. package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
  36. package/dist/plugins/server/server/connectors/FsStorage.js +16 -20
  37. package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
  38. package/dist/plugins/server/server/utils/websiteDataSerialization.d.ts +4 -0
  39. package/dist/plugins/server/server/utils/websiteDataSerialization.js +21 -12
  40. package/dist/plugins/server/server/utils/websiteDataSerialization.js.map +1 -1
  41. package/dist/plugins/server/types.d.ts +3 -1
  42. package/dist/plugins/server/types.js +12 -9
  43. package/dist/plugins/server/types.js.map +1 -1
  44. package/dist/server/constants.js +3 -2
  45. package/dist/server/server/connectors/FsStorage.js +14 -18
  46. package/dist/server/server/utils/websiteDataSerialization.js +16 -11
  47. package/dist/server/types.js +6 -2
  48. package/package.json +15 -10
  49. package/src/css/_variables.scss +2 -1
  50. package/src/css/grapesjs-plugins.scss +31 -0
  51. package/src/css/grapesjs.scss +15 -0
  52. package/src/css/page-panel.scss +1 -1
  53. package/src/css/silex.scss +64 -5
  54. package/src/ts/client/config.ts +23 -0
  55. package/src/ts/client/grapesjs/breadcrumbs.ts +3 -2
  56. package/src/ts/client/grapesjs/cms/DataSource.ts +333 -0
  57. package/src/ts/client/grapesjs/cms/blocks.ts +124 -0
  58. package/src/ts/client/grapesjs/cms/buttons.ts +137 -0
  59. package/src/ts/client/grapesjs/cms/collection-pages.ts +205 -0
  60. package/src/ts/client/grapesjs/cms/config.ts +47 -0
  61. package/src/ts/client/grapesjs/cms/filters.ts +53 -0
  62. package/src/ts/client/grapesjs/cms/index.ts +130 -0
  63. package/src/ts/client/grapesjs/cms/liquid.mock.ts +181 -0
  64. package/src/ts/client/grapesjs/cms/liquid.test.ts +239 -0
  65. package/src/ts/client/grapesjs/cms/liquid.ts +325 -0
  66. package/src/ts/client/grapesjs/cms/page-settings.ts +285 -0
  67. package/src/ts/client/grapesjs/cms/publication.test.ts +300 -0
  68. package/src/ts/client/grapesjs/cms/publication.ts +708 -0
  69. package/src/ts/client/grapesjs/cms/site-settings.ts +79 -0
  70. package/src/ts/client/grapesjs/cms/states.ts +125 -0
  71. package/src/ts/client/grapesjs/cms/traits.ts +95 -0
  72. package/src/ts/client/grapesjs/flex.ts +59 -39
  73. package/src/ts/client/grapesjs/img.ts +1 -1
  74. package/src/ts/client/grapesjs/index.ts +77 -4
  75. package/src/ts/client/grapesjs/internal-links.ts +4 -2
  76. package/src/ts/client/grapesjs/page-panel.ts +8 -7
  77. package/src/ts/client/grapesjs/sectors.ts +18 -10
  78. package/src/ts/client/grapesjs/settings.ts +252 -101
  79. package/src/ts/client/grapesjs/storage.ts +6 -1
  80. package/src/ts/client/grapesjs/view-buttons.ts +39 -3
  81. package/src/ts/client/utils.ts +15 -2
  82. package/src/ts/constants.ts +2 -1
  83. package/src/ts/plugins/client/template.ts +1 -1
  84. package/src/ts/plugins/server/FtpConnector.test.ts +1 -0
  85. package/src/ts/plugins/server/FtpConnector.ts +20 -23
  86. package/src/ts/plugins/server/GitlabConnector.ts +40 -10
  87. package/src/ts/server/connectors/FsStorage.test.ts +3 -2
  88. package/src/ts/server/connectors/FsStorage.ts +21 -23
  89. package/src/ts/server/utils/websiteDataSerialization.test.ts +45 -13
  90. package/src/ts/server/utils/websiteDataSerialization.ts +23 -14
  91. package/src/ts/types.ts +6 -2
@@ -60,11 +60,70 @@
60
60
  }
61
61
 
62
62
  // full screen settings
63
- .settings-dialog > div {
64
- max-width: none;
65
- width: 100%;
63
+ .settings-dialog {
64
+ position: fixed;
65
+ top: 0;
66
+ left: 0;
67
+ width: 100vw;
66
68
  height: 100vh;
69
+ z-index: $z-dialog-inline;
70
+ pointer-events: auto;
67
71
  overflow: auto;
72
+ display: flex;
73
+ flex-direction: column;
74
+
75
+ .settings-content {
76
+ flex: 1;
77
+ display: flex;
78
+ flex-direction: column;
79
+ background-color: var(--primaryColor);
80
+ overflow: hidden;
81
+ }
82
+ .settings-header {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: space-between;
86
+ padding: 16px 20px;
87
+ border-bottom: 1px solid var(--gjs-border-color, #ddd);
88
+ }
89
+ .settings-header h3 {
90
+ margin: 0;
91
+ font-size: 18px;
92
+ }
93
+ .settings-close {
94
+ color: inherit;
95
+ background: none;
96
+ border: none;
97
+ font-size: 24px;
98
+ cursor: pointer;
99
+ padding: 0;
100
+ width: 30px;
101
+ height: 30px;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ }
106
+ .settings-body {
107
+ flex: 1;
108
+ overflow: auto;
109
+ padding: 0;
110
+ }
111
+ .settings-body .silex-form {
112
+ height: 100%;
113
+ display: flex;
114
+ flex-direction: column;
115
+ }
116
+ .settings-body .silex-sidebar-dialog {
117
+ flex: 1;
118
+ }
119
+ footer {
120
+ position: fixed;
121
+ z-index: 10;
122
+ bottom: 0;
123
+ left: 0;
124
+ width: 100%;
125
+ padding: 20px 0;
126
+ }
68
127
  form.silex-form {
69
128
  padding-bottom: 100px; // leave space for fixed footer
70
129
  textarea {
@@ -91,7 +150,7 @@
91
150
  .silex-version {
92
151
  float: left;
93
152
  padding: 0 15px;
94
- font-size: x-small;
153
+ font-size: small;
95
154
  }
96
155
  }
97
156
  .CodeMirror {
@@ -164,7 +223,7 @@ input.silex-button[type="submit"] {
164
223
  margin: 5px;
165
224
  }
166
225
  main {
167
- width: 100%;
226
+ width: 93%;
168
227
  }
169
228
  }
170
229
 
@@ -29,6 +29,7 @@ import { addSection, removeSection } from './grapesjs/settings'
29
29
  // Plugins
30
30
  import publishCustomCodeBlock from './publish-custom-code-block'
31
31
  import publishFonts from './publish-fonts'
32
+ import { EleventyPluginOptions } from './grapesjs/cms'
32
33
 
33
34
  /**
34
35
  * @fileoverview Silex client side config
@@ -118,6 +119,27 @@ export class ClientConfig extends Config {
118
119
  fontsServerUrl = 'https://fonts.googleapis.com'
119
120
  fontsApiUrl = 'https://www.googleapis.com'
120
121
 
122
+ /**
123
+ * CMS configuration options
124
+ * @default Default CMS configuration with enabled: true
125
+ */
126
+ cmsConfig: Partial<EleventyPluginOptions> & { enabled?: boolean } = {
127
+ enabled: true,
128
+ enable11ty: true,
129
+ cacheBuster: false,
130
+ dataSources: [],
131
+ dir: {
132
+ input: '',
133
+ html: '',
134
+ assets: 'assets',
135
+ css: 'css',
136
+ },
137
+ urls: {
138
+ css: '/css',
139
+ assets: '/assets',
140
+ },
141
+ }
142
+
121
143
  /**
122
144
  * Init GrapesJS config which depend on the config file properties
123
145
  */
@@ -179,6 +201,7 @@ export class ClientConfig extends Config {
179
201
  * Add a section to the settings dialog
180
202
  */
181
203
  addSettings(section: SettingsSection, siteOrPage: 'site' | 'page', position: 'first' | 'last' | number = 'last') {
204
+ console.warn('addSettings API is deprecated, use the command instead:')
182
205
  addSection(section, siteOrPage, position)
183
206
  }
184
207
 
@@ -47,9 +47,10 @@ export default function (editor, options) {
47
47
 
48
48
  editor.on('component:selected style:target component:drag:end', () => renderBreadcrumbs())
49
49
  function renderBreadcrumbs() {
50
- let component = editor.getSelected() ?? editor.Pages.getSelected().getMainComponent()
50
+ const selectedPage = editor.Pages?.getSelected()
51
+ let component = editor.getSelected() ?? selectedPage?.getMainComponent()
51
52
 
52
- if(!breadcrumbsContainer) return
53
+ if(!breadcrumbsContainer || !component) return
53
54
 
54
55
  // Clear the breadcrumbs container
55
56
  breadcrumbsContainer.innerHTML = ''
@@ -0,0 +1,333 @@
1
+ import { DataSourceType, Field, IDataSource, Type, addDataSource, addFilters, removeFilters } from '@silexlabs/grapesjs-data-source'
2
+ import { EleventyPluginOptions, Silex11tyPluginWebsiteSettings } from './index'
3
+ import { Editor } from 'grapesjs'
4
+ import { i18nFilters } from './filters'
5
+ import { ClientEvent } from '../../events'
6
+
7
+ //import { cmdPauseAutoSave } from '@silexlabs/silex/src/ts/client/grapesjs/storage'
8
+ const cmdPauseAutoSave = 'pause-auto-save'
9
+
10
+ export default function(editor: Editor, opts: EleventyPluginOptions): void {
11
+ editor.on(`
12
+ ${ ClientEvent.SETTINGS_SAVE_END }
13
+ storage:after:load
14
+ `, () => {
15
+ updateFilters(editor, opts)
16
+ })
17
+ updateFilters(editor, opts)
18
+ if(opts.enable11ty) {
19
+ // Add the 11ty data source using the new functional API
20
+ const ds = new EleventyDataSource()
21
+ editor.runCommand(cmdPauseAutoSave)
22
+
23
+ // Add the data source
24
+ addDataSource(ds)
25
+
26
+ // Wait for the next tick to avoid triggering a save
27
+ setTimeout(() => {
28
+ editor.stopCommand(cmdPauseAutoSave)
29
+ })
30
+ }
31
+ }
32
+
33
+ function updateFilters(editor: Editor, opts: EleventyPluginOptions) {
34
+ const settings = (editor.getModel().get('settings') || {}) as Silex11tyPluginWebsiteSettings
35
+ if(settings.eleventyI18n) {
36
+ addFilters(i18nFilters)
37
+ } else if (!opts.i18nPlugin) {
38
+ removeFilters(i18nFilters)
39
+ }
40
+ }
41
+
42
+ export const EleventyDataSourceId = 'eleventy'
43
+
44
+ export class EleventyDataSource implements IDataSource {
45
+ /**
46
+ * Unique identifier of the data source
47
+ * This is used to retrieve the data source from the editor
48
+ */
49
+ public id = EleventyDataSourceId
50
+ public label = 'Eleventy'
51
+ public url = ''
52
+ public type = 'graphql' as DataSourceType
53
+ public method = 'POST'
54
+ public headers = {}
55
+ public hidden = true
56
+ public readonly = true
57
+
58
+ private eventListeners: Record<string, ((...args: unknown[]) => void)[]> = {}
59
+
60
+ // Simple event handling
61
+ on(event: string, callback: (...args: unknown[]) => void): void {
62
+ if (!this.eventListeners[event]) {
63
+ this.eventListeners[event] = []
64
+ }
65
+ this.eventListeners[event].push(callback)
66
+ }
67
+
68
+ off(event: string, callback?: (...args: unknown[]) => void): void {
69
+ if (!this.eventListeners[event]) return
70
+ if (callback) {
71
+ this.eventListeners[event] = this.eventListeners[event].filter(cb => cb !== callback)
72
+ } else {
73
+ this.eventListeners[event] = []
74
+ }
75
+ }
76
+
77
+ trigger(event: string, ...args: unknown[]): void {
78
+ if (!this.eventListeners[event]) return
79
+ this.eventListeners[event].forEach(callback => callback(...args))
80
+ }
81
+
82
+ /**
83
+ * Implement IDatasource
84
+ */
85
+ async connect(): Promise<void> {}
86
+ isConnected(): boolean { return true }
87
+
88
+ /**
89
+ * Implement IDatasource
90
+ */
91
+ getQuery(/*expressions: Expression[]*/): string { return '' }
92
+
93
+ /**
94
+ * Implement IDatasource
95
+ */
96
+ getTypes(): Type[] {
97
+ return [{
98
+ id: 'string',
99
+ label: 'String',
100
+ dataSourceId: 'eleventy',
101
+ fields: [],
102
+ }, {
103
+ id: 'number',
104
+ label: 'Number',
105
+ dataSourceId: 'eleventy',
106
+ fields: [],
107
+ }, {
108
+ id: 'date',
109
+ label: 'Date',
110
+ dataSourceId: 'eleventy',
111
+ fields: [],
112
+ }, {
113
+ id: 'page',
114
+ label: 'page',
115
+ dataSourceId: 'eleventy',
116
+ fields: [{
117
+ id: 'url',
118
+ label: 'url',
119
+ typeIds: ['string'],
120
+ kind: 'scalar',
121
+ dataSourceId: 'eleventy',
122
+ }, {
123
+ id: 'fileSlug',
124
+ label: 'fileSlug',
125
+ typeIds: ['string'],
126
+ kind: 'scalar',
127
+ dataSourceId: 'eleventy',
128
+ }, {
129
+ id: 'filePathStem',
130
+ label: 'filePathStem',
131
+ typeIds: ['string'],
132
+ kind: 'scalar',
133
+ dataSourceId: 'eleventy',
134
+ }, {
135
+ id: 'date',
136
+ label: 'date',
137
+ typeIds: ['date'],
138
+ kind: 'scalar',
139
+ dataSourceId: 'eleventy',
140
+ }, {
141
+ id: 'inputPath',
142
+ label: 'inputPath',
143
+ typeIds: ['string'],
144
+ kind: 'scalar',
145
+ dataSourceId: 'eleventy',
146
+ }, {
147
+ id: 'outputPath',
148
+ label: 'outputPath',
149
+ typeIds: ['string'],
150
+ kind: 'scalar',
151
+ dataSourceId: 'eleventy',
152
+ }, {
153
+ id: 'outputFileExtension',
154
+ label: 'outputFileExtension',
155
+ typeIds: ['string'],
156
+ kind: 'scalar',
157
+ dataSourceId: 'eleventy',
158
+ }, {
159
+ id: 'lang',
160
+ label: 'lang',
161
+ typeIds: ['string'],
162
+ kind: 'scalar',
163
+ dataSourceId: 'eleventy',
164
+ }],
165
+ }, {
166
+ id: 'pagination',
167
+ label: 'pagination',
168
+ dataSourceId: 'eleventy',
169
+ fields: [{
170
+ id: 'hrefs',
171
+ label: 'hrefs',
172
+ typeIds: ['string'],
173
+ kind: 'list',
174
+ dataSourceId: 'eleventy',
175
+ }, {
176
+ id: 'href',
177
+ label: 'href',
178
+ typeIds: ['paginationHref'],
179
+ kind: 'object',
180
+ dataSourceId: 'eleventy',
181
+ }, {
182
+ id: 'pageNumber',
183
+ label: 'pageNumber',
184
+ typeIds: ['number'],
185
+ kind: 'scalar',
186
+ dataSourceId: 'eleventy',
187
+ }, {
188
+ id: 'pages',
189
+ label: 'pages',
190
+ typeIds: ['page'],
191
+ kind: 'list',
192
+ dataSourceId: 'eleventy',
193
+ }],
194
+ }, {
195
+ id: 'paginationHref',
196
+ label: 'paginationHref',
197
+ dataSourceId: 'eleventy',
198
+ fields: [{
199
+ id: 'next',
200
+ label: 'next',
201
+ typeIds: ['string'],
202
+ kind: 'scalar',
203
+ dataSourceId: 'eleventy',
204
+ }, {
205
+ id: 'previous',
206
+ label: 'previous',
207
+ typeIds: ['string'],
208
+ kind: 'scalar',
209
+ dataSourceId: 'eleventy',
210
+ }, {
211
+ id: 'first',
212
+ label: 'first',
213
+ typeIds: ['string'],
214
+ kind: 'scalar',
215
+ dataSourceId: 'eleventy',
216
+ }, {
217
+ id: 'last',
218
+ label: 'last',
219
+ typeIds: ['string'],
220
+ kind: 'scalar',
221
+ dataSourceId: 'eleventy',
222
+ }],
223
+ }, {
224
+ id: 'eleventy',
225
+ label: 'eleventy',
226
+ dataSourceId: 'eleventy',
227
+ fields: [{
228
+ id: 'version',
229
+ label: 'version',
230
+ typeIds: ['string'],
231
+ kind: 'scalar',
232
+ dataSourceId: 'eleventy',
233
+ }, {
234
+ id: 'generator',
235
+ label: 'generator',
236
+ typeIds: ['string'],
237
+ kind: 'scalar',
238
+ dataSourceId: 'eleventy',
239
+ }, {
240
+ id: 'env',
241
+ label: 'env',
242
+ typeIds: ['env'],
243
+ kind: 'object',
244
+ dataSourceId: 'eleventy',
245
+ }]
246
+ }, {
247
+ id: 'env',
248
+ label: 'env',
249
+ dataSourceId: 'eleventy',
250
+ fields: [{
251
+ id: 'root',
252
+ label: 'root',
253
+ typeIds: ['string'],
254
+ kind: 'scalar',
255
+ dataSourceId: 'eleventy',
256
+ }, {
257
+ id: 'config',
258
+ label: 'config',
259
+ typeIds: ['string'],
260
+ kind: 'scalar',
261
+ dataSourceId: 'eleventy',
262
+ }, {
263
+ id: 'source',
264
+ label: 'source',
265
+ typeIds: ['string'],
266
+ kind: 'scalar',
267
+ dataSourceId: 'eleventy',
268
+ }, {
269
+ id: 'runMode',
270
+ label: 'runMode',
271
+ typeIds: ['string'],
272
+ kind: 'scalar',
273
+ dataSourceId: 'eleventy',
274
+ }],
275
+ }, {
276
+ id: 'locale_link',
277
+ label: 'locale_link',
278
+ dataSourceId: 'eleventy',
279
+ fields: [{
280
+ id: 'url',
281
+ label: 'url',
282
+ typeIds: ['string'],
283
+ kind: 'scalar',
284
+ dataSourceId: 'eleventy',
285
+ }, {
286
+ id: 'lang',
287
+ label: 'lang',
288
+ typeIds: ['string'],
289
+ kind: 'scalar',
290
+ dataSourceId: 'eleventy',
291
+ }, {
292
+ id: 'label',
293
+ label: 'label',
294
+ typeIds: ['string'],
295
+ kind: 'scalar',
296
+ dataSourceId: 'eleventy',
297
+ }],
298
+ }]
299
+ }
300
+
301
+ /**
302
+ * Implement IDatasource
303
+ */
304
+ getQueryables(): Field[] {
305
+ return [{
306
+ id: 'page',
307
+ label: 'page',
308
+ typeIds: ['page'],
309
+ kind: 'object',
310
+ dataSourceId: 'eleventy',
311
+ //}, {
312
+ // id: 'eleventy',
313
+ // label: 'eleventy',
314
+ // typeIds: ['eleventy'],
315
+ // kind: 'object',
316
+ // dataSourceId: 'eleventy',
317
+ //}, {
318
+ // id: 'env',
319
+ // label: 'env',
320
+ // typeIds: ['env'],
321
+ // kind: 'object',
322
+ // dataSourceId: 'eleventy',
323
+ }]
324
+ }
325
+
326
+ fetchValues(query: string): Promise<unknown> {
327
+ console.log('fetch', {query})
328
+ throw new Error('not implemented yet')
329
+ }
330
+ }
331
+
332
+ export class EleventyDataSourceTest extends EleventyDataSource {
333
+ }
@@ -0,0 +1,124 @@
1
+ import { Component, Editor } from 'grapesjs'
2
+ import { ClientConfig } from '../../config'
3
+
4
+ export default function(editor: Editor/*, opts: EleventyPluginOptions*/): void {
5
+ // **
6
+ // Shortcode
7
+ editor.BlockManager.add('eleventy-shortcode', {
8
+ label: 'Shortcode',
9
+ category: 'Eleventy',
10
+ content: { type: 'eleventy-shortcode' },
11
+ media: '<span style="font-size: 50px;">{%</span>',
12
+ })
13
+ editor.DomComponents.addType('eleventy-shortcode', {
14
+ model: {
15
+ defaults: {
16
+ traits: [{
17
+ type: 'shortcode-name',
18
+ label: 'Shortcode name',
19
+ name: 'shortcode_name',
20
+ placeholder: 'shortcode_name',
21
+ }, {
22
+ type: 'shortcode-attributes',
23
+ label: 'Shortcode attributes',
24
+ name: 'shortcode_attributes',
25
+ placeholder: 'param1, "example param 2"',
26
+ }],
27
+ },
28
+ init() {
29
+ let pending = false
30
+ editor.on('component:add component:remove component:mount', model => {
31
+ if (pending) return
32
+ setTimeout(() => {
33
+ // FIXME: why do we need a timeout?
34
+ if (model.parent() === this || model === this) {
35
+ pending = true
36
+ updateShortcode(this)
37
+ pending = false
38
+ }
39
+ }, 500)
40
+ })
41
+ },
42
+ },
43
+ })
44
+ function updateShortcode(component: Component) {
45
+ const { shortcode_attributes, shortcode_name } = component.get('attributes')
46
+ const componentsToKeep = component.components()
47
+ .filter(c => c.view?.el?.nodeType === 1)
48
+ if (componentsToKeep.length) {
49
+ // Paired shortcode
50
+ component.components(`
51
+ {% ${shortcode_name} ${shortcode_attributes ?? ''} %}
52
+ <div class="replace_content"></div>
53
+ {% end${shortcode_name} %}
54
+ `)
55
+ const replaceContentDiv = component.components().find(c => c.getClasses().includes('replace_content'))
56
+ if (!replaceContentDiv) {
57
+ console.error('No replace_content found', { component, componentsToKeep })
58
+ return
59
+ }
60
+ replaceContentDiv.replaceWith(componentsToKeep)
61
+ } else {
62
+ component.components(`
63
+ {% ${shortcode_name} ${shortcode_attributes ?? ''} %}
64
+ `)
65
+ }
66
+ }
67
+ editor.TraitManager.addType('shortcode-name', {
68
+ onEvent({ component }) {
69
+ updateShortcode(component)
70
+ },
71
+ })
72
+ editor.TraitManager.addType('shortcode-attributes', {
73
+ onEvent({ component }) {
74
+ updateShortcode(component)
75
+ },
76
+ })
77
+ // **
78
+ // Select element
79
+ editor.BlockManager.add('eleventy-select', {
80
+ label: 'select',
81
+ category: 'Eleventy',
82
+ media: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 9c0-.6-.5-1-1.3-1H3.4C2.5 8 2 8.4 2 9v6c0 .6.5 1 1.3 1h17.4c.8 0 1.3-.4 1.3-1V9zm-1 6H3V9h18v6z"></path><path d="M18.5 13l1.5-2h-3zM4 11.5h11v1H4z"></path></svg>',
83
+ content: { type: 'eleventy-select' },
84
+ })
85
+ editor.DomComponents.addType('eleventy-select', {
86
+ model: {
87
+ defaults: {
88
+ tagName: 'select',
89
+ droppable: true,
90
+ // // Prevent the drop down from opening on click
91
+ // script: function() {
92
+ // this.addEventListener('mousedown', event => {
93
+ // event.preventDefault()
94
+ // })
95
+ // },
96
+ },
97
+ },
98
+ })
99
+ // **
100
+ // Option element
101
+ editor.BlockManager.add('eleventy-option', {
102
+ label: 'option',
103
+ category: 'Eleventy',
104
+ media: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>',
105
+ content: { type: 'eleventy-option' },
106
+ })
107
+ editor.DomComponents.addType('eleventy-option', {
108
+ model: {
109
+ defaults: {
110
+ tagName: 'option',
111
+ droppable: false,
112
+ //draggable: 'select, optgroup', // Sometimes we need a container between the select and the option to have states inside the loop
113
+ traits: [
114
+ {
115
+ type: 'text',
116
+ label: 'Value',
117
+ name: 'value',
118
+ placeholder: 'Value',
119
+ },
120
+ ],
121
+ },
122
+ },
123
+ })
124
+ }