@themarioga/grid-editor 7.2.0 → 8.0.0-beta.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/CHANGELOG.md +101 -0
- package/README.md +96 -30
- package/UPGRADING.md +86 -0
- package/dist/grideditor.bundle.min.js +3 -3
- package/dist/grideditor.css +149 -6
- package/dist/grideditor.d.ts +18 -0
- package/dist/grideditor.esm.js +257 -48
- package/dist/grideditor.jquery.js +0 -13
- package/dist/grideditor.jquery.min.js +1 -1
- package/dist/grideditor.jquery.min.js.map +3 -3
- package/dist/grideditor.js +257 -48
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/grideditor.min.js +2 -2
- package/dist/grideditor.min.js.map +3 -3
- package/dist/locales/grideditor.es.esm.js +80 -6
- package/dist/locales/grideditor.es.js +80 -6
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +2 -2
- package/dist/plugins/grideditor.accordion.min.js.map +2 -2
- package/dist/plugins/grideditor.alignment.esm.js +11 -38
- package/dist/plugins/grideditor.alignment.js +11 -38
- package/dist/plugins/grideditor.alignment.min.js +1 -1
- package/dist/plugins/grideditor.alignment.min.js.map +3 -3
- package/dist/plugins/grideditor.card.min.js.map +1 -1
- package/dist/plugins/grideditor.ckeditor.esm.js +0 -5
- package/dist/plugins/grideditor.ckeditor.js +0 -5
- package/dist/plugins/grideditor.ckeditor.min.js +1 -1
- package/dist/plugins/grideditor.ckeditor.min.js.map +3 -3
- package/dist/plugins/grideditor.clipboard.min.js.map +1 -1
- package/dist/plugins/grideditor.codemirror-inline.min.js.map +1 -1
- package/dist/plugins/grideditor.codemirror.min.js.map +1 -1
- package/dist/plugins/grideditor.elements.min.js.map +2 -2
- package/dist/plugins/grideditor.gutters.min.js.map +1 -1
- package/dist/plugins/grideditor.order.min.js.map +1 -1
- package/dist/plugins/grideditor.popup.min.js.map +1 -1
- package/dist/plugins/grideditor.sections.min.js.map +2 -2
- package/dist/plugins/grideditor.style.esm.js +1926 -0
- package/dist/plugins/grideditor.style.js +1923 -0
- package/dist/plugins/grideditor.style.min.js +4 -0
- package/dist/plugins/grideditor.style.min.js.map +7 -0
- package/dist/plugins/grideditor.summernote.min.js.map +2 -2
- package/dist/plugins/grideditor.tabs.min.js.map +2 -2
- package/dist/plugins/grideditor.tinymce.esm.js +1 -8
- package/dist/plugins/grideditor.tinymce.js +1 -8
- package/dist/plugins/grideditor.tinymce.min.js +1 -1
- package/dist/plugins/grideditor.tinymce.min.js.map +3 -3
- package/docs/events.md +4 -6
- package/docs/locale-keys.md +99 -15
- package/docs/plugins.md +47 -23
- package/example/ckeditor.html +0 -5
- package/example/index.html +17 -2
- package/example/style.html +163 -0
- package/example/utilities.html +21 -16
- package/package.json +1 -1
- package/src/js/dom.js +1 -2
- package/src/js/grideditor.jquery.js +7 -28
- package/src/js/grideditor.js +336 -67
- package/src/js/locales/grideditor.es.js +80 -6
- package/src/js/plugins/grideditor.alignment.js +21 -50
- package/src/js/plugins/grideditor.ckeditor.js +10 -23
- package/src/js/plugins/grideditor.style.js +185 -0
- package/src/js/plugins/grideditor.summernote.js +4 -5
- package/src/js/plugins/grideditor.tinymce.js +4 -17
- package/src/js/style/accordion.js +168 -0
- package/src/js/style/catalog.js +76 -0
- package/src/js/style/custom.js +84 -0
- package/src/js/style/display.js +229 -0
- package/src/js/style/drawerflow.js +118 -0
- package/src/js/style/fields.js +382 -0
- package/src/js/style/flex.js +156 -0
- package/src/js/{plugins/grideditor.float.js → style/float.js} +6 -16
- package/src/js/style/options.js +60 -0
- package/src/js/style/sections.js +298 -0
- package/src/js/{plugins/grideditor.spacing.js → style/spacing.js} +7 -19
- package/src/js/style/sticky.js +39 -0
- package/src/js/{plugins/grideditor.textalign.js → style/textalign.js} +6 -14
- package/src/js/text/grideditor.text.js +1 -1
- package/src/less/grideditor.less +151 -10
- package/types/grideditor.d.ts +18 -0
- package/dist/plugins/grideditor.float.esm.js +0 -34
- package/dist/plugins/grideditor.float.js +0 -41
- package/dist/plugins/grideditor.float.min.js +0 -1
- package/dist/plugins/grideditor.float.min.js.map +0 -7
- package/dist/plugins/grideditor.spacing.esm.js +0 -318
- package/dist/plugins/grideditor.spacing.js +0 -323
- package/dist/plugins/grideditor.spacing.min.js +0 -1
- package/dist/plugins/grideditor.spacing.min.js.map +0 -7
- package/dist/plugins/grideditor.textalign.esm.js +0 -40
- package/dist/plugins/grideditor.textalign.js +0 -47
- package/dist/plugins/grideditor.textalign.min.js +0 -1
- package/dist/plugins/grideditor.textalign.min.js.map +0 -7
- package/dist/plugins/grideditor.visibility.esm.js +0 -158
- package/dist/plugins/grideditor.visibility.js +0 -163
- package/dist/plugins/grideditor.visibility.min.js +0 -1
- package/dist/plugins/grideditor.visibility.min.js.map +0 -7
- package/src/js/plugins/grideditor.visibility.js +0 -171
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style plugin's inline fields: one per css property, each writing the
|
|
3
|
+
* node's own style through ge.setHostStyle, never by hand, so the preview
|
|
4
|
+
* of a breakpoint view neither shows in them nor eats what they write.
|
|
5
|
+
*
|
|
6
|
+
* A field is filled from the node by renderStyleField, again whenever the
|
|
7
|
+
* plugin asks: after a write elsewhere, after the classes change. A value
|
|
8
|
+
* the browser refused stays in its field, marked invalid, until it is put
|
|
9
|
+
* right or emptied; nothing is written meanwhile.
|
|
10
|
+
*/
|
|
11
|
+
import * as dom from '../dom.js';
|
|
12
|
+
import { overriding } from './sections.js';
|
|
13
|
+
|
|
14
|
+
var renderers = new WeakMap(); // field -> what fills it from the node
|
|
15
|
+
var fieldCounter = 0;
|
|
16
|
+
|
|
17
|
+
function classesOf(node) {
|
|
18
|
+
return (node.getAttribute('class') || '').split(/\s+/).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Write what the user typed: trimmed, with a trailing !important taken as
|
|
23
|
+
* the priority. Without one, the property keeps the priority it had.
|
|
24
|
+
*/
|
|
25
|
+
function write(ge, node, property, raw) {
|
|
26
|
+
var text = String(raw === null || raw === undefined ? '' : raw).trim();
|
|
27
|
+
var important = /\s*!\s*important\s*$/i.exec(text);
|
|
28
|
+
var priority;
|
|
29
|
+
|
|
30
|
+
if (important) {
|
|
31
|
+
text = text.slice(0, important.index).trim();
|
|
32
|
+
priority = 'important';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return ge.setHostStyle(node, property, text, priority);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function mark(input, ok, ge) {
|
|
39
|
+
dom.toggleClass(input, 'is-invalid', !ok);
|
|
40
|
+
if (ok) {
|
|
41
|
+
input.removeAttribute('title');
|
|
42
|
+
} else {
|
|
43
|
+
input.setAttribute('title', ge.t('style.invalid'));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Put a value in an input, unless it holds something refused that is still being put right. */
|
|
48
|
+
function fill(input, value) {
|
|
49
|
+
if (!dom.hasClass(input, 'is-invalid')) { input.value = value; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function textInput(className) {
|
|
53
|
+
return dom.element('input', { type: 'text', 'class': 'form-control form-control-sm ' + (className || '') });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Colors */
|
|
57
|
+
|
|
58
|
+
var HEX = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
59
|
+
|
|
60
|
+
/** What a color picker can show of a value: a six digit hex, or nothing. */
|
|
61
|
+
function pickable(value) {
|
|
62
|
+
var match = HEX.exec(value.trim());
|
|
63
|
+
if (!match) { return null; }
|
|
64
|
+
|
|
65
|
+
var hex = match[1];
|
|
66
|
+
if (hex.length === 3) { hex = hex.split('').map(function(c) { return c + c; }).join(''); }
|
|
67
|
+
|
|
68
|
+
return '#' + hex.toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A picker and a text beside it, which takes what a picker cannot: rgba, var(), transparent. */
|
|
72
|
+
function colorControl(onValue) {
|
|
73
|
+
var box = dom.element('div', { 'class': 'input-group input-group-sm ge-style-color' });
|
|
74
|
+
var picker = box.appendChild(dom.element('input', { type: 'color', 'class': 'form-control form-control-color' }));
|
|
75
|
+
var text = box.appendChild(textInput());
|
|
76
|
+
|
|
77
|
+
picker.addEventListener('input', function() {
|
|
78
|
+
dom.removeClass(text, 'is-invalid');
|
|
79
|
+
text.value = picker.value;
|
|
80
|
+
onValue(picker.value, text);
|
|
81
|
+
});
|
|
82
|
+
text.addEventListener('change', function() {
|
|
83
|
+
var hex = pickable(text.value);
|
|
84
|
+
if (hex) { picker.value = hex; }
|
|
85
|
+
onValue(text.value, text);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
element: box,
|
|
90
|
+
set: function(value) {
|
|
91
|
+
fill(text, value);
|
|
92
|
+
var hex = pickable(value);
|
|
93
|
+
if (hex) { picker.value = hex; }
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Background images */
|
|
99
|
+
|
|
100
|
+
var URL_VALUE = /^url\(\s*(["']?)(.*)\1\s*\)$/i;
|
|
101
|
+
|
|
102
|
+
/** What the field shows of a background-image: the url itself, or the value as it is. */
|
|
103
|
+
function shownUrl(value) {
|
|
104
|
+
var match = URL_VALUE.exec(value.trim());
|
|
105
|
+
if (!match) { return value; }
|
|
106
|
+
|
|
107
|
+
return match[1] ? match[2].replace(/\\(["\\])/g, '$1') : match[2];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A url becomes url("…"); a gradient, none or any other css function goes as it is. */
|
|
111
|
+
function urlValue(text) {
|
|
112
|
+
text = text.trim();
|
|
113
|
+
if (text === '' || /^(?:none|inherit|initial|unset|revert)$/i.test(text) || /^[a-z-]+\(/i.test(text)) { return text; }
|
|
114
|
+
|
|
115
|
+
return 'url("' + text.replace(/(["\\])/g, '\\$1') + '")';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Shadows */
|
|
119
|
+
|
|
120
|
+
/** Split at spaces and commas outside parentheses. */
|
|
121
|
+
function tokens(value) {
|
|
122
|
+
var list = [];
|
|
123
|
+
var depth = 0;
|
|
124
|
+
var current = '';
|
|
125
|
+
|
|
126
|
+
for (var i = 0; i < value.length; i++) {
|
|
127
|
+
var c = value.charAt(i);
|
|
128
|
+
|
|
129
|
+
if (c === '(') { depth++; }
|
|
130
|
+
if (c === ')') { depth--; }
|
|
131
|
+
|
|
132
|
+
if (depth === 0 && (c === ' ' || c === ',')) {
|
|
133
|
+
if (current) { list.push(current); }
|
|
134
|
+
if (c === ',') { list.push(','); }
|
|
135
|
+
current = '';
|
|
136
|
+
} else {
|
|
137
|
+
current += c;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (current) { list.push(current); }
|
|
141
|
+
|
|
142
|
+
return list;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
var LENGTH = /^-?(?:\d*\.)?\d+(?:[a-z%]+)?$/i;
|
|
146
|
+
|
|
147
|
+
/** The lengths of a shadow, in the order css writes them; a text shadow has no spread. */
|
|
148
|
+
var SHADOW_PARTS = [
|
|
149
|
+
{ name: 'x', labelKey: 'style.shadow_x' },
|
|
150
|
+
{ name: 'y', labelKey: 'style.shadow_y' },
|
|
151
|
+
{ name: 'blur', labelKey: 'style.shadow_blur' },
|
|
152
|
+
{ name: 'spread', labelKey: 'style.shadow_spread' },
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* One shadow as the builder's parts, or null when the value is something
|
|
157
|
+
* the builder cannot hold: several shadows, var(), a keyword.
|
|
158
|
+
*/
|
|
159
|
+
function parseShadow(value, box) {
|
|
160
|
+
value = value.trim();
|
|
161
|
+
if (value === '') { return { inset: false, lengths: [], color: '' }; }
|
|
162
|
+
if (/var\(/i.test(value)) { return null; }
|
|
163
|
+
|
|
164
|
+
var list = tokens(value);
|
|
165
|
+
if (list.indexOf(',') !== -1) { return null; }
|
|
166
|
+
|
|
167
|
+
var inset = false;
|
|
168
|
+
var lengths = [];
|
|
169
|
+
var colors = [];
|
|
170
|
+
|
|
171
|
+
list.forEach(function(token) {
|
|
172
|
+
if (box && token.toLowerCase() === 'inset') {
|
|
173
|
+
inset = true;
|
|
174
|
+
} else if (LENGTH.test(token)) {
|
|
175
|
+
lengths.push(token);
|
|
176
|
+
} else {
|
|
177
|
+
colors.push(token);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (colors.length > 1 || lengths.length < 2 || lengths.length > (box ? 4 : 3)) { return null; }
|
|
182
|
+
if (colors.length && /^(?:none|inherit|initial|unset|revert)$/i.test(colors[0])) { return null; }
|
|
183
|
+
|
|
184
|
+
return { inset: inset, lengths: lengths, color: colors[0] || '' };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function composeShadow(parts, box) {
|
|
188
|
+
var lengths = parts.lengths.slice();
|
|
189
|
+
|
|
190
|
+
// A later length needs the earlier ones: a spread needs a blur
|
|
191
|
+
for (var i = lengths.length - 1; i >= 0; i--) {
|
|
192
|
+
if (lengths[i] === '' && lengths.slice(i + 1).some(Boolean)) { lengths[i] = '0'; }
|
|
193
|
+
}
|
|
194
|
+
lengths = lengths.filter(Boolean);
|
|
195
|
+
|
|
196
|
+
if (!lengths.length && !parts.color && !parts.inset) { return ''; }
|
|
197
|
+
while (lengths.length < 2) { lengths.push('0'); }
|
|
198
|
+
|
|
199
|
+
return (box && parts.inset ? 'inset ' : '') + lengths.join(' ') + (parts.color ? ' ' + parts.color : '');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The shadow field: a builder for one shadow - x, y, blur, the spread and
|
|
204
|
+
* inset for a box - and the value as text when it is more than the
|
|
205
|
+
* builder holds, or when the user asks for the text.
|
|
206
|
+
*/
|
|
207
|
+
function shadowControl(ge, node, property, changed) {
|
|
208
|
+
var box = property === 'box-shadow';
|
|
209
|
+
var holder = dom.element('div', { 'class': 'ge-style-shadow' });
|
|
210
|
+
var textMode = false;
|
|
211
|
+
|
|
212
|
+
function writeValue(value, input) {
|
|
213
|
+
var ok = write(ge, node, property, value);
|
|
214
|
+
mark(input, ok, ge);
|
|
215
|
+
if (ok) { changed(); }
|
|
216
|
+
return ok;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function builder(parts) {
|
|
220
|
+
holder.innerHTML = '';
|
|
221
|
+
var grid = holder.appendChild(dom.element('div', { 'class': 'ge-style-shadow-builder' }));
|
|
222
|
+
var names = box ? SHADOW_PARTS : SHADOW_PARTS.slice(0, 3);
|
|
223
|
+
var inputs = names.map(function(part, i) {
|
|
224
|
+
var cell = grid.appendChild(dom.element('label', { 'class': 'ge-style-shadow-part' }));
|
|
225
|
+
cell.appendChild(dom.element('span', { 'class': 'ge-style-shadow-label' }, ge.t(part.labelKey)));
|
|
226
|
+
var input = cell.appendChild(textInput('ge-style-shadow-' + part.name));
|
|
227
|
+
input.value = parts.lengths[i] || '';
|
|
228
|
+
return input;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
var colorCell = grid.appendChild(dom.element('label', { 'class': 'ge-style-shadow-part ge-style-shadow-color' }));
|
|
232
|
+
colorCell.appendChild(dom.element('span', { 'class': 'ge-style-shadow-label' }, ge.t('style.shadow_color')));
|
|
233
|
+
var color = colorControl(function() { update(); });
|
|
234
|
+
colorCell.appendChild(color.element);
|
|
235
|
+
color.set(parts.color);
|
|
236
|
+
|
|
237
|
+
var inset = null;
|
|
238
|
+
if (box) {
|
|
239
|
+
var insetCell = grid.appendChild(dom.element('label', { 'class': 'form-check ge-style-shadow-inset' }));
|
|
240
|
+
inset = insetCell.appendChild(dom.element('input', { type: 'checkbox', 'class': 'form-check-input' }));
|
|
241
|
+
insetCell.appendChild(dom.element('span', { 'class': 'form-check-label' }, ge.t('style.shadow_inset')));
|
|
242
|
+
inset.checked = parts.inset;
|
|
243
|
+
inset.addEventListener('change', function() { update(); });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
inputs.forEach(function(input) { input.addEventListener('change', function() { update(); }); });
|
|
247
|
+
|
|
248
|
+
var toText = holder.appendChild(dom.element('a', { href: '#', 'class': 'ge-style-shadow-mode' }, ge.t('style.shadow_text_mode')));
|
|
249
|
+
toText.addEventListener('click', function(e) {
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
textMode = true;
|
|
252
|
+
render();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
function update() {
|
|
256
|
+
var value = composeShadow({
|
|
257
|
+
inset: inset ? inset.checked : false,
|
|
258
|
+
lengths: inputs.map(function(input) { return input.value.trim(); }),
|
|
259
|
+
color: dom.one(color.element, 'input[type="text"]').value.trim(),
|
|
260
|
+
}, box);
|
|
261
|
+
writeValue(value, inputs[0]);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function text(value) {
|
|
266
|
+
holder.innerHTML = '';
|
|
267
|
+
var input = holder.appendChild(textInput('ge-style-shadow-text'));
|
|
268
|
+
input.value = value;
|
|
269
|
+
input.addEventListener('change', function() {
|
|
270
|
+
if (input.value.trim() === '') {
|
|
271
|
+
if (writeValue('', input)) {
|
|
272
|
+
textMode = false;
|
|
273
|
+
render();
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
writeValue(input.value, input);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
var shown = null; // The value the builder or the text was last built for
|
|
282
|
+
|
|
283
|
+
function render() {
|
|
284
|
+
if (holder.querySelector('.is-invalid')) { return; }
|
|
285
|
+
|
|
286
|
+
var value = ge.hostStyle(node, property).value;
|
|
287
|
+
var parts = parseShadow(value, box);
|
|
288
|
+
|
|
289
|
+
// Built again only when the value changed under it: building takes
|
|
290
|
+
// the focus from whichever of its inputs the user moved on to
|
|
291
|
+
if (value === shown && holder.firstChild && textMode === !!holder.querySelector('.ge-style-shadow-text')) { return; }
|
|
292
|
+
shown = value;
|
|
293
|
+
|
|
294
|
+
if (textMode || !parts) {
|
|
295
|
+
textMode = !!value || textMode;
|
|
296
|
+
text(value);
|
|
297
|
+
} else {
|
|
298
|
+
builder(parts);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { element: holder, render: render, value: function() { return ge.hostStyle(node, property).value; } };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The field for one property of a node. `changed` is called after every
|
|
307
|
+
* write that went through, so the plugin can bring the rest of its
|
|
308
|
+
* sections up to date.
|
|
309
|
+
*/
|
|
310
|
+
export function createStyleField(ge, node, property, changed) {
|
|
311
|
+
var field = dom.element('div', { 'class': 'ge-style-field', 'data-ge-style-property': property.name });
|
|
312
|
+
var label = field.appendChild(dom.element('label', { 'class': 'ge-style-label' }, ge.t(property.labelKey)));
|
|
313
|
+
var render;
|
|
314
|
+
|
|
315
|
+
function current() {
|
|
316
|
+
return ge.hostStyle(node, property.name).value;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function commit(value, input) {
|
|
320
|
+
var ok = write(ge, node, property.name, value);
|
|
321
|
+
mark(input, ok, ge);
|
|
322
|
+
if (ok) { changed(); }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (property.type === 'select') {
|
|
326
|
+
var select = field.appendChild(dom.element('select', { 'class': 'form-select form-select-sm' }));
|
|
327
|
+
select.addEventListener('change', function() { commit(select.value, select); });
|
|
328
|
+
render = function() {
|
|
329
|
+
var value = current();
|
|
330
|
+
var values = property.values.slice();
|
|
331
|
+
if (value && values.indexOf(value) === -1) { values.push(value); }
|
|
332
|
+
|
|
333
|
+
select.innerHTML = '';
|
|
334
|
+
select.appendChild(dom.element('option', { value: '' }, ''));
|
|
335
|
+
values.forEach(function(each) { select.appendChild(dom.element('option', { value: each }, each)); });
|
|
336
|
+
select.value = value;
|
|
337
|
+
};
|
|
338
|
+
} else if (property.type === 'color') {
|
|
339
|
+
var color = colorControl(function(value, input) { commit(value, input); });
|
|
340
|
+
field.appendChild(color.element);
|
|
341
|
+
render = function() { color.set(current()); };
|
|
342
|
+
} else if (property.type === 'url') {
|
|
343
|
+
var url = field.appendChild(textInput());
|
|
344
|
+
url.addEventListener('change', function() { commit(urlValue(url.value), url); });
|
|
345
|
+
render = function() { fill(url, shownUrl(current())); };
|
|
346
|
+
} else if (property.type === 'shadow') {
|
|
347
|
+
var shadow = shadowControl(ge, node, property.name, changed);
|
|
348
|
+
field.appendChild(shadow.element);
|
|
349
|
+
render = shadow.render;
|
|
350
|
+
} else {
|
|
351
|
+
var input = field.appendChild(textInput());
|
|
352
|
+
input.addEventListener('change', function() { commit(input.value, input); });
|
|
353
|
+
render = function() { fill(input, current()); };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
var control = field.querySelector('input, select');
|
|
357
|
+
if (control) {
|
|
358
|
+
control.id = 'ge-style-field-' + (++fieldCounter);
|
|
359
|
+
label.setAttribute('for', control.id);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
field.appendChild(dom.element('small', { 'class': 'ge-style-note ge-style-overridden' }));
|
|
363
|
+
|
|
364
|
+
renderers.set(field, function() {
|
|
365
|
+
render();
|
|
366
|
+
|
|
367
|
+
// A Bootstrap utility on the node wins over the value: every one is !important
|
|
368
|
+
var winner = current() ? overriding(property.name, classesOf(node)) : null;
|
|
369
|
+
var note = dom.child(field, '.ge-style-overridden');
|
|
370
|
+
note.textContent = winner ? ge.t('style.overridden', { 'class': winner }) : '';
|
|
371
|
+
dom.toggle(note, !!winner);
|
|
372
|
+
});
|
|
373
|
+
renderStyleField(field);
|
|
374
|
+
|
|
375
|
+
return field;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/** Fill a field from its node again. */
|
|
379
|
+
export function renderStyleField(field) {
|
|
380
|
+
var render = renderers.get(field);
|
|
381
|
+
if (render) { render(); }
|
|
382
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flex, a part of the style plugin: a flex container's direction, wrap,
|
|
3
|
+
* alignment and gaps, and how its children grow, shrink and fill, per
|
|
4
|
+
* breakpoint, with Bootstrap's flex-{breakpoint}-*, justify-content-*,
|
|
5
|
+
* align-items-*, align-content-*, gap-* and row-/column-gap-* classes.
|
|
6
|
+
*
|
|
7
|
+
* Its families, and whatever it does on the canvas, as a function of the
|
|
8
|
+
* handle and the spacing options, whose scale the gaps share:
|
|
9
|
+
* grideditor.style.js puts them in its Flex section.
|
|
10
|
+
*/
|
|
11
|
+
import { GridEditor } from '../grideditor.js';
|
|
12
|
+
|
|
13
|
+
Object.assign(GridEditor.locales.en, {
|
|
14
|
+
'utility.flex_direction': 'Direction',
|
|
15
|
+
'utility.flex_wrap': 'Wrap',
|
|
16
|
+
'utility.justify_content': 'Justify columns',
|
|
17
|
+
'utility.align_items': 'Align columns',
|
|
18
|
+
'utility.align_content': 'Align lines',
|
|
19
|
+
'utility.gap': 'Gap',
|
|
20
|
+
'utility.row_gap': 'Row gap',
|
|
21
|
+
'utility.column_gap': 'Column gap',
|
|
22
|
+
'utility.flex_fill': 'Fill',
|
|
23
|
+
'utility.flex_grow': 'Grow',
|
|
24
|
+
'utility.flex_shrink': 'Shrink',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** What Bootstrap's value names come to in css. */
|
|
28
|
+
var FLEX = {
|
|
29
|
+
start: 'flex-start',
|
|
30
|
+
end: 'flex-end',
|
|
31
|
+
center: 'center',
|
|
32
|
+
between: 'space-between',
|
|
33
|
+
around: 'space-around',
|
|
34
|
+
evenly: 'space-evenly',
|
|
35
|
+
baseline: 'baseline',
|
|
36
|
+
stretch: 'stretch',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
var GAPS = ['0', '1', '2', '3', '4', '5'];
|
|
40
|
+
|
|
41
|
+
/** Bootstrap's $spacers. */
|
|
42
|
+
var SCALE = ['0', '.25rem', '.5rem', '1rem', '1.5rem', '3rem'];
|
|
43
|
+
|
|
44
|
+
/** A flex container: a row is one already, and has its gutters for gaps. */
|
|
45
|
+
var CONTAINERS = ['row', 'column', 'element', 'container'];
|
|
46
|
+
|
|
47
|
+
/** What sits in a flex container and can grow, shrink or fill it. */
|
|
48
|
+
var ITEMS = ['column', 'element', 'container'];
|
|
49
|
+
|
|
50
|
+
export function flexPart(ge, spacing) {
|
|
51
|
+
|
|
52
|
+
var scale = (spacing && spacing.scale) || SCALE;
|
|
53
|
+
|
|
54
|
+
/** A family whose preview is its one css property, the node's own without a class. */
|
|
55
|
+
function family(definition) {
|
|
56
|
+
var property = definition.property || definition.name;
|
|
57
|
+
|
|
58
|
+
return Object.assign({
|
|
59
|
+
prefix: definition.name,
|
|
60
|
+
panel: false,
|
|
61
|
+
preview: function(value, node) {
|
|
62
|
+
var styles = {};
|
|
63
|
+
styles[property] = value === null ? ge.bareStyle(node, definition.name, property) : definition.css(value);
|
|
64
|
+
return styles;
|
|
65
|
+
},
|
|
66
|
+
}, definition);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** An alignment family: its values are Bootstrap's names, and without a class it is normal. */
|
|
70
|
+
function alignment(definition) {
|
|
71
|
+
return family(Object.assign({
|
|
72
|
+
appliesTo: CONTAINERS,
|
|
73
|
+
preview: function(value) {
|
|
74
|
+
var styles = {};
|
|
75
|
+
styles[definition.name] = value === null ? 'normal' : FLEX[value];
|
|
76
|
+
return styles;
|
|
77
|
+
},
|
|
78
|
+
}, definition));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function gap(definition) {
|
|
82
|
+
return family(Object.assign({
|
|
83
|
+
values: GAPS,
|
|
84
|
+
appliesTo: ITEMS,
|
|
85
|
+
css: function(value) { return scale[value]; },
|
|
86
|
+
}, definition));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function same(value) {
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
families: [
|
|
95
|
+
family({
|
|
96
|
+
name: 'flex-direction',
|
|
97
|
+
prefix: 'flex',
|
|
98
|
+
labelKey: 'utility.flex_direction',
|
|
99
|
+
values: ['row', 'row-reverse', 'column', 'column-reverse'],
|
|
100
|
+
appliesTo: CONTAINERS,
|
|
101
|
+
css: same,
|
|
102
|
+
}),
|
|
103
|
+
family({
|
|
104
|
+
name: 'flex-wrap',
|
|
105
|
+
prefix: 'flex',
|
|
106
|
+
labelKey: 'utility.flex_wrap',
|
|
107
|
+
values: ['wrap', 'nowrap', 'wrap-reverse'],
|
|
108
|
+
appliesTo: CONTAINERS,
|
|
109
|
+
css: same,
|
|
110
|
+
}),
|
|
111
|
+
alignment({
|
|
112
|
+
name: 'justify-content',
|
|
113
|
+
labelKey: 'utility.justify_content',
|
|
114
|
+
values: ['start', 'center', 'end', 'between', 'around', 'evenly'],
|
|
115
|
+
}),
|
|
116
|
+
alignment({
|
|
117
|
+
name: 'align-items',
|
|
118
|
+
labelKey: 'utility.align_items',
|
|
119
|
+
values: ['start', 'center', 'end', 'baseline', 'stretch'],
|
|
120
|
+
}),
|
|
121
|
+
alignment({
|
|
122
|
+
name: 'align-content',
|
|
123
|
+
labelKey: 'utility.align_content',
|
|
124
|
+
values: ['start', 'center', 'end', 'between', 'around', 'stretch'],
|
|
125
|
+
}),
|
|
126
|
+
gap({ name: 'gap', labelKey: 'utility.gap' }),
|
|
127
|
+
gap({ name: 'row-gap', labelKey: 'utility.row_gap' }),
|
|
128
|
+
gap({ name: 'column-gap', labelKey: 'utility.column_gap' }),
|
|
129
|
+
family({
|
|
130
|
+
name: 'flex-fill',
|
|
131
|
+
prefix: 'flex',
|
|
132
|
+
property: 'flex',
|
|
133
|
+
labelKey: 'utility.flex_fill',
|
|
134
|
+
values: ['fill'],
|
|
135
|
+
appliesTo: ITEMS,
|
|
136
|
+
css: function() { return '1 1 auto'; },
|
|
137
|
+
}),
|
|
138
|
+
family({
|
|
139
|
+
name: 'flex-grow',
|
|
140
|
+
prefix: 'flex',
|
|
141
|
+
labelKey: 'utility.flex_grow',
|
|
142
|
+
values: ['grow-0', 'grow-1'],
|
|
143
|
+
appliesTo: ITEMS,
|
|
144
|
+
css: function(value) { return value.slice(-1); },
|
|
145
|
+
}),
|
|
146
|
+
family({
|
|
147
|
+
name: 'flex-shrink',
|
|
148
|
+
prefix: 'flex',
|
|
149
|
+
labelKey: 'utility.flex_shrink',
|
|
150
|
+
values: ['shrink-0', 'shrink-1'],
|
|
151
|
+
appliesTo: ITEMS,
|
|
152
|
+
css: function(value) { return value.slice(-1); },
|
|
153
|
+
}),
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -1,19 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Float, a part of the style plugin: floating an element per breakpoint, with Bootstrap's
|
|
3
|
+
* float-{breakpoint}-start, -end and -none classes.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* area per breakpoint, with the text around it, using Bootstrap's
|
|
7
|
-
* float-{breakpoint}-start, -end and -none classes. What it can ask the
|
|
8
|
-
* editor for is the handle its factory is called with, described in
|
|
9
|
-
* docs/plugins.md.
|
|
10
|
-
*
|
|
11
|
-
* <script src="dist/grideditor.min.js"></script>
|
|
12
|
-
* <script src="dist/plugins/grideditor.elements.min.js"></script>
|
|
13
|
-
* <script src="dist/plugins/grideditor.float.min.js"></script>
|
|
14
|
-
*
|
|
15
|
-
* Elements only. A floated row or column stops being part of the grid, and a
|
|
16
|
-
* container sits beside the content areas rather than in their text.
|
|
5
|
+
* Its families, and whatever it does on the canvas, as a function of the
|
|
6
|
+
* handle: grideditor.style.js puts them in its sections.
|
|
17
7
|
*/
|
|
18
8
|
import { GridEditor } from '../grideditor.js';
|
|
19
9
|
|
|
@@ -27,7 +17,7 @@ Object.assign(GridEditor.locales.en, {
|
|
|
27
17
|
/** Start and end are left and right: Bootstrap's css is left to right. */
|
|
28
18
|
var CSS = { start: 'left', end: 'right', none: 'none' };
|
|
29
19
|
|
|
30
|
-
|
|
20
|
+
export function floatPart(ge) {
|
|
31
21
|
|
|
32
22
|
function label(value) {
|
|
33
23
|
if (value === 'start') { return ge.t('utility.float_start'); }
|
|
@@ -51,4 +41,4 @@ GridEditor.utilities.float = function(ge) {
|
|
|
51
41
|
},
|
|
52
42
|
}],
|
|
53
43
|
};
|
|
54
|
-
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style plugin's settings, `style` in the editor's options:
|
|
3
|
+
*
|
|
4
|
+
* style: {
|
|
5
|
+
* sections: { border: { properties: [...], catalog: false }, position: false, … },
|
|
6
|
+
* spacing: { values, scale }, // the values offered, and what 0 to 5 come to
|
|
7
|
+
* visibility: { drawer }, // false leaves the eye out of the drawers
|
|
8
|
+
* }
|
|
9
|
+
*
|
|
10
|
+
* Every section is on unless it is turned off.
|
|
11
|
+
*/
|
|
12
|
+
import { SECTIONS, section } from './sections.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What each section shows: { key: { properties, catalog } } for the ones
|
|
16
|
+
* that are on, in the sections' own order.
|
|
17
|
+
*/
|
|
18
|
+
function resolveSections(ge, given) {
|
|
19
|
+
var resolved = {};
|
|
20
|
+
|
|
21
|
+
Object.keys(given || {}).forEach(function(key) {
|
|
22
|
+
if (!section(key)) { ge.warn('style.sections: there is no "' + key + '" section: ignored'); }
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
SECTIONS.forEach(function(each) {
|
|
26
|
+
var option = given && given[each.key] !== undefined ? given[each.key] : true;
|
|
27
|
+
if (option === false) { return; }
|
|
28
|
+
|
|
29
|
+
var own = each.properties.map(function(property) { return property.name; });
|
|
30
|
+
var properties = each.properties;
|
|
31
|
+
var catalog = true;
|
|
32
|
+
|
|
33
|
+
if (option && typeof option === 'object') {
|
|
34
|
+
if (Array.isArray(option.properties)) {
|
|
35
|
+
option.properties.forEach(function(name) {
|
|
36
|
+
if (own.indexOf(name) === -1) {
|
|
37
|
+
ge.warn('style.sections.' + each.key + ': "' + name + '" is not one of its properties: ignored');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
properties = option.properties.filter(function(name) { return own.indexOf(name) !== -1; })
|
|
41
|
+
.map(function(name) { return each.properties[own.indexOf(name)]; });
|
|
42
|
+
}
|
|
43
|
+
if (option.catalog === false) { catalog = false; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
resolved[each.key] = { section: each, properties: properties, catalog: catalog };
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return resolved;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resolveOptions(ge) {
|
|
53
|
+
var style = ge.settings.style || {};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
sections: resolveSections(ge, style.sections),
|
|
57
|
+
spacing: style.spacing,
|
|
58
|
+
visibility: style.visibility,
|
|
59
|
+
};
|
|
60
|
+
}
|