@silexlabs/silex 3.0.0-alpha.152 → 3.0.0-alpha.154
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/dist/client/client/grapesjs/index.d.ts +4 -0
- package/dist/client/client/grapesjs/keymaps.d.ts +71 -0
- package/dist/client/client/grapesjs/project-bar.d.ts +1 -0
- package/dist/client/client/utils.d.ts +7 -0
- package/dist/client/css/admin.css +102 -0
- package/dist/client/js/main.js +76 -68
- package/dist/plugins/server/plugins/server/GitlabConnector.d.ts +39 -9
- package/dist/plugins/server/plugins/server/GitlabConnector.js +53 -31
- package/dist/plugins/server/plugins/server/GitlabConnector.js.map +1 -1
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.d.ts +18 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js +94 -0
- package/dist/plugins/server/plugins/server/GitlabHostingConnector.js.map +1 -0
- package/package.json +6 -6
- package/src/css/grapesjs-plugins.scss +126 -0
- package/src/ts/client/api.ts +8 -1
- package/src/ts/client/config.ts +1 -1
- package/src/ts/client/grapesjs/PublicationManager.ts +5 -0
- package/src/ts/client/grapesjs/PublicationUi.ts +45 -27
- package/src/ts/client/grapesjs/index.ts +36 -13
- package/src/ts/client/grapesjs/keymaps.ts +155 -0
- package/src/ts/client/grapesjs/project-bar.ts +6 -4
- package/src/ts/client/utils.ts +33 -9
- package/src/ts/plugins/server/GitlabConnector.ts +64 -43
- package/src/ts/plugins/server/GitlabHostingConnector.ts +97 -0
|
@@ -58,6 +58,132 @@
|
|
|
58
58
|
margin-left: 10px;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// publication-ui
|
|
62
|
+
#publish-dialog {
|
|
63
|
+
header {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: space-between;
|
|
67
|
+
padding-top: 10px;
|
|
68
|
+
padding-bottom: 10px;
|
|
69
|
+
font-size: 0.95rem;
|
|
70
|
+
transition: 200ms;
|
|
71
|
+
|
|
72
|
+
svg {
|
|
73
|
+
width: 1.4rem;
|
|
74
|
+
vertical-align: middle;
|
|
75
|
+
margin-right: 5px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
button {
|
|
79
|
+
border: solid 2px #484848;
|
|
80
|
+
border-radius: 50px;
|
|
81
|
+
padding: 4px 9px;
|
|
82
|
+
background-color: #444;
|
|
83
|
+
color: $secondaryColor;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.connector {
|
|
87
|
+
color: $quaternaryColor;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
main {
|
|
92
|
+
max-height: none;
|
|
93
|
+
|
|
94
|
+
.buttons {
|
|
95
|
+
display: grid;
|
|
96
|
+
grid-template-columns: 1fr;
|
|
97
|
+
grid-template-rows: auto;
|
|
98
|
+
grid-row-gap: 0.5rem;
|
|
99
|
+
transition: 200ms;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
svg {
|
|
103
|
+
height: 0.9rem;
|
|
104
|
+
margin-left: 5px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
h3 {
|
|
108
|
+
margin: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
p {
|
|
112
|
+
margin-top: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
details {
|
|
116
|
+
border-radius: 5px;
|
|
117
|
+
border: solid #222 2px;
|
|
118
|
+
background-color: #222;
|
|
119
|
+
margin: 10px 0;
|
|
120
|
+
padding: 10px;
|
|
121
|
+
word-break: break-word;
|
|
122
|
+
overflow-y: auto;
|
|
123
|
+
max-height: 200px;
|
|
124
|
+
position: relative;
|
|
125
|
+
|
|
126
|
+
pre {
|
|
127
|
+
word-break: break-word;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
summary {
|
|
131
|
+
font-size: 0.95rem;
|
|
132
|
+
opacity: 0.7;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.logs {
|
|
136
|
+
font-family: monospace;
|
|
137
|
+
|
|
138
|
+
* {
|
|
139
|
+
margin: 0;
|
|
140
|
+
padding-top: 3px;
|
|
141
|
+
padding-bottom: 3px;
|
|
142
|
+
border-radius: 5px;
|
|
143
|
+
list-style: '-';
|
|
144
|
+
|
|
145
|
+
&:hover {
|
|
146
|
+
transition: 200ms;
|
|
147
|
+
background-color: rgba(255, 255, 255, 0.05);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&:hover {
|
|
153
|
+
transition: 200ms;
|
|
154
|
+
border-color: $lighterPrimaryColor;
|
|
155
|
+
background-color: $darkerPrimaryColor;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
* {
|
|
159
|
+
transition: 200ms;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
details[open] summary {
|
|
164
|
+
margin-bottom: 10px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.silex-button {
|
|
168
|
+
padding: 10px;
|
|
169
|
+
width: 100%;
|
|
170
|
+
transition: 200ms;
|
|
171
|
+
|
|
172
|
+
&:hover {
|
|
173
|
+
filter: brightness(1.2);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
&:active {
|
|
177
|
+
filter: brightness(0.8);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.silex-button--primary {
|
|
182
|
+
background-color: $tertiaryColor;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
61
187
|
// Adapting the layout of the suggestion list
|
|
62
188
|
.gjs-suggest {
|
|
63
189
|
max-height: 30vh;
|
package/src/ts/client/api.ts
CHANGED
|
@@ -141,7 +141,14 @@ export async function api<ReqQuery, ReqBody, ResBody>(route: ApiRoute | string,
|
|
|
141
141
|
})
|
|
142
142
|
|
|
143
143
|
if (!response.ok) {
|
|
144
|
-
|
|
144
|
+
let json
|
|
145
|
+
try {
|
|
146
|
+
json = await response.json()
|
|
147
|
+
} catch (err) {
|
|
148
|
+
// If the response is not JSON, throw a generic error
|
|
149
|
+
// This is the case when Silex backend has an uncatched error, e.g. expressjs 413 PayloadTooLargeError: request entity too large
|
|
150
|
+
throw new ApiError(response.statusText, response.status)
|
|
151
|
+
}
|
|
145
152
|
throw new ApiError(json.message, response.status)
|
|
146
153
|
}
|
|
147
154
|
|
package/src/ts/client/config.ts
CHANGED
|
@@ -115,7 +115,7 @@ export class ClientConfig extends Config {
|
|
|
115
115
|
* @see https://fontlay.com/
|
|
116
116
|
* @default Google Fonts
|
|
117
117
|
*/
|
|
118
|
-
fontsServerUrl = 'https://
|
|
118
|
+
fontsServerUrl = 'https://fonts.googleapis.com'
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* Init GrapesJS config which depend on the config file properties
|
|
@@ -298,6 +298,11 @@ export class PublicationManager {
|
|
|
298
298
|
data: data as ApiPublicationPublishBody,
|
|
299
299
|
options: this.settings.options,
|
|
300
300
|
})
|
|
301
|
+
|
|
302
|
+
// in gitlab pages situation, getUrl from gitlabHostingConnector gives publication url obtained with Gitlab API pages
|
|
303
|
+
console.info('Gitlab url: ', url)
|
|
304
|
+
// could be used in an future UI
|
|
305
|
+
|
|
301
306
|
this.job = job
|
|
302
307
|
this.status = jobStatusToPublicationStatus(this.job.status)
|
|
303
308
|
this.trackProgress()
|
|
@@ -21,6 +21,8 @@ import { unsafeHTML } from 'lit-html/directives/unsafe-html.js'
|
|
|
21
21
|
import { cmdPublicationLogin, cmdPublicationLogout, cmdPublicationStart, PublicationStatus, PublishableEditor } from './PublicationManager'
|
|
22
22
|
import { ConnectorData, ConnectorType, PublicationJobData, PublicationSettings } from '../../types'
|
|
23
23
|
import { connectorList } from '../api'
|
|
24
|
+
import { defaultKms } from './keymaps'
|
|
25
|
+
import { titleCase } from '../utils'
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* @fileoverview define the publication dialog
|
|
@@ -36,6 +38,12 @@ import { connectorList } from '../api'
|
|
|
36
38
|
// Constants
|
|
37
39
|
export const cmdPublish = 'publish-open-dialog'
|
|
38
40
|
|
|
41
|
+
// **
|
|
42
|
+
// Semantic Icons
|
|
43
|
+
const svgConnector = '<svg width="100%" height="100%" viewBox="0 0 44 23" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><g><g><path d="M2,11.427c0,0 3.846,-2.655 7.376,0c3.531,2.656 7.45,0 7.45,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/></g><path d="M32.165,6.424l9.156,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M32.165,2l0,18.854" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M32.165,16.43l9.156,0" style="fill:none;stroke:#a291ff;stroke-width:4px;"/><path d="M26.253,20.854c-5.203,0 -9.427,-4.224 -9.427,-9.427c-0,-5.203 4.224,-9.427 9.427,-9.427l5.912,-0l0,18.854l-5.912,0Z" style="fill:none;stroke:#a291ff;stroke-width:4px;"/></g></svg>'
|
|
44
|
+
const svgSuccess = '<svg viewBox="0 0 40 28" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><path d="M3,12.969l11.868,11.869l21.838,-21.838" style="fill:none;stroke:#37bf76;stroke-width:6px;"/></svg>'
|
|
45
|
+
const svgError = '<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><g><path d="M3,28.915l25.915,-25.915" style="fill:none;stroke:#cf4443;stroke-width:6px;"/><path d="M28.915,28.915l-25.915,-25.915" style="fill:none;stroke:#cf4443;stroke-width:6px;"/></g></svg>'
|
|
46
|
+
|
|
39
47
|
// **
|
|
40
48
|
// Types
|
|
41
49
|
export type PublicationDialogOptions = {
|
|
@@ -132,7 +140,7 @@ export class PublicationUi {
|
|
|
132
140
|
id: 'publish-button',
|
|
133
141
|
className: 'silex-button--size publish-button',
|
|
134
142
|
command: cmdPublish,
|
|
135
|
-
attributes: { title:
|
|
143
|
+
attributes: { title: `Publish [${titleCase(defaultKms.kmOpenPublish.keys, '+')}]` },
|
|
136
144
|
label: '<span class="fa-solid fa-upload"></span><span class="silex-button--small">Publish</span>',
|
|
137
145
|
})
|
|
138
146
|
})
|
|
@@ -152,6 +160,8 @@ export class PublicationUi {
|
|
|
152
160
|
`, this.el)
|
|
153
161
|
if (this.isOpen) {
|
|
154
162
|
this.el.classList.remove('silex-dialog-hide')
|
|
163
|
+
const primaryBtn = this.el.querySelector('.silex-button--primary') as HTMLElement
|
|
164
|
+
if (primaryBtn) primaryBtn.focus()
|
|
155
165
|
} else {
|
|
156
166
|
this.el.classList.add('silex-dialog-hide')
|
|
157
167
|
}
|
|
@@ -162,26 +172,39 @@ export class PublicationUi {
|
|
|
162
172
|
async renderOpenDialog(job: PublicationJobData, status: PublicationStatus): Promise<TemplateResult> {
|
|
163
173
|
return html`
|
|
164
174
|
<header>
|
|
165
|
-
<
|
|
175
|
+
<span>${unsafeHTML(svgConnector)} Connected to <span class="connector">${this.settings.connector.displayName}</span></span>
|
|
176
|
+
${this.settings.connector.disableLogout ? '' : html`
|
|
177
|
+
<button
|
|
178
|
+
class="silex-button silex-button--secondary"
|
|
179
|
+
id="publish-button--secondary"
|
|
180
|
+
@click=${() => this.editor.Commands.run(cmdPublicationLogout)}
|
|
181
|
+
>Disconnect
|
|
182
|
+
</button>`}
|
|
166
183
|
</header>
|
|
167
184
|
<main>
|
|
168
185
|
${this.isPending(status) ? html`
|
|
169
|
-
<p>Publication in progress
|
|
186
|
+
<p>Publication in progress...</p>
|
|
170
187
|
` : ''}
|
|
171
188
|
${this.isReady(status) ? html`
|
|
172
|
-
<p>
|
|
189
|
+
<p>Click on the button below to publish your website.</p>
|
|
173
190
|
${this.settings.options && Object.entries(this.settings.options).length && html`<p>Publication options: <ul>${ Object.entries(this.settings.options).map(([key, value]) => html`<li>${key}: ${value}</li>`) }</ul></p>`}
|
|
174
191
|
` : ''}
|
|
175
192
|
${this.isSuccess(status) ? html`
|
|
176
|
-
<
|
|
193
|
+
<h3 class="status">Publication success ${unsafeHTML(svgSuccess)}</h3>
|
|
177
194
|
${this.settings.options?.websiteUrl ? html`<p><a href="${this.settings.options.websiteUrl}" target="_blank">Click here to view the published website</a></p>` : ''}
|
|
178
195
|
` : ''}
|
|
179
196
|
${this.isError(status) || this.isLoggedOut(status) ? html`
|
|
180
|
-
<
|
|
181
|
-
<
|
|
197
|
+
<h3 class="status">Publication error ${unsafeHTML(svgError)}</h3>
|
|
198
|
+
<details open>
|
|
199
|
+
<summary>Details</summary>
|
|
200
|
+
${unsafeHTML(this.errorMessage)}
|
|
201
|
+
</details>
|
|
182
202
|
` : ''}
|
|
183
203
|
${job?.message ? html`
|
|
184
|
-
<
|
|
204
|
+
<details open>
|
|
205
|
+
<summary>Details</summary>
|
|
206
|
+
${unsafeHTML(job.message)}
|
|
207
|
+
</details>
|
|
185
208
|
` : ''}
|
|
186
209
|
${this.isPending(status) ? html`
|
|
187
210
|
<progress
|
|
@@ -190,17 +213,13 @@ export class PublicationUi {
|
|
|
190
213
|
></progress>
|
|
191
214
|
` : ''}
|
|
192
215
|
${job?.logs?.length && job.logs[0].length ? html`
|
|
193
|
-
<
|
|
216
|
+
<details>
|
|
194
217
|
<summary>Logs</summary>
|
|
195
|
-
<
|
|
196
|
-
max-width: 100%;
|
|
197
|
-
font-size: x-small;
|
|
198
|
-
" >${unsafeHTML(cleanupLogEntry(job.logs))}
|
|
199
|
-
</pre>
|
|
218
|
+
<div class="logs">${unsafeHTML(cleanupLogEntry(job.logs))}</div>
|
|
200
219
|
</details>
|
|
201
220
|
` : ''}
|
|
202
221
|
${job?.errors?.length && job.errors[0].length ? html`
|
|
203
|
-
<
|
|
222
|
+
<details>
|
|
204
223
|
<summary>Errors</summary>
|
|
205
224
|
<pre style="
|
|
206
225
|
max-width: 100%;
|
|
@@ -210,8 +229,6 @@ export class PublicationUi {
|
|
|
210
229
|
</pre>
|
|
211
230
|
</details>
|
|
212
231
|
` : ''}
|
|
213
|
-
</main>
|
|
214
|
-
<footer>
|
|
215
232
|
${this.isPending(status) || this.isLoggedOut(status) ? '' : html`
|
|
216
233
|
<button
|
|
217
234
|
class="silex-button silex-button--primary"
|
|
@@ -219,19 +236,16 @@ export class PublicationUi {
|
|
|
219
236
|
@click=${() => this.editor.Commands.run(cmdPublicationStart)}
|
|
220
237
|
>Publish</button>
|
|
221
238
|
`}
|
|
239
|
+
</main>
|
|
240
|
+
<footer>
|
|
222
241
|
${this.isLoggedOut(status) ? html`
|
|
223
242
|
<button
|
|
224
243
|
class="silex-button silex-button--primary"
|
|
225
244
|
id="publish-button--primary"
|
|
226
245
|
@click=${() => this.editor.Commands.run(cmdPublicationLogin, this.settings.connector)}
|
|
227
246
|
>Connect</button>
|
|
228
|
-
`:
|
|
229
|
-
|
|
230
|
-
class="silex-button silex-button--secondary"
|
|
231
|
-
id="publish-button--secondary"
|
|
232
|
-
@click=${() => this.editor.Commands.run(cmdPublicationLogout)}
|
|
233
|
-
>Disconnect</button>
|
|
234
|
-
`}
|
|
247
|
+
`: ''}
|
|
248
|
+
<a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
|
|
235
249
|
<button
|
|
236
250
|
class="silex-button silex-button--secondary"
|
|
237
251
|
id="publish-button--secondary"
|
|
@@ -256,20 +270,23 @@ export class PublicationUi {
|
|
|
256
270
|
//}
|
|
257
271
|
return html`
|
|
258
272
|
<main>
|
|
259
|
-
<p>You need to
|
|
273
|
+
<p>You need to select a hosting connector to publish your website.</p>
|
|
260
274
|
${this.isError(status) || this.isLoggedOut(status) ? html`
|
|
261
275
|
<p>Login error</p>
|
|
262
276
|
<div>${unsafeHTML(this.errorMessage)}</div>
|
|
263
277
|
` : ''}
|
|
264
|
-
|
|
278
|
+
<div class="buttons">
|
|
279
|
+
${hostingConnectors.map(connector => html`
|
|
265
280
|
<button
|
|
266
|
-
class="silex-button
|
|
281
|
+
class="silex-button"
|
|
267
282
|
id="publish-button--primary"
|
|
268
283
|
@click=${() => this.editor.Commands.run(cmdPublicationLogin, connector)}
|
|
269
284
|
>${connector.displayName}</button>
|
|
270
285
|
`)}
|
|
286
|
+
</div>
|
|
271
287
|
</main>
|
|
272
288
|
<footer>
|
|
289
|
+
<a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
|
|
273
290
|
<button
|
|
274
291
|
class="silex-button silex-button--secondary"
|
|
275
292
|
id="publish-button--secondary"
|
|
@@ -288,6 +305,7 @@ export class PublicationUi {
|
|
|
288
305
|
<p>Something went wrong: ${err.message}</p>
|
|
289
306
|
</main>
|
|
290
307
|
<footer>
|
|
308
|
+
<a href="https://docs.silex.me/en/user/publish" target="_blank">Help</a>
|
|
291
309
|
<button
|
|
292
310
|
class="silex-button silex-button--secondary"
|
|
293
311
|
id="publish-button--secondary"
|
|
@@ -57,11 +57,13 @@ import { blocksPlugin } from './blocks'
|
|
|
57
57
|
import { semanticPlugin } from './semantic'
|
|
58
58
|
import { orderedList, richTextPlugin, unorderedList } from './rich-text'
|
|
59
59
|
import { internalLinksPlugin } from './internal-links'
|
|
60
|
+
import { defaultKms, keymapsPlugin } from './keymaps'
|
|
60
61
|
import publicationManagerPlugin, { PublicationManagerOptions } from './PublicationManager'
|
|
61
62
|
import ViewButtons from './view-buttons'
|
|
62
63
|
import { storagePlugin } from './storage'
|
|
63
64
|
import { API_PATH, API_WEBSITE_ASSETS_WRITE, API_WEBSITE_PATH } from '../../constants'
|
|
64
65
|
import { ClientConfig } from '../config'
|
|
66
|
+
import { titleCase } from '../utils'
|
|
65
67
|
|
|
66
68
|
const plugins = [
|
|
67
69
|
{name: './project-bar', value: projectBarPlugin}, // has to be before panels and dialogs
|
|
@@ -79,6 +81,7 @@ const plugins = [
|
|
|
79
81
|
{name: 'grapesjs-plugin-forms', value: formPlugin},
|
|
80
82
|
{name: 'grapesjs-custom-code', value: codePlugin},
|
|
81
83
|
{name: './internal-links', value: internalLinksPlugin},
|
|
84
|
+
{name: './keymaps', value: keymapsPlugin},
|
|
82
85
|
{name: '@silexlabs/grapesjs-ui-suggest-classes', value: uiSuggestClasses},
|
|
83
86
|
{name: '@silexlabs/grapesjs-filter-styles', value: filterStyles},
|
|
84
87
|
{name: './symbolDialogs', value: symbolDialogsPlugin},
|
|
@@ -111,6 +114,12 @@ const QUATERNARY_COLOR = '#A291FF'
|
|
|
111
114
|
const DARKER_PRIMARY_COLOR = '#363636'
|
|
112
115
|
const LIGHTER_PRIMARY_COLOR = '#575757'
|
|
113
116
|
|
|
117
|
+
// Commands
|
|
118
|
+
export const cmdToggleLayers = 'open-layers'
|
|
119
|
+
export const cmdToggleBlocks = 'open-blocks'
|
|
120
|
+
export const cmdToggleSymbols = 'open-symbols'
|
|
121
|
+
export const cmdToggleNotifications = 'open-notifications'
|
|
122
|
+
|
|
114
123
|
// ////////////////////
|
|
115
124
|
// Config
|
|
116
125
|
// ////////////////////
|
|
@@ -195,13 +204,15 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
195
204
|
}, {
|
|
196
205
|
id: 'block-manager-btn',
|
|
197
206
|
className: 'block-manager-btn fa fa-fw fa-plus',
|
|
198
|
-
|
|
199
|
-
|
|
207
|
+
name: 'Blocks',
|
|
208
|
+
attributes: { title: `Blocks [${titleCase(defaultKms.kmBlocks.keys, '+')}]`, containerClassName: 'block-manager-container', },
|
|
209
|
+
command: cmdToggleBlocks,
|
|
200
210
|
}, {
|
|
201
211
|
id: 'symbols-btn',
|
|
202
212
|
className: 'symbols-btn fa-regular fa-gem',
|
|
203
|
-
|
|
204
|
-
|
|
213
|
+
name: 'Symbols',
|
|
214
|
+
attributes: { title: `Symbols [${titleCase(defaultKms.kmSymbols.keys, '+')}]`, containerClassName: 'symbols-list-container', },
|
|
215
|
+
command: cmdToggleSymbols,
|
|
205
216
|
buttons: [
|
|
206
217
|
{
|
|
207
218
|
id: 'symbol-create-button',
|
|
@@ -213,7 +224,8 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
213
224
|
}, {
|
|
214
225
|
id: 'page-panel-btn',
|
|
215
226
|
className: 'page-panel-btn fa fa-fw fa-file',
|
|
216
|
-
|
|
227
|
+
name: 'Pages',
|
|
228
|
+
attributes: { title: `Pages [${titleCase(defaultKms.kmPages.keys, '+')}]`, containerClassName: 'page-panel-container', },
|
|
217
229
|
command: cmdTogglePages,
|
|
218
230
|
buttons: [{
|
|
219
231
|
className: 'gjs-pn-btn',
|
|
@@ -223,19 +235,22 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
223
235
|
}, {
|
|
224
236
|
id: 'layer-manager-btn',
|
|
225
237
|
className: 'layer-manager-btn fa-solid fa-layer-group',
|
|
226
|
-
|
|
227
|
-
|
|
238
|
+
name: 'Layers',
|
|
239
|
+
attributes: { title: `Layers [${titleCase(defaultKms.kmLayers.keys, '+')}]`, containerClassName: 'layer-manager-container', },
|
|
240
|
+
command: cmdToggleLayers,
|
|
228
241
|
}, {
|
|
229
242
|
id: 'font-dialog-btn',
|
|
230
243
|
className: 'font-manager-btn fa-solid fa-font',
|
|
231
|
-
|
|
244
|
+
name: 'Fonts',
|
|
245
|
+
attributes: { title: `Fonts [${titleCase(defaultKms.kmOpenFonts.keys, '+')}]` },
|
|
232
246
|
command: () => {
|
|
233
247
|
editor.runCommand(cmdOpenFonts)
|
|
234
248
|
},
|
|
235
249
|
}, {
|
|
236
250
|
id: 'settings-dialog-btn',
|
|
237
251
|
className: 'page-panel-btn fa-solid fa-gears',
|
|
238
|
-
|
|
252
|
+
name: 'Settings',
|
|
253
|
+
attributes: { title: `Settings [${titleCase(defaultKms.kmOpenSettings.keys, '+')}]` },
|
|
239
254
|
command: cmdOpenSettings,
|
|
240
255
|
}, {
|
|
241
256
|
id: 'spacer',
|
|
@@ -244,8 +259,9 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
244
259
|
}, {
|
|
245
260
|
id: 'notifications-btn',
|
|
246
261
|
className: 'notifications-btn fa-regular fa-bell',
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
name: 'Notifications',
|
|
263
|
+
attributes: { title: `Notifications [${titleCase(defaultKms.kmNotifications.keys, '+')}]`, containerClassName: 'notifications-container', },
|
|
264
|
+
command: cmdToggleNotifications,
|
|
249
265
|
buttons: [{
|
|
250
266
|
className: 'gjs-pn-btn',
|
|
251
267
|
command: 'notifications:clear',
|
|
@@ -289,6 +305,13 @@ export function getEditorConfig(config: ClientConfig): EditorConfig {
|
|
|
289
305
|
[filterStyles]: {
|
|
290
306
|
appendBefore: '.gjs-sm-sectors',
|
|
291
307
|
},
|
|
308
|
+
[internalLinksPlugin.toString()]: {
|
|
309
|
+
// FIXME: warn the user about links in error
|
|
310
|
+
onError: (errors) => console.warn('Links errors:', errors),
|
|
311
|
+
},
|
|
312
|
+
[keymapsPlugin.toString()]: {
|
|
313
|
+
disableKeymaps: false,
|
|
314
|
+
},
|
|
292
315
|
[codePlugin.toString()]: {
|
|
293
316
|
blockLabel: 'HTML',
|
|
294
317
|
blockCustomCode: {
|
|
@@ -384,8 +407,8 @@ export async function initEditor(config: EditorConfig) {
|
|
|
384
407
|
|
|
385
408
|
// Remove blocks and layers buttons from the properties
|
|
386
409
|
// This is because in Silex they are on the left
|
|
387
|
-
views.buttons.remove(
|
|
388
|
-
views.buttons.remove(
|
|
410
|
+
views.buttons.remove(cmdToggleBlocks)
|
|
411
|
+
views.buttons.remove(cmdToggleLayers)
|
|
389
412
|
|
|
390
413
|
// Remove useless buttons
|
|
391
414
|
editor.Panels.getPanel('options').buttons.remove('export-template')
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {Editor, PluginOptions} from 'grapesjs'
|
|
2
|
+
import {cmdPublish} from './PublicationUi'
|
|
3
|
+
import {cmdOpenFonts} from '@silexlabs/grapesjs-fonts'
|
|
4
|
+
import {cmdToggleBlocks, cmdToggleLayers, cmdToggleNotifications, cmdToggleSymbols} from './index'
|
|
5
|
+
import {cmdTogglePages} from './page-panel'
|
|
6
|
+
import {cmdOpenSettings} from './settings'
|
|
7
|
+
import {isTextOrInputField, selectBody} from '../utils'
|
|
8
|
+
import {PublishableEditor} from './PublicationManager'
|
|
9
|
+
|
|
10
|
+
// Utility functions
|
|
11
|
+
|
|
12
|
+
function getPanelCommandIds (): string[] {
|
|
13
|
+
return [
|
|
14
|
+
cmdToggleBlocks,
|
|
15
|
+
cmdToggleLayers,
|
|
16
|
+
cmdToggleNotifications,
|
|
17
|
+
cmdToggleSymbols,
|
|
18
|
+
cmdTogglePages,
|
|
19
|
+
cmdOpenSettings,
|
|
20
|
+
cmdOpenFonts
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function toggleCommand (editor: Editor, name: string): void {
|
|
25
|
+
const cmd = editor.Commands
|
|
26
|
+
|
|
27
|
+
if (!cmd.isActive(name)) {
|
|
28
|
+
resetPanel(editor)
|
|
29
|
+
cmd.run(name)
|
|
30
|
+
} else {
|
|
31
|
+
cmd.stop(name)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resetPanel(editor: Editor): void {
|
|
36
|
+
getPanelCommandIds().forEach(p => editor.Commands.stop(p))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Escapes the current context in this order : modal, Publish dialog, left panel.
|
|
41
|
+
* If none of these are open, it selects the body.
|
|
42
|
+
* @param editor The editor.
|
|
43
|
+
*/
|
|
44
|
+
function escapeContext(editor: Editor): void {
|
|
45
|
+
const publishDialog = (editor as PublishableEditor).PublicationManager.dialog
|
|
46
|
+
|
|
47
|
+
if (editor.Modal.isOpen()) {
|
|
48
|
+
editor.Modal.close()
|
|
49
|
+
} else if (publishDialog && publishDialog.isOpen) {
|
|
50
|
+
publishDialog.closeDialog()
|
|
51
|
+
} else if (getPanelCommandIds().some(cmd => editor.Commands.isActive(cmd))) {
|
|
52
|
+
resetPanel(editor)
|
|
53
|
+
} else {
|
|
54
|
+
selectBody(editor)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Constants
|
|
59
|
+
|
|
60
|
+
export const cmdSelectBody = 'select-body'
|
|
61
|
+
export let prefixKey = 'shift'
|
|
62
|
+
|
|
63
|
+
export const defaultKms = {
|
|
64
|
+
kmOpenSettings: {
|
|
65
|
+
id: 'general:open-settings',
|
|
66
|
+
keys: 'alt+s',
|
|
67
|
+
handler: editor => toggleCommand(editor, cmdOpenSettings)
|
|
68
|
+
},
|
|
69
|
+
kmOpenPublish: {
|
|
70
|
+
id: 'general:open-publish',
|
|
71
|
+
keys: 'alt+p',
|
|
72
|
+
handler: editor => toggleCommand(editor, cmdPublish)
|
|
73
|
+
},
|
|
74
|
+
kmOpenFonts: {
|
|
75
|
+
id: 'general:open-fonts',
|
|
76
|
+
keys: 'alt+f',
|
|
77
|
+
handler: editor => toggleCommand(editor, cmdOpenFonts)
|
|
78
|
+
},
|
|
79
|
+
kmPreviewMode: {
|
|
80
|
+
id: 'general:preview-mode',
|
|
81
|
+
keys: 'tab',
|
|
82
|
+
handler: editor => toggleCommand(editor, 'preview'),
|
|
83
|
+
options: {prevent: true}
|
|
84
|
+
},
|
|
85
|
+
kmLayers: {
|
|
86
|
+
id: 'panels:layers',
|
|
87
|
+
keys: prefixKey + '+l',
|
|
88
|
+
handler: editor => toggleCommand(editor, cmdToggleLayers)
|
|
89
|
+
},
|
|
90
|
+
kmBlocks: {
|
|
91
|
+
id: 'panels:blocks',
|
|
92
|
+
keys: prefixKey + '+a',
|
|
93
|
+
handler: editor => toggleCommand(editor, cmdToggleBlocks)
|
|
94
|
+
},
|
|
95
|
+
kmNotifications: {
|
|
96
|
+
id: 'panels:notifications',
|
|
97
|
+
keys: prefixKey + '+n',
|
|
98
|
+
handler: editor => toggleCommand(editor, cmdToggleNotifications)
|
|
99
|
+
},
|
|
100
|
+
kmPages: {
|
|
101
|
+
id: 'panels:pages',
|
|
102
|
+
keys: prefixKey + '+p',
|
|
103
|
+
handler: editor => toggleCommand(editor, cmdTogglePages)
|
|
104
|
+
},
|
|
105
|
+
kmSymbols: {
|
|
106
|
+
id: 'panels:symbols',
|
|
107
|
+
keys: prefixKey + '+s',
|
|
108
|
+
handler: editor => toggleCommand(editor, cmdToggleSymbols)
|
|
109
|
+
},
|
|
110
|
+
kmClosePanel: {
|
|
111
|
+
id: 'panels:close-panel',
|
|
112
|
+
keys: 'escape',
|
|
113
|
+
handler: escapeContext
|
|
114
|
+
},
|
|
115
|
+
kmSelectBody: {
|
|
116
|
+
id: 'workflow:select-body',
|
|
117
|
+
keys: prefixKey + '+b',
|
|
118
|
+
handler: cmdSelectBody
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Main part
|
|
123
|
+
|
|
124
|
+
export function keymapsPlugin(editor: Editor, opts: PluginOptions): void {
|
|
125
|
+
// Commands
|
|
126
|
+
editor.Commands.add(cmdSelectBody, selectBody)
|
|
127
|
+
|
|
128
|
+
if (opts.disableKeymaps) return
|
|
129
|
+
if (opts.prefixKey) prefixKey = opts.prefixKey
|
|
130
|
+
|
|
131
|
+
const km = editor.Keymaps
|
|
132
|
+
|
|
133
|
+
// Default keymaps
|
|
134
|
+
for (const keymap in defaultKms) {
|
|
135
|
+
km.add(defaultKms[keymap].id, defaultKms[keymap].keys, defaultKms[keymap].handler, defaultKms[keymap].options)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Handling the Escape keymap during text edition
|
|
139
|
+
document.addEventListener('keydown', event => {
|
|
140
|
+
if (event.key.toLowerCase() === defaultKms.kmClosePanel.keys) {
|
|
141
|
+
const target = event.target as HTMLElement | null
|
|
142
|
+
const rte = editor.getEditing()
|
|
143
|
+
|
|
144
|
+
if (rte) { // If in Rich Text edition...
|
|
145
|
+
// TODO: Close the Rich Text edition and un-focus the text field
|
|
146
|
+
} else if (target) { // If target exists...
|
|
147
|
+
if (target.tagName === 'INPUT' && target.getAttribute('type') === 'submit') { // If it's a submit button...
|
|
148
|
+
escapeContext(editor)
|
|
149
|
+
} else if (isTextOrInputField(target)) { // If it's a text field...
|
|
150
|
+
target.blur()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}
|
|
@@ -25,6 +25,7 @@ export interface PanelObject {
|
|
|
25
25
|
command: string | ((editor: Editor) => void)
|
|
26
26
|
text: string
|
|
27
27
|
className: string
|
|
28
|
+
name?: string
|
|
28
29
|
attributes: {
|
|
29
30
|
title?: string
|
|
30
31
|
containerClassName?: string
|
|
@@ -41,13 +42,13 @@ export const projectBarPlugin = (editor, opts) => {
|
|
|
41
42
|
// create the panels container for all panels in grapesjs
|
|
42
43
|
const containerPanel = editor.Panels.addPanel({
|
|
43
44
|
id: containerPanelId,
|
|
44
|
-
visible
|
|
45
|
+
visible: false,
|
|
45
46
|
})
|
|
46
47
|
// create the project bar panel in grapesjs
|
|
47
48
|
editor.Panels.addPanel({
|
|
48
49
|
id: PROJECT_BAR_PANEL_ID,
|
|
49
50
|
buttons: opts.panels,
|
|
50
|
-
visible
|
|
51
|
+
visible: true,
|
|
51
52
|
})
|
|
52
53
|
// add the panels to the container
|
|
53
54
|
opts.panels.map(panel => addButton(editor, panel))
|
|
@@ -79,10 +80,11 @@ export function addButton(editor: Editor, panel: PanelObject) {
|
|
|
79
80
|
if(panel.attributes.containerClassName) {
|
|
80
81
|
el.classList.add('project-bar__panel', panel.attributes.containerClassName, 'gjs-hidden')
|
|
81
82
|
// add header
|
|
82
|
-
|
|
83
|
+
const title = panel.name ?? panel.attributes.title
|
|
84
|
+
if(title) {
|
|
83
85
|
render(html`
|
|
84
86
|
<header class="project-bar__panel-header">
|
|
85
|
-
<h3 class="project-bar__panel-header-title">${
|
|
87
|
+
<h3 class="project-bar__panel-header-title">${ title }</h3>
|
|
86
88
|
${ panel.buttons?.map(button => {
|
|
87
89
|
return html`
|
|
88
90
|
<div
|