@silexlabs/silex 3.5.0 → 3.5.1-2
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.
- package/.silex.js +0 -3
- package/dist/client/client/config.d.ts +8 -0
- package/dist/client/client/grapesjs/PublicationManager.d.ts +43 -0
- package/dist/client/client/grapesjs/cms/DataSource.d.ts +43 -0
- package/dist/client/client/grapesjs/cms/blocks.d.ts +2 -0
- package/dist/client/client/grapesjs/cms/buttons.d.ts +2 -0
- package/dist/client/client/grapesjs/cms/collection-pages.d.ts +4 -0
- package/dist/client/client/grapesjs/cms/config.d.ts +9 -0
- package/dist/client/client/grapesjs/cms/filters.d.ts +3 -0
- package/dist/client/client/grapesjs/cms/index.d.ts +44 -0
- package/dist/client/client/grapesjs/cms/liquid.d.ts +59 -0
- package/dist/client/client/grapesjs/cms/page-settings.d.ts +6 -0
- package/dist/client/client/grapesjs/cms/publication.d.ts +60 -0
- package/dist/client/client/grapesjs/cms/site-settings.d.ts +3 -0
- package/dist/client/client/grapesjs/cms/states.d.ts +8 -0
- package/dist/client/client/grapesjs/cms/traits.d.ts +8 -0
- package/dist/client/client/grapesjs/settings.d.ts +12 -3
- package/dist/client/client/grapesjs/view-buttons.d.ts +2 -1
- package/dist/client/client/utils.d.ts +1 -0
- package/dist/client/constants.d.ts +2 -1
- package/dist/client/css/admin.css +117 -19
- package/dist/client/index.html +2 -2
- package/dist/client/js/main.js +1974 -267
- package/dist/client/types.d.ts +3 -1
- package/dist/plugins/client/plugins/client/template.js +1 -1
- package/dist/plugins/client/plugins/client/template.js.map +1 -1
- package/dist/plugins/client/types.d.ts +3 -1
- package/dist/plugins/client/types.js +11 -8
- package/dist/plugins/client/types.js.map +1 -1
- package/dist/plugins/server/constants.d.ts +2 -1
- package/dist/plugins/server/constants.js +3 -2
- package/dist/plugins/server/constants.js.map +1 -1
- package/dist/plugins/server/plugins/server/FtpConnector.js +15 -20
- package/dist/plugins/server/plugins/server/FtpConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabConnector.js +34 -7
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/dist/plugins/server/server/connectors/FsStorage.js +16 -20
- package/dist/plugins/server/server/connectors/FsStorage.js.map +1 -1
- package/dist/plugins/server/server/utils/websiteDataSerialization.d.ts +4 -0
- package/dist/plugins/server/server/utils/websiteDataSerialization.js +21 -12
- package/dist/plugins/server/server/utils/websiteDataSerialization.js.map +1 -1
- package/dist/plugins/server/types.d.ts +3 -1
- package/dist/plugins/server/types.js +12 -9
- package/dist/plugins/server/types.js.map +1 -1
- package/dist/server/constants.js +3 -2
- package/dist/server/server/connectors/FsStorage.js +14 -18
- package/dist/server/server/utils/websiteDataSerialization.js +16 -11
- package/dist/server/types.js +6 -2
- package/package.json +16 -11
- package/src/css/_variables.scss +2 -1
- package/src/css/grapesjs-plugins.scss +31 -0
- package/src/css/grapesjs.scss +20 -0
- package/src/css/page-panel.scss +25 -6
- package/src/css/silex.scss +64 -5
- package/src/ts/client/config.ts +23 -0
- package/src/ts/client/grapesjs/PublicationManager.ts +159 -1
- package/src/ts/client/grapesjs/breadcrumbs.ts +3 -2
- package/src/ts/client/grapesjs/cms/DataSource.ts +333 -0
- package/src/ts/client/grapesjs/cms/blocks.ts +124 -0
- package/src/ts/client/grapesjs/cms/buttons.ts +163 -0
- package/src/ts/client/grapesjs/cms/collection-pages.ts +214 -0
- package/src/ts/client/grapesjs/cms/config.ts +47 -0
- package/src/ts/client/grapesjs/cms/filters.ts +53 -0
- package/src/ts/client/grapesjs/cms/index.ts +130 -0
- package/src/ts/client/grapesjs/cms/liquid.mock.ts +181 -0
- package/src/ts/client/grapesjs/cms/liquid.test.ts +239 -0
- package/src/ts/client/grapesjs/cms/liquid.ts +325 -0
- package/src/ts/client/grapesjs/cms/page-settings.ts +285 -0
- package/src/ts/client/grapesjs/cms/publication.test.ts +300 -0
- package/src/ts/client/grapesjs/cms/publication.ts +708 -0
- package/src/ts/client/grapesjs/cms/site-settings.ts +79 -0
- package/src/ts/client/grapesjs/cms/states.ts +125 -0
- package/src/ts/client/grapesjs/cms/traits.ts +95 -0
- package/src/ts/client/grapesjs/css-props.ts +15 -16
- package/src/ts/client/grapesjs/flex.ts +59 -39
- package/src/ts/client/grapesjs/img.ts +1 -1
- package/src/ts/client/grapesjs/index.ts +77 -4
- package/src/ts/client/grapesjs/internal-links.ts +4 -2
- package/src/ts/client/grapesjs/page-panel.ts +126 -11
- package/src/ts/client/grapesjs/sectors.ts +18 -10
- package/src/ts/client/grapesjs/semantic.ts +4 -0
- package/src/ts/client/grapesjs/settings.ts +252 -101
- package/src/ts/client/grapesjs/storage.ts +12 -3
- package/src/ts/client/grapesjs/view-buttons.ts +39 -3
- package/src/ts/client/utils.ts +15 -2
- package/src/ts/constants.ts +2 -1
- package/src/ts/plugins/client/template.ts +1 -1
- package/src/ts/plugins/server/FtpConnector.test.ts +1 -0
- package/src/ts/plugins/server/FtpConnector.ts +20 -23
- package/src/ts/plugins/server/GitlabConnector.ts +40 -10
- package/src/ts/server/connectors/FsStorage.test.ts +3 -2
- package/src/ts/server/connectors/FsStorage.ts +21 -23
- package/src/ts/server/utils/websiteDataSerialization.test.ts +45 -13
- package/src/ts/server/utils/websiteDataSerialization.ts +23 -14
- package/src/ts/types.ts +6 -2
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ClientConfig } from '../../config'
|
|
2
|
+
import { CMS_SETTINGS_SECTION_ID, EleventyPluginOptions, Silex11tyPluginWebsiteSettings } from './index'
|
|
3
|
+
import { html } from 'lit-html'
|
|
4
|
+
import { addDataSource, createDataSource } from '@silexlabs/grapesjs-data-source'
|
|
5
|
+
import { Editor } from 'grapesjs'
|
|
6
|
+
import { cmdAddSection } from '../settings'
|
|
7
|
+
|
|
8
|
+
export default function(editor: Editor, opts: EleventyPluginOptions): void {
|
|
9
|
+
editor.runCommand(cmdAddSection, {
|
|
10
|
+
section: {
|
|
11
|
+
id: CMS_SETTINGS_SECTION_ID,
|
|
12
|
+
label: 'CMS',
|
|
13
|
+
render: () => {
|
|
14
|
+
const settings = (editor.getModel().get('settings') || {}) as Silex11tyPluginWebsiteSettings
|
|
15
|
+
return html`
|
|
16
|
+
<style>
|
|
17
|
+
#settings-${CMS_SETTINGS_SECTION_ID} label {
|
|
18
|
+
display: block;
|
|
19
|
+
margin-bottom: 10px;
|
|
20
|
+
}
|
|
21
|
+
.add-ds-btn {
|
|
22
|
+
width: 30px;
|
|
23
|
+
height: 30px;
|
|
24
|
+
margin-left: auto;
|
|
25
|
+
font-size: 24px;
|
|
26
|
+
background-color: var(--gjs-main-light-color);
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
29
|
+
<div id="settings-${CMS_SETTINGS_SECTION_ID}" class="silex-hideable silex-hidden">
|
|
30
|
+
<div class="gjs-sm-sector-title">Silex CMS</div>
|
|
31
|
+
<div class="silex-help">
|
|
32
|
+
<p>The <a target="_blank" href="https://github.com/silexlabs/silex-cms">Silex CMS feature</a> integrates with your favorite headless CMS, API or database.</p>
|
|
33
|
+
<p>By adding data sources to your website you activate <a target="_blank" href="https://www.11ty.dev/docs/">11ty static site generator</a> integration. When you wil publish your website, the generated files assume you build the site with 11ty and possibly with Gitlab pages.</p>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="gjs-sm-sector-title">
|
|
36
|
+
Data Sources
|
|
37
|
+
<button
|
|
38
|
+
class="silex-button add-ds-btn"
|
|
39
|
+
title="Add a new data source"
|
|
40
|
+
@click=${async () => {
|
|
41
|
+
const newDataSource = createDataSource()
|
|
42
|
+
addDataSource(newDataSource)
|
|
43
|
+
try {
|
|
44
|
+
await newDataSource.connect()
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('🟡 [site-settings] Data source connection failed:', error)
|
|
47
|
+
}
|
|
48
|
+
}}>
|
|
49
|
+
+
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
${opts.view?.settingsEl ? (opts.view.settingsEl as () => HTMLElement)() : ''}
|
|
53
|
+
<div class="gjs-sm-sector-title">11ty Config</div>
|
|
54
|
+
<div class="silex-help">
|
|
55
|
+
<p>These settings are used to configure the <a target="_blank" href="https://www.11ty.dev/docs/">11ty static site generator</a> integration.</p>
|
|
56
|
+
<p>Depending on your 11ty configuration, you may need to adjust these settings, it will enable or disable features in Silex.</p>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="silex-help">
|
|
59
|
+
<p>⚠️ You need to reload Silex for these settings to take effect.</p>
|
|
60
|
+
</div>
|
|
61
|
+
<label>
|
|
62
|
+
I18N Plugin
|
|
63
|
+
<input id="i18n-checkbox" type="checkbox" name="eleventyI18n" ?checked=${settings.eleventyI18n || opts.i18nPlugin} ?disabled=${typeof opts.i18nPlugin !== 'undefined'}>
|
|
64
|
+
</label>
|
|
65
|
+
<label>
|
|
66
|
+
Fetch Plugin
|
|
67
|
+
<input type="checkbox" name="eleventyFetch" ?checked=${settings.eleventyFetch || !!opts.fetchPluginSettings} ?disabled=${typeof opts.fetchPluginSettings !== 'undefined'}>
|
|
68
|
+
</label>
|
|
69
|
+
<div class="silex-form__group col2">
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
`
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
siteOrPage: 'site',
|
|
77
|
+
position: 'last'
|
|
78
|
+
})
|
|
79
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { removeState, setState, COMPONENT_NAME_PREFIX, Property, toExpression, StoredFilter, State, StoredState, StoredToken, Expression } from '@silexlabs/grapesjs-data-source'
|
|
2
|
+
import { Silex11tyPluginWebsiteSettings } from './index'
|
|
3
|
+
import { Component, Editor, Page } from 'grapesjs'
|
|
4
|
+
|
|
5
|
+
export default function(editor: Editor/*, opts: EleventyPluginOptions */): void {
|
|
6
|
+
editor.on('page:select page:update', () => updatePaginationStates(editor))
|
|
7
|
+
editor.on('settings:save:start', () => updatePaginationStates(editor))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Update pagination states for a page with the ability to control which page index to show
|
|
12
|
+
* @param editor - The GrapesJS editor instance
|
|
13
|
+
* @param pageIndex - The current page index (0-based) for pagination preview. Use 0 for publication.
|
|
14
|
+
*/
|
|
15
|
+
export function updatePaginationStates(editor: Editor, pageIndex = 0, preventTrigger = false) {
|
|
16
|
+
const page = editor.Pages.getSelected()
|
|
17
|
+
const body: Component = page?.getMainComponent() as Component
|
|
18
|
+
if (!body) return // This happens when the current page is deleted
|
|
19
|
+
|
|
20
|
+
// Do not show "Body's " prefix for states on the body
|
|
21
|
+
body.attributes.COMPONENT_NAME_PREFIX = ''
|
|
22
|
+
|
|
23
|
+
// Store pagination data in the body component
|
|
24
|
+
// This is for the GraphQL query to include it
|
|
25
|
+
const settings = page?.get('settings') as Silex11tyPluginWebsiteSettings | undefined
|
|
26
|
+
const pageData = toExpression(settings?.eleventyPageData) as (Property[] | null)
|
|
27
|
+
|
|
28
|
+
if (pageData && pageData.length > 0) {
|
|
29
|
+
try {
|
|
30
|
+
// Taken from the pagination object https://www.11ty.dev/docs/pagination/
|
|
31
|
+
// Apply pagination size limit using slice filter
|
|
32
|
+
const pageSize = parseInt(settings?.eleventyPageSize || '1')
|
|
33
|
+
const startIndex = pageIndex * pageSize
|
|
34
|
+
const endIndex = startIndex + pageSize
|
|
35
|
+
|
|
36
|
+
const slice = {
|
|
37
|
+
type: 'filter',
|
|
38
|
+
id: 'slice',
|
|
39
|
+
label: 'slice',
|
|
40
|
+
options: {
|
|
41
|
+
start: startIndex,
|
|
42
|
+
end: endIndex,
|
|
43
|
+
},
|
|
44
|
+
} as StoredFilter
|
|
45
|
+
|
|
46
|
+
const itemsExpression = [
|
|
47
|
+
...pageData,
|
|
48
|
+
slice,
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
if (preventTrigger) {
|
|
52
|
+
const itemsState: StoredState = body.attributes.publicStates.find((state: StoredFilter) => state.id === 'items')
|
|
53
|
+
if (!itemsState) {
|
|
54
|
+
if (!body.attributes.publicStates) body.attributes.publicStates = []
|
|
55
|
+
body.attributes.publicStates.push({
|
|
56
|
+
hidden: true,
|
|
57
|
+
label: 'pagination.items',
|
|
58
|
+
expression: itemsExpression,
|
|
59
|
+
})
|
|
60
|
+
} else {
|
|
61
|
+
itemsState.expression = itemsExpression
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Add or update pagination state in publicStates
|
|
65
|
+
const paginationState: StoredState = body.attributes.publicStates.find((state: StoredFilter) => state.id === 'pagination')
|
|
66
|
+
const paginationExpression = [{
|
|
67
|
+
label: 'Unused pagination label',
|
|
68
|
+
type: 'property',
|
|
69
|
+
propType: 'field',
|
|
70
|
+
fieldId: 'pagination',
|
|
71
|
+
dataSourceId: 'eleventy',
|
|
72
|
+
typeIds: ['pagination'],
|
|
73
|
+
kind: 'object',
|
|
74
|
+
}] as Expression
|
|
75
|
+
if (!paginationState) {
|
|
76
|
+
body.attributes.publicStates.push({
|
|
77
|
+
hidden: true,
|
|
78
|
+
label: 'pagination',
|
|
79
|
+
expression: paginationExpression,
|
|
80
|
+
})
|
|
81
|
+
} else {
|
|
82
|
+
paginationState.expression = paginationExpression
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
// FIXME: should we let UndoManager save?
|
|
86
|
+
editor.UndoManager.skip(() => {
|
|
87
|
+
setState(body, 'items', {
|
|
88
|
+
hidden: true,
|
|
89
|
+
label: 'pagination.items',
|
|
90
|
+
expression: itemsExpression,
|
|
91
|
+
}, true, 1)
|
|
92
|
+
|
|
93
|
+
// Update body states with the new settings
|
|
94
|
+
setState(body, 'pagination', {
|
|
95
|
+
hidden: true,
|
|
96
|
+
label: 'pagination',
|
|
97
|
+
expression: [{
|
|
98
|
+
label: 'Unused pagination label',
|
|
99
|
+
type: 'property' as const,
|
|
100
|
+
propType: 'field' as const,
|
|
101
|
+
fieldId: 'pagination',
|
|
102
|
+
dataSourceId: 'eleventy',
|
|
103
|
+
typeIds: ['pagination'],
|
|
104
|
+
kind: 'object' as const,
|
|
105
|
+
}]
|
|
106
|
+
}, true, 0)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
console.error('Invalid JSON for eleventyPageData', e)
|
|
111
|
+
removeState(body, 'pagination', true)
|
|
112
|
+
removeState(body, 'items', true)
|
|
113
|
+
editor.runCommand('notifications:add', {
|
|
114
|
+
type: 'error',
|
|
115
|
+
message: 'Invalid JSON for eleventyPageData',
|
|
116
|
+
group: 'Errors in your settings',
|
|
117
|
+
componentId: body.id,
|
|
118
|
+
})
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
removeState(body, 'pagination', true)
|
|
123
|
+
removeState(body, 'items', true)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Editor } from 'grapesjs'
|
|
2
|
+
import { ClientConfig } from '../../config'
|
|
3
|
+
import { html, render } from 'lit-html'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @fileoverview
|
|
7
|
+
* This adds these traits to all components:
|
|
8
|
+
* - unwrap the component
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const UNWRAP_ID = 'plugin-unwrap'
|
|
12
|
+
const LABEL = 'Unwrap content'
|
|
13
|
+
const LABEL_DETAILS = 'Remove the component and keep its content'
|
|
14
|
+
|
|
15
|
+
export default function(editor: Editor/*, opts: EleventyPluginOptions */): void {
|
|
16
|
+
// Add the new trait to all component types
|
|
17
|
+
editor.DomComponents.getTypes().map(type => {
|
|
18
|
+
editor.DomComponents.addType(type.id, {
|
|
19
|
+
model: {
|
|
20
|
+
defaults: {
|
|
21
|
+
traits: [
|
|
22
|
+
// Keep the type original traits
|
|
23
|
+
...(editor.DomComponents.getType(type.id)?.model.prototype.defaults.traits || []),
|
|
24
|
+
// Add the new trait
|
|
25
|
+
{
|
|
26
|
+
label: LABEL,
|
|
27
|
+
type: UNWRAP_ID,
|
|
28
|
+
name: UNWRAP_ID,
|
|
29
|
+
//type: 'checkbox',
|
|
30
|
+
},
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
function doRender(el: HTMLElement, remove: boolean) {
|
|
38
|
+
render(html`
|
|
39
|
+
<label
|
|
40
|
+
for=${UNWRAP_ID}
|
|
41
|
+
class="gjs-field gjs-field-checkbox silex-label"
|
|
42
|
+
title=${LABEL_DETAILS}
|
|
43
|
+
style="width: 100%; background: var(--primaryColor);"
|
|
44
|
+
>
|
|
45
|
+
<input
|
|
46
|
+
type="checkbox"
|
|
47
|
+
id=${UNWRAP_ID}
|
|
48
|
+
@change=${(event: Event) => doRender(el, (event.target as HTMLInputElement)?.checked)}
|
|
49
|
+
?checked=${remove}
|
|
50
|
+
style="
|
|
51
|
+
display: initial;
|
|
52
|
+
appearance: none;
|
|
53
|
+
width: 20px;
|
|
54
|
+
height: 20px;
|
|
55
|
+
position: absolute;
|
|
56
|
+
"
|
|
57
|
+
>
|
|
58
|
+
<i
|
|
59
|
+
class="gjs-chk-icon"
|
|
60
|
+
style="
|
|
61
|
+
position: absolute;
|
|
62
|
+
z-index: 1;
|
|
63
|
+
"
|
|
64
|
+
></i>
|
|
65
|
+
</label>
|
|
66
|
+
`, el)
|
|
67
|
+
}
|
|
68
|
+
function doRenderCurrent(el: HTMLElement) {
|
|
69
|
+
doRender(el, editor.getSelected()?.get(UNWRAP_ID))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// inspired by https://github.com/olivmonnier/grapesjs-plugin-header/blob/master/src/components.js
|
|
73
|
+
editor.TraitManager.addType(UNWRAP_ID, {
|
|
74
|
+
createInput() {
|
|
75
|
+
// Create a new element container and add some content
|
|
76
|
+
const el = document.createElement('div')
|
|
77
|
+
// update the UI when a page is added/renamed/removed
|
|
78
|
+
editor.on('page', () => doRenderCurrent(el))
|
|
79
|
+
doRenderCurrent(el)
|
|
80
|
+
// this will be the element passed to onEvent and onUpdate
|
|
81
|
+
return el
|
|
82
|
+
},
|
|
83
|
+
// Update the component based on UI changes
|
|
84
|
+
// `elInput` is the result HTMLElement you get from `createInput`
|
|
85
|
+
onEvent({ elInput, component }) {
|
|
86
|
+
const value = (elInput.querySelector(`#${UNWRAP_ID}`) as HTMLInputElement)?.checked
|
|
87
|
+
component.set(UNWRAP_ID, value)
|
|
88
|
+
},
|
|
89
|
+
// Update UI on the component change
|
|
90
|
+
onUpdate({ elInput, component }) {
|
|
91
|
+
const value = component.get(UNWRAP_ID)
|
|
92
|
+
doRender(elInput, value)
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
}
|
|
@@ -243,19 +243,6 @@ export default (editor: Editor, opts) => {
|
|
|
243
243
|
info: 'Controls content overflow in a block.',
|
|
244
244
|
}, { at: 6 })
|
|
245
245
|
|
|
246
|
-
editor.StyleManager.addProperty('dimension', {
|
|
247
|
-
name: 'Scroll Behavior',
|
|
248
|
-
property: 'scroll-behavior',
|
|
249
|
-
type: 'select',
|
|
250
|
-
defaults: '',
|
|
251
|
-
options: [
|
|
252
|
-
{ id: '', value: '', name: '' },
|
|
253
|
-
{ id: 'auto', value: 'auto', name: 'auto' },
|
|
254
|
-
{ id: 'smooth', value: 'smooth', name: 'smooth' }
|
|
255
|
-
],
|
|
256
|
-
info: 'Sets smooth or auto scroll behavior.',
|
|
257
|
-
}, { at: 7 })
|
|
258
|
-
|
|
259
246
|
editor.StyleManager.removeProperty('dimension', 'margin')
|
|
260
247
|
editor.StyleManager.addProperty('dimension', {
|
|
261
248
|
name: 'Margin',
|
|
@@ -794,6 +781,18 @@ export default (editor: Editor, opts) => {
|
|
|
794
781
|
],
|
|
795
782
|
info: 'The column-span CSS property makes it possible for an element to span across all columns when its value is set to all.',
|
|
796
783
|
}, { at: 9 })
|
|
784
|
+
editor.StyleManager.addProperty('extra', {
|
|
785
|
+
name: 'Scroll Behavior',
|
|
786
|
+
property: 'scroll-behavior',
|
|
787
|
+
type: 'select',
|
|
788
|
+
defaults: '',
|
|
789
|
+
options: [
|
|
790
|
+
{ id: '', value: '', name: '' },
|
|
791
|
+
{ id: 'auto', value: 'auto', name: 'auto' },
|
|
792
|
+
{ id: 'smooth', value: 'smooth', name: 'smooth' }
|
|
793
|
+
],
|
|
794
|
+
info: 'Sets smooth or auto scroll behavior.',
|
|
795
|
+
}, { at: 10 })
|
|
797
796
|
editor.StyleManager.addProperty('extra', {
|
|
798
797
|
name: 'Scroll Snap Type',
|
|
799
798
|
property: 'scroll-snap-type',
|
|
@@ -824,7 +823,7 @@ export default (editor: Editor, opts) => {
|
|
|
824
823
|
],
|
|
825
824
|
}],
|
|
826
825
|
info: 'The scroll-snap-type CSS property sets the direction and mode of enforced snap points on the scroll container.',
|
|
827
|
-
}, { at:
|
|
826
|
+
}, { at: 11 })
|
|
828
827
|
editor.StyleManager.addProperty('extra', {
|
|
829
828
|
name: 'Scroll Padding',
|
|
830
829
|
property: 'scroll-padding',
|
|
@@ -832,7 +831,7 @@ export default (editor: Editor, opts) => {
|
|
|
832
831
|
defaults: '',
|
|
833
832
|
units: ['px', 'em', 'rem', '%'],
|
|
834
833
|
info: 'Defines offsets for scroll snapping.',
|
|
835
|
-
}, { at:
|
|
834
|
+
}, { at: 12 })
|
|
836
835
|
editor.StyleManager.addProperty('extra', {
|
|
837
836
|
name: 'Scroll Snap Align',
|
|
838
837
|
property: 'scroll-snap-align',
|
|
@@ -846,7 +845,7 @@ export default (editor: Editor, opts) => {
|
|
|
846
845
|
{ id: 'center', value: 'center', name: 'center' },
|
|
847
846
|
],
|
|
848
847
|
info: 'Aligns elements to scroll snaps.',
|
|
849
|
-
}, { at:
|
|
848
|
+
}, { at: 13 })
|
|
850
849
|
|
|
851
850
|
editor.SelectorManager.states.add({name: 'before', label: 'Before'})
|
|
852
851
|
editor.SelectorManager.states.add({name: 'after', label: 'After'})
|
|
@@ -42,47 +42,67 @@ const FLEX_ITEM_PROPS = [
|
|
|
42
42
|
export default (editor: Editor) => {
|
|
43
43
|
editor.on('load', () => {
|
|
44
44
|
const sector = editor.StyleManager.getSector('flex')
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
|
|
46
|
+
let showSector = false
|
|
47
|
+
let visibleProps: string[] = []
|
|
48
|
+
|
|
49
|
+
// Update visibility depending on selected component
|
|
50
|
+
function updateVisibility(comp: Component) {
|
|
51
|
+
const computedDisplay = comp?.view?.el && getComputedStyle(comp.view.el)['display'] as string || ''
|
|
52
|
+
const isFlex = computedDisplay === 'flex' || computedDisplay === 'inline-flex'
|
|
53
|
+
const parent = comp.parent()
|
|
54
|
+
const parentComputedDisplay = parent?.view?.el && getComputedStyle(parent.view.el)['display'] as string || ''
|
|
55
|
+
const parentIsFlex = parentComputedDisplay === 'flex' || parentComputedDisplay === 'inline-flex'
|
|
56
|
+
|
|
57
|
+
// Determine which props should be visible
|
|
58
|
+
const newVisibleProps: string[] = []
|
|
59
|
+
if (isFlex) newVisibleProps.push(...FLEX_PROPS)
|
|
60
|
+
if (parentIsFlex) newVisibleProps.push(...FLEX_ITEM_PROPS)
|
|
61
|
+
|
|
62
|
+
// Set sector visibility
|
|
63
|
+
showSector = parentIsFlex || isFlex
|
|
64
|
+
sector.set('visible', showSector)
|
|
65
|
+
|
|
66
|
+
// Set property visibility
|
|
67
|
+
visibleProps = newVisibleProps
|
|
68
|
+
sector.getProperties().forEach(p => {
|
|
69
|
+
p.set('visible', visibleProps.includes(p.getId()))
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
56
73
|
// Listen to the events that should trigger a visibility check
|
|
57
|
-
editor.on('component:selected', comp =>
|
|
58
|
-
|
|
59
|
-
|
|
74
|
+
editor.on('component:selected', (comp: Component) => {
|
|
75
|
+
if (!comp) return
|
|
76
|
+
updateVisibility(comp)
|
|
77
|
+
})
|
|
78
|
+
|
|
60
79
|
// `style:property:update` is useful for instantly display the flex sector when selecting display: inline-flex;
|
|
61
|
-
editor.on('style:property:update', () =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
if (parentIsFlex && isFlex) {
|
|
75
|
-
sector.getProperties()
|
|
76
|
-
.forEach(p => p.set('visible', true))
|
|
77
|
-
} else if (isFlex) {
|
|
78
|
-
sector.getProperties()
|
|
79
|
-
.forEach(p => p.set('visible', FLEX_PROPS.includes(p.getId())))
|
|
80
|
-
} else if (parentIsFlex) {
|
|
81
|
-
sector.getProperties()
|
|
82
|
-
.forEach(p => p.set('visible', FLEX_ITEM_PROPS.includes(p.getId())))
|
|
83
|
-
}
|
|
84
|
-
editor.on('change:visible', applyVisibility)
|
|
85
|
-
})
|
|
80
|
+
editor.on('style:property:update', () => {
|
|
81
|
+
const comp = editor.getSelected()
|
|
82
|
+
if (comp) updateVisibility(comp)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// Watch changes made by grapesjs and revert them if needed
|
|
86
|
+
function doubleCheckSector() {
|
|
87
|
+
if (sector.get('visible') !== showSector) {
|
|
88
|
+
sector.off('change:visible', doubleCheckSector)
|
|
89
|
+
sector.set('visible', showSector)
|
|
90
|
+
sector.on('change:visible', doubleCheckSector)
|
|
91
|
+
}
|
|
86
92
|
}
|
|
93
|
+
sector.on('change:visible', doubleCheckSector)
|
|
94
|
+
|
|
95
|
+
// Watch property visibility changes
|
|
96
|
+
sector.getProperties().forEach(p => {
|
|
97
|
+
function doubleCheckProp() {
|
|
98
|
+
const shouldBeVisible = visibleProps.includes(p.getId())
|
|
99
|
+
if (p.get('visible') !== shouldBeVisible) {
|
|
100
|
+
p.off('change:visible', doubleCheckProp)
|
|
101
|
+
p.set('visible', shouldBeVisible)
|
|
102
|
+
p.on('change:visible', doubleCheckProp)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
p.on('change:visible', doubleCheckProp)
|
|
106
|
+
})
|
|
87
107
|
})
|
|
88
108
|
}
|
|
@@ -29,7 +29,7 @@ export default (editor: Editor, opts) => {
|
|
|
29
29
|
placeholderSrc: '/assets/placeholder.png',
|
|
30
30
|
// These default values will never be used,
|
|
31
31
|
// as there is default values in config
|
|
32
|
-
replacedElements: ['img'],
|
|
32
|
+
replacedElements: ['img', 'li', 'video'],
|
|
33
33
|
// Override with provided config
|
|
34
34
|
...opts,
|
|
35
35
|
}
|
|
@@ -31,6 +31,7 @@ const notificationContainer = document.createElement('div')
|
|
|
31
31
|
import blocksBasicPlugin from 'grapesjs-blocks-basic'
|
|
32
32
|
import styleFilterPlugin from 'grapesjs-style-filter'
|
|
33
33
|
import formPlugin from 'grapesjs-plugin-forms'
|
|
34
|
+
// import tailwindPlugin from 'grapesjs-tailwind'
|
|
34
35
|
import codePlugin from 'grapesjs-custom-code'
|
|
35
36
|
import filterStyles from '@silexlabs/grapesjs-filter-styles'
|
|
36
37
|
import symbolsPlugin from '@silexlabs/grapesjs-symbols'
|
|
@@ -51,6 +52,7 @@ import backgroundPlugin from 'grapesjs-style-bg'
|
|
|
51
52
|
import resizePanelPlugin from './resize-panel'
|
|
52
53
|
import notificationsPlugin, { NOTIFICATION_CHANGED } from '@silexlabs/grapesjs-notifications'
|
|
53
54
|
import keymapsDialogPlugin, { cmdKeymapsDialog } from '@silexlabs/grapesjs-keymaps-dialog'
|
|
55
|
+
import parserPostCSS from 'grapesjs-parser-postcss'
|
|
54
56
|
|
|
55
57
|
import { pagePanelPlugin, cmdTogglePages, cmdAddPage } from './page-panel'
|
|
56
58
|
import { newPageDialog, cmdOpenNewPageDialog } from './new-page-dialog'
|
|
@@ -68,6 +70,9 @@ import { API_PATH, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_PATH, SILEX_VERSION } f
|
|
|
68
70
|
import { ClientConfig } from '../config'
|
|
69
71
|
import { titleCase } from '../utils'
|
|
70
72
|
import uploadProgress from './upload-progress'
|
|
73
|
+
import cmsPlugin from './cms'
|
|
74
|
+
import { ClientEvent } from '../events'
|
|
75
|
+
import { ClientSideFileWithContent, PublicationData } from '../../types'
|
|
71
76
|
|
|
72
77
|
const plugins = [
|
|
73
78
|
{name: './project-bar', value: projectBarPlugin}, // has to be before panels and dialogs
|
|
@@ -84,6 +89,7 @@ const plugins = [
|
|
|
84
89
|
{name: './rich-text', value: richTextPlugin},
|
|
85
90
|
{name: 'grapesjs-style-filter', value: styleFilterPlugin},
|
|
86
91
|
{name: 'grapesjs-plugin-forms', value: formPlugin},
|
|
92
|
+
// {name: 'grapesjs-tailwind', value: tailwindPlugin},
|
|
87
93
|
{name: 'grapesjs-custom-code', value: codePlugin},
|
|
88
94
|
{name: './internal-links', value: internalLinksPlugin},
|
|
89
95
|
{name: './keymaps', value: keymapsPlugin},
|
|
@@ -105,6 +111,7 @@ const plugins = [
|
|
|
105
111
|
{name: './resize-panel', value: resizePanelPlugin},
|
|
106
112
|
{name: '@silexlabs/grapesjs-notifications', value: notificationsPlugin},
|
|
107
113
|
{name: '@silexlabs/grapesjs-keymaps-dialog', value: keymapsDialogPlugin},
|
|
114
|
+
{name: 'grapesjs-parser-postcss', value: parserPostCSS},
|
|
108
115
|
{name: './upload-progress', value: uploadProgress},
|
|
109
116
|
]
|
|
110
117
|
// Check that all plugins are loaded correctly
|
|
@@ -136,6 +143,17 @@ const catComponents = 'Components'
|
|
|
136
143
|
|
|
137
144
|
export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
138
145
|
const { websiteId, storageId, rootUrl } = config
|
|
146
|
+
|
|
147
|
+
// Create dynamic plugins array with conditional CMS plugin
|
|
148
|
+
const dynamicPlugins = [...plugins]
|
|
149
|
+
const dynamicPluginsOpts: any = {}
|
|
150
|
+
|
|
151
|
+
// Add CMS plugin if enabled
|
|
152
|
+
if (config.cmsConfig?.enabled !== false) {
|
|
153
|
+
dynamicPlugins.push({name: './cms', value: cmsPlugin})
|
|
154
|
+
dynamicPluginsOpts[cmsPlugin.toString()] = config.cmsConfig
|
|
155
|
+
}
|
|
156
|
+
|
|
139
157
|
return {
|
|
140
158
|
container: '#gjs',
|
|
141
159
|
height: '100%',
|
|
@@ -180,9 +198,10 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
180
198
|
escapeName: (name) => `${name}`,
|
|
181
199
|
},
|
|
182
200
|
|
|
183
|
-
plugins:
|
|
201
|
+
plugins: dynamicPlugins.map(p => p.value),
|
|
184
202
|
|
|
185
203
|
pluginsOpts: {
|
|
204
|
+
...dynamicPluginsOpts,
|
|
186
205
|
[blocksBasicPlugin.toString()]: {
|
|
187
206
|
blocks: ['text', 'image', 'video', 'map'],
|
|
188
207
|
category: catBasic,
|
|
@@ -248,6 +267,11 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
248
267
|
name: 'Settings',
|
|
249
268
|
attributes: { title: `Settings (${titleCase(defaultKms.kmOpenSettings.keys, '+')})` },
|
|
250
269
|
command: cmdOpenSettings,
|
|
270
|
+
//}, {
|
|
271
|
+
// id: 'tailwind-theme',
|
|
272
|
+
// className: 'fa-solid fa-brush',
|
|
273
|
+
// attributes: { title: 'Tailwind color theme' },
|
|
274
|
+
// command: 'open-update-theme'
|
|
251
275
|
}, {
|
|
252
276
|
id: 'spacer',
|
|
253
277
|
attributes: {},
|
|
@@ -310,6 +334,9 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
310
334
|
[keymapsPlugin.toString()]: {
|
|
311
335
|
disableKeymaps: false,
|
|
312
336
|
},
|
|
337
|
+
// [tailwindPlugin.toString()]: {
|
|
338
|
+
// tailwindPlayCdn: `${config.rootUrl}/tailwind-3.4.17.js`,
|
|
339
|
+
// },
|
|
313
340
|
[codePlugin.toString()]: {
|
|
314
341
|
blockLabel: 'HTML',
|
|
315
342
|
blockCustomCode: {
|
|
@@ -381,11 +408,18 @@ export async function initEditor(config: EditorConfig) {
|
|
|
381
408
|
|
|
382
409
|
editor.Commands.add('gjs-open-import-webpage', openImport(editor, {
|
|
383
410
|
modalImportLabel: '',
|
|
384
|
-
modalImportContent: 'Paste a web page HTML code here.',
|
|
411
|
+
modalImportContent: 'Paste a web page HTML code here. This is an experiment, it might destroy the existing website.',
|
|
385
412
|
modalImportButton: 'Import',
|
|
386
413
|
modalImportTitle: 'Import from website',
|
|
387
414
|
}))
|
|
388
415
|
|
|
416
|
+
// // Add tailwind css to the published site
|
|
417
|
+
// editor.on(ClientEvent.PUBLISH_DATA, ({ data }: { data: PublicationData }) => {
|
|
418
|
+
// editor.runCommand('get-tailwindCss', {
|
|
419
|
+
// callback: (css: string) => (data.files.find(f => f.type === 'css') as ClientSideFileWithContent).content += css,
|
|
420
|
+
// })
|
|
421
|
+
// })
|
|
422
|
+
|
|
389
423
|
// Detect loading errors
|
|
390
424
|
// Display a useful notification
|
|
391
425
|
const typeConfig = {
|
|
@@ -403,8 +437,35 @@ export async function initEditor(config: EditorConfig) {
|
|
|
403
437
|
},
|
|
404
438
|
},
|
|
405
439
|
}
|
|
406
|
-
editor.DomComponents
|
|
407
|
-
|
|
440
|
+
const dc = editor.DomComponents
|
|
441
|
+
dc.addType('image', typeConfig)
|
|
442
|
+
dc.addType('iframe', typeConfig)
|
|
443
|
+
|
|
444
|
+
dc.getTypes().map(type => {
|
|
445
|
+
const dcmp = dc.getType(type.id)?.model.prototype
|
|
446
|
+
dc.addType(type.id, {
|
|
447
|
+
model: {
|
|
448
|
+
defaults: {
|
|
449
|
+
traits: [
|
|
450
|
+
{
|
|
451
|
+
type: 'text',
|
|
452
|
+
label: 'ID',
|
|
453
|
+
name: 'id',
|
|
454
|
+
},
|
|
455
|
+
...(dcmp.defaults.traits || []),
|
|
456
|
+
]
|
|
457
|
+
},
|
|
458
|
+
init(...args) {
|
|
459
|
+
(dcmp.init.apply(this, args))
|
|
460
|
+
if (!this.getAttributes().id) {
|
|
461
|
+
this.addAttributes({ id: this.getId() })
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
})
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
|
|
408
469
|
|
|
409
470
|
// Adjustments to do when the editor is ready
|
|
410
471
|
editor.on('load', () => {
|
|
@@ -415,6 +476,18 @@ export async function initEditor(config: EditorConfig) {
|
|
|
415
476
|
views.buttons.remove(cmdToggleBlocks)
|
|
416
477
|
views.buttons.remove(cmdToggleLayers)
|
|
417
478
|
|
|
479
|
+
// Select body when changing pages
|
|
480
|
+
// This includes initial loading, as the 1st page is selected in storage
|
|
481
|
+
editor.on('page:select', () => {
|
|
482
|
+
// Only select wrapper if nothing else is selected
|
|
483
|
+
if (!editor.getSelected()) {
|
|
484
|
+
const wrapper = editor.getWrapper()
|
|
485
|
+
if (wrapper) {
|
|
486
|
+
editor.select(wrapper)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
})
|
|
490
|
+
|
|
418
491
|
// Remove useless buttons
|
|
419
492
|
editor.Panels.getPanel('options').buttons.remove('export-template')
|
|
420
493
|
editor.Panels.getPanel('options').buttons.remove('fullscreen')
|
|
@@ -35,8 +35,10 @@ export const internalLinksPlugin = (editor, opts) => {
|
|
|
35
35
|
function onAll(cbk) {
|
|
36
36
|
editor.Pages.getAll()
|
|
37
37
|
.forEach(page => {
|
|
38
|
-
page.getMainComponent()
|
|
39
|
-
|
|
38
|
+
const mainComponent = page.getMainComponent()
|
|
39
|
+
if (mainComponent) {
|
|
40
|
+
mainComponent.onAll(cbk)
|
|
41
|
+
}
|
|
40
42
|
})
|
|
41
43
|
}
|
|
42
44
|
editor.on('page', ({ event, page }) => {
|