@themarioga/grid-editor 7.2.0 → 7.3.0
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 +41 -0
- package/README.md +83 -14
- package/UPGRADING.md +31 -0
- package/dist/grideditor.bundle.min.js +2 -2
- package/dist/grideditor.css +102 -0
- package/dist/grideditor.d.ts +18 -0
- package/dist/grideditor.esm.js +291 -31
- package/dist/grideditor.js +291 -31
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/grideditor.min.js +1 -1
- package/dist/grideditor.min.js.map +3 -3
- package/dist/locales/grideditor.es.esm.js +67 -0
- package/dist/locales/grideditor.es.js +67 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +2 -2
- package/dist/plugins/grideditor.float.esm.js +10 -1
- package/dist/plugins/grideditor.float.js +8 -2
- package/dist/plugins/grideditor.float.min.js +1 -1
- package/dist/plugins/grideditor.float.min.js.map +4 -4
- package/dist/plugins/grideditor.spacing.esm.js +12 -3
- package/dist/plugins/grideditor.spacing.js +9 -3
- package/dist/plugins/grideditor.spacing.min.js +1 -1
- package/dist/plugins/grideditor.spacing.min.js.map +4 -4
- package/dist/plugins/grideditor.style.esm.js +1593 -0
- package/dist/plugins/grideditor.style.js +1592 -0
- package/dist/plugins/grideditor.style.min.js +2 -0
- package/dist/plugins/grideditor.style.min.js.map +7 -0
- package/dist/plugins/grideditor.textalign.esm.js +10 -1
- package/dist/plugins/grideditor.textalign.js +8 -2
- package/dist/plugins/grideditor.textalign.min.js +1 -1
- package/dist/plugins/grideditor.textalign.min.js.map +4 -4
- package/dist/plugins/grideditor.visibility.esm.js +12 -3
- package/dist/plugins/grideditor.visibility.js +9 -3
- package/dist/plugins/grideditor.visibility.min.js +1 -1
- package/dist/plugins/grideditor.visibility.min.js.map +4 -4
- package/docs/locale-keys.md +86 -7
- package/docs/plugins.md +49 -5
- package/example/index.html +14 -0
- package/example/style.html +151 -0
- package/example/utilities.html +15 -11
- package/package.json +1 -1
- package/src/js/grideditor.js +365 -27
- package/src/js/locales/grideditor.es.js +67 -0
- package/src/js/plugins/grideditor.float.js +8 -33
- package/src/js/plugins/grideditor.spacing.js +8 -273
- package/src/js/plugins/grideditor.style.js +179 -0
- package/src/js/plugins/grideditor.textalign.js +8 -39
- package/src/js/plugins/grideditor.visibility.js +8 -147
- package/src/js/style/accordion.js +157 -0
- package/src/js/style/catalog.js +76 -0
- package/src/js/style/custom.js +84 -0
- package/src/js/style/fields.js +382 -0
- package/src/js/style/float.js +46 -0
- package/src/js/style/options.js +75 -0
- package/src/js/style/sections.js +271 -0
- package/src/js/style/spacing.js +286 -0
- package/src/js/style/textalign.js +52 -0
- package/src/js/style/visibility.js +160 -0
- package/src/less/grideditor.less +111 -0
- package/types/grideditor.d.ts +18 -0
|
@@ -17,155 +17,16 @@
|
|
|
17
17
|
*
|
|
18
18
|
* utilities.visibility.drawer: false leaves the eye out of the drawers, and
|
|
19
19
|
* the panel field is the only control.
|
|
20
|
+
*
|
|
21
|
+
* Deprecated since 7.3, and gone in 8.0: grideditor.style.js carries this
|
|
22
|
+
* plugin's families, and edits them in its own sections. This file is that
|
|
23
|
+
* part on its own, as it was, and says so once per editor. Loaded beside
|
|
24
|
+
* the style plugin, it stands down.
|
|
20
25
|
*/
|
|
21
26
|
import { GridEditor } from '../grideditor.js';
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
Object.assign(GridEditor.locales.en, {
|
|
25
|
-
'utility.visibility': 'Visibility',
|
|
26
|
-
'utility.visibility_hidden': 'Hidden',
|
|
27
|
-
'utility.visibility_shown': 'Shown',
|
|
28
|
-
'tool.hide_in_view': 'Hide in this view',
|
|
29
|
-
'tool.show_in_view': 'Show in this view',
|
|
30
|
-
'badge.hidden_in': 'Hidden at {breakpoints}',
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
/** A node that carries any class of the family, at any breakpoint. */
|
|
34
|
-
var CLASS_PATTERN = /(?:^|\s)d-(?:(?:sm|md|lg|xl|xxl)-)?(?:none|block|flex)(?:\s|$)/;
|
|
35
|
-
|
|
36
|
-
var NODES = '.row, .column, .ge-content, .ge-element, [data-ge-container]';
|
|
27
|
+
import { visibilityPart } from '../style/visibility.js';
|
|
37
28
|
|
|
38
29
|
GridEditor.utilities.visibility = function(ge) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/** What "shown" is written as on this kind of node. */
|
|
43
|
-
function shown(kind) {
|
|
44
|
-
return kind === 'row' ? 'flex' : 'block';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function applies(node, kind) {
|
|
48
|
-
return kind === 'row' || kind === 'column' || kind === 'element' || kind === 'text' || kind === 'plain' ||
|
|
49
|
-
dom.is(node, '[data-ge-container]');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function hiddenAt(node, view) {
|
|
53
|
-
return ge.getUtility(node, 'visibility', view) === 'none';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** The breakpoints at which the node is hidden, smallest first. */
|
|
57
|
-
function hiddenTiers(node) {
|
|
58
|
-
return ge.breakpoints.filter(function(key) { return hiddenAt(node, key); });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Whether the node is hidden in the view being edited. In the all view
|
|
63
|
-
* that means hidden at every breakpoint, since "hidden at some" is what
|
|
64
|
-
* the badge is for.
|
|
65
|
-
*/
|
|
66
|
-
function hiddenHere(node) {
|
|
67
|
-
return ge.view() === 'all'
|
|
68
|
-
? hiddenTiers(node).length === ge.breakpoints.length
|
|
69
|
-
: hiddenAt(node, ge.view());
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** What the breakpoint below the view shows, which an inherit falls back to. */
|
|
73
|
-
function hiddenBelow(node) {
|
|
74
|
-
var index = ge.breakpoints.indexOf(ge.view());
|
|
75
|
-
|
|
76
|
-
return index > 0 && hiddenAt(node, ge.breakpoints[index - 1]);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Hide or show the node in the view being edited, with as few classes as
|
|
81
|
-
* that takes. In a breakpoint view that is often none at all: showing a
|
|
82
|
-
* node the breakpoint below shows removes this breakpoint's d-*-none
|
|
83
|
-
* rather than writing a d-*-block on top of it. In the all view a node
|
|
84
|
-
* is hidden everywhere or shown everywhere, and shown is no class.
|
|
85
|
-
*/
|
|
86
|
-
function toggle(node, kind) {
|
|
87
|
-
var hide = !hiddenHere(node);
|
|
88
|
-
var value;
|
|
89
|
-
|
|
90
|
-
if (ge.view() === 'all') {
|
|
91
|
-
value = hide ? 'none' : null;
|
|
92
|
-
} else if (hide) {
|
|
93
|
-
value = hiddenBelow(node) ? null : 'none';
|
|
94
|
-
} else {
|
|
95
|
-
value = hiddenBelow(node) ? shown(kind) : null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
ge.setUtility(node, 'visibility', value, { source: 'tool' });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* The canvas shows each node as the view sees it. ge-visibility keeps a
|
|
103
|
-
* node Bootstrap would hide on the canvas, ge-hidden-in-view fades it,
|
|
104
|
-
* and data-ge-hidden-in carries the badge text for the all view.
|
|
105
|
-
*/
|
|
106
|
-
function mark(scope) {
|
|
107
|
-
dom.selfAndAll(scope, NODES).forEach(function(node) {
|
|
108
|
-
var kind = ge.kindOf(node);
|
|
109
|
-
var carries = applies(node, kind) && CLASS_PATTERN.test(node.getAttribute('class') || '');
|
|
110
|
-
var tiers = carries ? hiddenTiers(node) : [];
|
|
111
|
-
var here = carries && hiddenHere(node);
|
|
112
|
-
var partly = carries && ge.view() === 'all' && tiers.length > 0 && !here;
|
|
113
|
-
|
|
114
|
-
dom.toggleClass(node, 'ge-visibility', carries);
|
|
115
|
-
dom.toggleClass(node, 'ge-hidden-in-view', here);
|
|
116
|
-
|
|
117
|
-
if (partly) {
|
|
118
|
-
node.setAttribute('data-ge-hidden-in', ge.t('badge.hidden_in', { breakpoints: tiers.join(', ') }));
|
|
119
|
-
} else {
|
|
120
|
-
node.removeAttribute('data-ge-hidden-in');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
dom.children(node, '.ge-tools-drawer').forEach(function(drawer) {
|
|
124
|
-
dom.children(drawer, '.ge-visibility-tool').forEach(function(tool) {
|
|
125
|
-
tool.setAttribute('title', here ? ge.t('tool.show_in_view') : ge.t('tool.hide_in_view'));
|
|
126
|
-
dom.all(tool, 'i').forEach(function(icon) {
|
|
127
|
-
icon.setAttribute('class', here ? 'bi bi-eye-slash' : 'bi bi-eye');
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function unmark() {
|
|
135
|
-
dom.all(ge.canvas, '.ge-visibility, .ge-hidden-in-view, [data-ge-hidden-in]').forEach(function(node) {
|
|
136
|
-
dom.removeClass(node, 'ge-visibility ge-hidden-in-view');
|
|
137
|
-
node.removeAttribute('data-ge-hidden-in');
|
|
138
|
-
dom.dropEmptyClass(node);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
families: [{
|
|
144
|
-
name: 'visibility',
|
|
145
|
-
prefix: 'd',
|
|
146
|
-
values: ['none', 'block', 'flex'],
|
|
147
|
-
appliesTo: ['row', 'column', 'text', 'element', 'container'],
|
|
148
|
-
labelKey: 'utility.visibility',
|
|
149
|
-
|
|
150
|
-
/** Hidden, or shown the way this kind of node is shown. */
|
|
151
|
-
choices: function(node, kind) {
|
|
152
|
-
return ['none', shown(kind)];
|
|
153
|
-
},
|
|
154
|
-
|
|
155
|
-
label: function(value) {
|
|
156
|
-
return value === 'none' ? ge.t('utility.visibility_hidden') : ge.t('utility.visibility_shown');
|
|
157
|
-
},
|
|
158
|
-
}],
|
|
159
|
-
|
|
160
|
-
drawerTools: function(drawer, node, kind) {
|
|
161
|
-
if (!options.drawer || !applies(node, kind)) { return; }
|
|
162
|
-
|
|
163
|
-
ge.createTool(drawer, ge.t('tool.hide_in_view'), 'ge-visibility-tool', 'bi bi-eye', function() {
|
|
164
|
-
toggle(node, kind);
|
|
165
|
-
});
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
onRefresh: mark,
|
|
169
|
-
onDeinit: unmark,
|
|
170
|
-
};
|
|
30
|
+
ge.warn('the "visibility" plugin is deprecated and will be removed in 8.0: load grideditor.style.js');
|
|
31
|
+
return visibilityPart(ge, ge.settings.utilities.visibility);
|
|
171
32
|
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style plugin's accordion for one node: a section per group of
|
|
3
|
+
* properties, each with the merged utilities' per breakpoint fields, the
|
|
4
|
+
* inline fields and the catalog - as far as each applies to the node.
|
|
5
|
+
*
|
|
6
|
+
* Bootstrap's accordion markup, opened and closed here, so it needs none of
|
|
7
|
+
* Bootstrap's javascript. One section is open at a time, and the one the
|
|
8
|
+
* user opened last is open again on the next node's panel.
|
|
9
|
+
*/
|
|
10
|
+
import * as dom from '../dom.js';
|
|
11
|
+
import { offeredOn } from './sections.js';
|
|
12
|
+
import { createStyleField, renderStyleField } from './fields.js';
|
|
13
|
+
import { createCatalog, renderCatalog } from './catalog.js';
|
|
14
|
+
import { createCustom } from './custom.js';
|
|
15
|
+
|
|
16
|
+
/** The kinds of node that take inline style: not a text, nor plain content. */
|
|
17
|
+
export function takesInlineStyle(node, kind) {
|
|
18
|
+
if (kind === 'row' || kind === 'column' || kind === 'element' || kind === 'section') { return true; }
|
|
19
|
+
if (dom.is(node, '[data-ge-container]')) { return true; }
|
|
20
|
+
|
|
21
|
+
// A pane of a container, whatever its container calls it
|
|
22
|
+
return !!dom.child(node, '.ge-tools-drawer.ge-pane-drawer');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Whether a merged family applies to the node, as the editor decides it. */
|
|
26
|
+
function familyApplies(family, node, kind) {
|
|
27
|
+
if (kind === 'plain') { kind = 'text'; }
|
|
28
|
+
if (family.appliesTo.indexOf(kind) !== -1) { return true; }
|
|
29
|
+
|
|
30
|
+
return family.appliesTo.indexOf('container') !== -1 && dom.is(node, '[data-ge-container]');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What a merged part shows in its section for a node: the spacing part
|
|
35
|
+
* its own panel, the others the field of their one family.
|
|
36
|
+
*/
|
|
37
|
+
function partContent(ge, part, node, kind) {
|
|
38
|
+
if (part.panel) { return part.panel(node, kind); }
|
|
39
|
+
|
|
40
|
+
var family = part.families[0];
|
|
41
|
+
return familyApplies(family, node, kind) ? ge.utilityField(node, family.name) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* `context` is the plugin's: its resolved sections, its parts by name, the
|
|
46
|
+
* state it keeps for the editor ({ open }) and `opened(key)`, called when
|
|
47
|
+
* the user opens or closes a section.
|
|
48
|
+
*/
|
|
49
|
+
export function createAccordion(ge, node, kind, context) {
|
|
50
|
+
var inline = takesInlineStyle(node, kind);
|
|
51
|
+
var accordion = dom.element('div', { 'class': 'accordion ge-style' });
|
|
52
|
+
var fields = [];
|
|
53
|
+
var catalogs = [];
|
|
54
|
+
var notes = [];
|
|
55
|
+
var custom = null;
|
|
56
|
+
var customItem = null;
|
|
57
|
+
var changed = function() { render(); };
|
|
58
|
+
|
|
59
|
+
function item(key, labelKey) {
|
|
60
|
+
var entry = accordion.appendChild(dom.element('div', { 'class': 'accordion-item', 'data-ge-style-section': key }));
|
|
61
|
+
var header = entry.appendChild(dom.element('h2', { 'class': 'accordion-header' }));
|
|
62
|
+
var button = header.appendChild(dom.element('button', {
|
|
63
|
+
type: 'button',
|
|
64
|
+
'class': 'accordion-button collapsed',
|
|
65
|
+
'aria-expanded': 'false',
|
|
66
|
+
}, ge.t(labelKey)));
|
|
67
|
+
var collapse = entry.appendChild(dom.element('div', { 'class': 'accordion-collapse collapse' }));
|
|
68
|
+
var body = collapse.appendChild(dom.element('div', { 'class': 'accordion-body' }));
|
|
69
|
+
|
|
70
|
+
button.addEventListener('click', function() {
|
|
71
|
+
var opening = !dom.hasClass(collapse, 'show');
|
|
72
|
+
context.state.open = opening ? key : null;
|
|
73
|
+
// Every panel is built on init, so the others are told as well:
|
|
74
|
+
// whoever opened Border on one node wants it on the next
|
|
75
|
+
context.opened(context.state.open);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return { entry: entry, body: body };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toggle(entry, shown) {
|
|
82
|
+
dom.toggleClass(dom.one(entry, '.accordion-collapse'), 'show', shown);
|
|
83
|
+
var button = dom.one(entry, '.accordion-button');
|
|
84
|
+
dom.toggleClass(button, 'collapsed', !shown);
|
|
85
|
+
button.setAttribute('aria-expanded', shown ? 'true' : 'false');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Object.keys(context.sections).forEach(function(key) {
|
|
89
|
+
var resolved = context.sections[key];
|
|
90
|
+
var section = resolved.section;
|
|
91
|
+
|
|
92
|
+
if (section.custom) {
|
|
93
|
+
if (inline) { customItem = item(key, section.labelKey); }
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var parts = section.parts.map(function(name) {
|
|
98
|
+
return context.parts[name] ? partContent(ge, context.parts[name], node, kind) : null;
|
|
99
|
+
}).filter(Boolean);
|
|
100
|
+
var properties = inline ? resolved.properties.filter(function(property) { return offeredOn(property, kind); }) : [];
|
|
101
|
+
var catalog = inline && resolved.catalog ? createCatalog(ge, node, kind, section.catalog) : null;
|
|
102
|
+
|
|
103
|
+
if (!parts.length && !properties.length && !catalog) { return; }
|
|
104
|
+
|
|
105
|
+
var body = item(key, section.labelKey).body;
|
|
106
|
+
parts.forEach(function(part) { body.appendChild(dom.addClass(part, 'ge-style-part')); });
|
|
107
|
+
|
|
108
|
+
if (properties.length) {
|
|
109
|
+
var note = body.appendChild(dom.element('small', { 'class': 'ge-style-note ge-style-all-sizes' }, ge.t('style.all_sizes')));
|
|
110
|
+
notes.push(note);
|
|
111
|
+
|
|
112
|
+
var grid = body.appendChild(dom.element('div', { 'class': 'ge-style-fields' }));
|
|
113
|
+
properties.forEach(function(property) {
|
|
114
|
+
var field = createStyleField(ge, node, property, changed);
|
|
115
|
+
fields.push(field);
|
|
116
|
+
grid.appendChild(field);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (catalog) {
|
|
121
|
+
catalogs.push(catalog);
|
|
122
|
+
body.appendChild(catalog);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Last, since it shows what the fields made above do not
|
|
127
|
+
if (customItem) {
|
|
128
|
+
custom = createCustom(ge, node, fields.map(function(field) {
|
|
129
|
+
return field.getAttribute('data-ge-style-property');
|
|
130
|
+
}), changed);
|
|
131
|
+
customItem.body.appendChild(custom.element);
|
|
132
|
+
accordion.appendChild(customItem.entry);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!accordion.children.length) { return null; }
|
|
136
|
+
|
|
137
|
+
/** Open this section and no other; null closes them all. */
|
|
138
|
+
function open(key) {
|
|
139
|
+
dom.all(accordion, '.accordion-item').forEach(function(entry) {
|
|
140
|
+
toggle(entry, entry.getAttribute('data-ge-style-section') === key);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
open(context.state.open);
|
|
145
|
+
|
|
146
|
+
/** Fill everything from the node again: after a write, a change of classes or view. */
|
|
147
|
+
function render() {
|
|
148
|
+
fields.forEach(renderStyleField);
|
|
149
|
+
catalogs.forEach(function(catalog) { renderCatalog(catalog, node); });
|
|
150
|
+
if (custom) { custom.render(); }
|
|
151
|
+
notes.forEach(function(note) { dom.toggle(note, ge.view() !== 'all'); });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
render();
|
|
155
|
+
|
|
156
|
+
return { element: accordion, render: render, open: open };
|
|
157
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style plugin's catalog: Bootstrap's classes for what a section
|
|
3
|
+
* styles, as chips that put a class on the node or take it off.
|
|
4
|
+
*
|
|
5
|
+
* A chip never touches the node itself. It writes the panel's classes
|
|
6
|
+
* field and fires its change, as the user typing there would, so the
|
|
7
|
+
* editor keeps the node's own classes apart from its editing ones, and the
|
|
8
|
+
* Responsive section and the preview follow. Within an exclusive group,
|
|
9
|
+
* choosing one class takes the others off.
|
|
10
|
+
*/
|
|
11
|
+
import * as dom from '../dom.js';
|
|
12
|
+
import { offeredOn } from './sections.js';
|
|
13
|
+
|
|
14
|
+
function classesOf(node) {
|
|
15
|
+
return (node.getAttribute('class') || '').split(/\s+/).filter(Boolean);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Put a class on or off through the node's classes field. */
|
|
19
|
+
function choose(ge, node, group, name) {
|
|
20
|
+
var details = ge.detailsOf(node);
|
|
21
|
+
var input = details ? dom.one(details, '.ge-classes') : null;
|
|
22
|
+
if (!input) { return; }
|
|
23
|
+
|
|
24
|
+
var on = classesOf(node).indexOf(name) !== -1;
|
|
25
|
+
var list = input.value.split(/\s+/).filter(Boolean).filter(function(each) {
|
|
26
|
+
if (each === name) { return false; }
|
|
27
|
+
return !(group.exclusive && !on && group.classes.indexOf(each) !== -1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!on) { list.push(name); }
|
|
31
|
+
|
|
32
|
+
input.value = list.join(' ');
|
|
33
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A section's catalog for a node, or null when none of its groups is
|
|
38
|
+
* offered on this kind of node.
|
|
39
|
+
*/
|
|
40
|
+
export function createCatalog(ge, node, kind, groups) {
|
|
41
|
+
var offered = groups.filter(function(group) { return offeredOn(group, kind); });
|
|
42
|
+
if (!offered.length) { return null; }
|
|
43
|
+
|
|
44
|
+
var box = dom.element('div', { 'class': 'ge-style-catalog' });
|
|
45
|
+
box.appendChild(dom.element('span', { 'class': 'ge-style-label' }, ge.t('style.catalog')));
|
|
46
|
+
|
|
47
|
+
offered.forEach(function(group) {
|
|
48
|
+
var row = box.appendChild(dom.element('div', { 'class': 'ge-style-chips' }));
|
|
49
|
+
|
|
50
|
+
group.classes.forEach(function(name) {
|
|
51
|
+
var chip = row.appendChild(dom.element('button', {
|
|
52
|
+
type: 'button',
|
|
53
|
+
'class': 'btn btn-sm btn-outline-secondary ge-style-chip',
|
|
54
|
+
'data-ge-class': name,
|
|
55
|
+
'aria-pressed': 'false',
|
|
56
|
+
}, name));
|
|
57
|
+
|
|
58
|
+
chip.addEventListener('click', function() { choose(ge, node, group, name); });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
renderCatalog(box, node);
|
|
63
|
+
|
|
64
|
+
return box;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A chip is pressed while its class is on the node. */
|
|
68
|
+
export function renderCatalog(box, node) {
|
|
69
|
+
var classes = classesOf(node);
|
|
70
|
+
|
|
71
|
+
dom.all(box, '.ge-style-chip').forEach(function(chip) {
|
|
72
|
+
var on = classes.indexOf(chip.getAttribute('data-ge-class')) !== -1;
|
|
73
|
+
dom.toggleClass(chip, 'active', on);
|
|
74
|
+
chip.setAttribute('aria-pressed', on ? 'true' : 'false');
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The style plugin's free css: a textarea with every declaration of the
|
|
3
|
+
* node's own style that no field on show stands for - whatever the host
|
|
4
|
+
* wrote that the sections have no field for, and what a section the host
|
|
5
|
+
* turned off or narrowed would have shown.
|
|
6
|
+
*
|
|
7
|
+
* Compared longhand by longhand, which is how the browser holds a style: a
|
|
8
|
+
* margin written as a shorthand is its four sides, and a field for
|
|
9
|
+
* border-width stands for the four border-*-width. Editing the textarea
|
|
10
|
+
* writes those declarations again; the browser drops what it cannot
|
|
11
|
+
* parse, and the textarea shows what was kept.
|
|
12
|
+
*/
|
|
13
|
+
import * as dom from '../dom.js';
|
|
14
|
+
|
|
15
|
+
/** The longhands a property sets, as the browser expands it. */
|
|
16
|
+
function longhands(property) {
|
|
17
|
+
var probe = document.createElement('div').style;
|
|
18
|
+
var list = [];
|
|
19
|
+
|
|
20
|
+
probe.setProperty(property, 'initial');
|
|
21
|
+
for (var i = 0; i < probe.length; i++) { list.push(probe[i]); }
|
|
22
|
+
|
|
23
|
+
return list.length ? list : [property];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function declaration(cssText) {
|
|
27
|
+
var style = document.createElement('div').style;
|
|
28
|
+
style.cssText = cssText;
|
|
29
|
+
return style;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `properties` are the ones the node's fields on show stand for. */
|
|
33
|
+
export function createCustom(ge, node, properties, changed) {
|
|
34
|
+
var covered = {};
|
|
35
|
+
properties.forEach(function(property) {
|
|
36
|
+
longhands(property).forEach(function(longhand) { covered[longhand] = true; });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
var box = dom.element('div', { 'class': 'ge-style-custom' });
|
|
40
|
+
var textarea = box.appendChild(dom.element('textarea', {
|
|
41
|
+
'class': 'form-control form-control-sm font-monospace ge-style-custom-css',
|
|
42
|
+
rows: '4',
|
|
43
|
+
spellcheck: 'false',
|
|
44
|
+
'aria-label': ge.t('style.section_custom'),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
/** The declarations of the host's style no field stands for. */
|
|
48
|
+
function free() {
|
|
49
|
+
var style = declaration(ge.hostStyle(node));
|
|
50
|
+
var rest = document.createElement('div').style;
|
|
51
|
+
|
|
52
|
+
for (var i = 0; i < style.length; i++) {
|
|
53
|
+
var name = style[i];
|
|
54
|
+
if (!covered[name]) { rest.setProperty(name, style.getPropertyValue(name), style.getPropertyPriority(name)); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return rest;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
textarea.addEventListener('change', function() {
|
|
61
|
+
var before = free();
|
|
62
|
+
var wanted = declaration(textarea.value);
|
|
63
|
+
var i;
|
|
64
|
+
|
|
65
|
+
for (i = 0; i < before.length; i++) {
|
|
66
|
+
if (wanted.getPropertyValue(before[i]) === '') { ge.setHostStyle(node, before[i], ''); }
|
|
67
|
+
}
|
|
68
|
+
for (i = 0; i < wanted.length; i++) {
|
|
69
|
+
ge.setHostStyle(node, wanted[i], wanted.getPropertyValue(wanted[i]), wanted.getPropertyPriority(wanted[i]));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
render();
|
|
73
|
+
changed();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
function render() {
|
|
77
|
+
if (document.activeElement === textarea) { return; }
|
|
78
|
+
textarea.value = free().cssText.replace(/; /g, ';\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
render();
|
|
82
|
+
|
|
83
|
+
return { element: box, render: render };
|
|
84
|
+
}
|