@omicronenergy/oscd-shell 0.0.13 → 0.0.15
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/landing-page/landing-page.js +3 -2
- package/dist/landing-page/landing-page.js.map +1 -1
- package/dist/landing-page/landing-page.spec.js +4 -2
- package/dist/landing-page/landing-page.spec.js.map +1 -1
- package/dist/locales/de.d.ts +8 -0
- package/dist/locales/de.js +9 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/menus/files-menu.js +4 -5
- package/dist/menus/files-menu.js.map +1 -1
- package/dist/menus/plugins-menu.d.ts +6 -3
- package/dist/menus/plugins-menu.js +40 -6
- package/dist/menus/plugins-menu.js.map +1 -1
- package/dist/menus/plugins-menu.spec.js +1 -0
- package/dist/menus/plugins-menu.spec.js.map +1 -1
- package/dist/oscd-shell-design-tokens.js +139 -64
- package/dist/oscd-shell-design-tokens.js.map +1 -1
- package/dist/oscd-shell.d.ts +31 -22
- package/dist/oscd-shell.js +136 -73
- package/dist/oscd-shell.js.map +1 -1
- package/dist/side-panel/build-tree-nodes.spec.d.ts +1 -0
- package/dist/side-panel/build-tree-nodes.spec.js +50 -0
- package/dist/side-panel/build-tree-nodes.spec.js.map +1 -0
- package/dist/side-panel/editor-plugins-panel.d.ts +79 -7
- package/dist/side-panel/editor-plugins-panel.js +829 -92
- package/dist/side-panel/editor-plugins-panel.js.map +1 -1
- package/dist/side-panel/editor-plugins-panel.spec.js +469 -24
- package/dist/side-panel/editor-plugins-panel.spec.js.map +1 -1
- package/dist/utils/plugin-utils.d.ts +29 -5
- package/dist/utils/plugin-utils.js +73 -14
- package/dist/utils/plugin-utils.js.map +1 -1
- package/dist/utils/plugin-utils.spec.js +113 -4
- package/dist/utils/plugin-utils.spec.js.map +1 -1
- package/dist/utils/testing/plugin-helpers.d.ts +7 -3
- package/dist/utils/testing/plugin-helpers.js +21 -9
- package/dist/utils/testing/plugin-helpers.js.map +1 -1
- package/package.json +37 -38
|
@@ -1,165 +1,902 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { css, html, LitElement, nothing } from 'lit';
|
|
3
|
-
import { property } from 'lit/decorators.js';
|
|
3
|
+
import { property, state } from 'lit/decorators.js';
|
|
4
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
4
5
|
import { ScopedElementsMixin } from '@open-wc/scoped-elements/lit-element.js';
|
|
5
|
-
import { localized } from '@lit/localize';
|
|
6
|
+
import { localized, msg, str } from '@lit/localize';
|
|
6
7
|
import { OscdIconButton } from '@omicronenergy/oscd-ui/iconbutton/OscdIconButton.js';
|
|
7
8
|
import { OscdIcon } from '@omicronenergy/oscd-ui/icon/OscdIcon.js';
|
|
8
|
-
import { OscdList } from '@omicronenergy/oscd-ui/list/OscdList.js';
|
|
9
9
|
import { OscdListItem } from '@omicronenergy/oscd-ui/list/OscdListItem.js';
|
|
10
|
-
import {
|
|
10
|
+
import { OscdMenu } from '@omicronenergy/oscd-ui/menu/OscdMenu.js';
|
|
11
|
+
import { OscdMenuItem } from '@omicronenergy/oscd-ui/menu/OscdMenuItem.js';
|
|
12
|
+
import { OscdTree, } from '@omicronenergy/oscd-ui/tree/OscdTree.js';
|
|
13
|
+
import { filterBySearchTerm, filterByPinned, isPluginGroup, flattenPluginEntries, } from '../utils/plugin-utils.js';
|
|
14
|
+
import { OscdTreeItem } from '@omicronenergy/oscd-ui/tree/OscdTreeItem.js';
|
|
15
|
+
import { OscdDivider } from '@omicronenergy/oscd-ui/divider/OscdDivider.js';
|
|
16
|
+
import { localstorage } from '@omicronenergy/oscd-ui/decorators/localstorage.js';
|
|
17
|
+
import { OscdOutlinedSearchField } from '@omicronenergy/oscd-ui/search-field/OscdOutlinedSearchField.js';
|
|
18
|
+
const unpinnedIcon = html `<svg
|
|
19
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
+
width="28"
|
|
21
|
+
height="28"
|
|
22
|
+
viewBox="0 0 28 28"
|
|
23
|
+
fill="none"
|
|
24
|
+
>
|
|
25
|
+
<path
|
|
26
|
+
d="M16.391 14.4188L16.8773 16.2406L16.2096 17.3947L13.3244 15.7253L11.3211 19.1876L10.4102 19.4307L10.1671 18.5198L12.1703 15.0576L9.28512 13.3882L9.95287 12.2341L11.7747 11.7478L14.1118 7.70854L13.5348 7.37467L14.2025 6.22059L19.9729 9.55934L19.3052 10.7134L18.7281 10.3795L16.391 14.4188ZM11.5974 13.1857L15.2328 15.2891L14.9531 14.2415L17.574 9.71179L15.2659 8.37629L12.645 12.906L11.5974 13.1857Z"
|
|
27
|
+
fill="currentColor"
|
|
28
|
+
/>
|
|
29
|
+
</svg>`;
|
|
30
|
+
const pinnedIcon = html `<svg
|
|
31
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
32
|
+
width="28"
|
|
33
|
+
height="28"
|
|
34
|
+
viewBox="0 0 28 28"
|
|
35
|
+
fill="none"
|
|
36
|
+
>
|
|
37
|
+
<path
|
|
38
|
+
d="M16.4379 14.5274L16.9273 16.3484L16.2615 17.5036L13.3735 15.8391L11.3761 19.3047L10.4656 19.5494L10.2209 18.6389L12.2183 15.1733L9.33034 13.5088L9.99614 12.3536L11.8171 11.8642L14.1475 7.821L13.5699 7.4881L14.2357 6.3329L20.0117 9.66194L19.3459 10.8171L18.7683 10.4842L16.4379 14.5274Z"
|
|
39
|
+
fill="currentColor"
|
|
40
|
+
/>
|
|
41
|
+
</svg>`;
|
|
42
|
+
export function buildTreeNodes(plugins) {
|
|
43
|
+
function pluginEntryToTreeNode(pluginEntry) {
|
|
44
|
+
return {
|
|
45
|
+
...pluginEntry,
|
|
46
|
+
id: pluginEntry.tagName,
|
|
47
|
+
children: [], //explicitly set children to an empty array for leaf nodes
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return plugins.map((editor, index) => {
|
|
51
|
+
const isGroup = isPluginGroup(editor);
|
|
52
|
+
if (isGroup) {
|
|
53
|
+
const children = editor.plugins.map(pluginEntryToTreeNode);
|
|
54
|
+
return {
|
|
55
|
+
...editor,
|
|
56
|
+
id: `group:${index}:${editor.name}`,
|
|
57
|
+
children,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return pluginEntryToTreeNode(editor);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function renderFlyoutPlaceholder() {
|
|
64
|
+
return html `<oscd-menu-item disabled>
|
|
65
|
+
<div slot="headline">${msg('Items you pin will appear here')}</div>
|
|
66
|
+
</oscd-menu-item>`;
|
|
67
|
+
}
|
|
11
68
|
let EditorPluginsPanel = class EditorPluginsPanel extends ScopedElementsMixin(LitElement) {
|
|
12
69
|
constructor() {
|
|
13
70
|
super(...arguments);
|
|
71
|
+
this.id = 'editor-plugins-panel';
|
|
14
72
|
this.editors = [];
|
|
15
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Transient "search mode": the collapsed rail's search icon opens the full
|
|
75
|
+
* panel to let the user search, WITHOUT persisting the panel as expanded.
|
|
76
|
+
* It is reflected purely so `:host([search-mode])` can drive the open width.
|
|
77
|
+
* Exited on editor selection or Escape (see `exitSearchMode`).
|
|
78
|
+
*/
|
|
79
|
+
this.searchMode = false;
|
|
80
|
+
this.editorTreeNodes = [];
|
|
81
|
+
this.pinnedTreeNodes = [
|
|
82
|
+
{
|
|
83
|
+
id: 'pinned',
|
|
84
|
+
name: 'Pinned',
|
|
85
|
+
icon: 'keep',
|
|
86
|
+
plugins: [],
|
|
87
|
+
children: [],
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
this.searchValue = '';
|
|
91
|
+
this.focusedTree = null;
|
|
92
|
+
this.handleKeydown = (event) => {
|
|
93
|
+
const fromSearchField = event.currentTarget?.localName ===
|
|
94
|
+
'oscd-outlined-search-field' ||
|
|
95
|
+
event
|
|
96
|
+
.composedPath()
|
|
97
|
+
.some(target => target.localName === 'oscd-outlined-search-field');
|
|
98
|
+
if (event.key === 'Escape' && this.searchMode) {
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
this.exitSearchMode();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (fromSearchField &&
|
|
104
|
+
(event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
this.startTreeNavigation(event.key === 'ArrowDown' ? 'down' : 'up');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (fromSearchField && event.key === 'Enter') {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
event.stopPropagation();
|
|
113
|
+
this.activateKeyboardTarget();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
16
116
|
}
|
|
17
|
-
|
|
18
|
-
get
|
|
19
|
-
|
|
20
|
-
return expandedStr === 'false' ? false : true;
|
|
117
|
+
/** True when the panel is visually open (persisted expanded OR transient search). */
|
|
118
|
+
get isOpen() {
|
|
119
|
+
return this.expanded || this.searchMode;
|
|
21
120
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
121
|
+
willUpdate(changedProperties) {
|
|
122
|
+
if (changedProperties.has('editors') ||
|
|
123
|
+
changedProperties.has('searchValue') ||
|
|
124
|
+
changedProperties.has('locale')) {
|
|
125
|
+
const searchResults = filterBySearchTerm(this.editors, this.searchValue, this.locale);
|
|
126
|
+
this.editorTreeNodes = buildTreeNodes(searchResults);
|
|
127
|
+
}
|
|
128
|
+
if (changedProperties.has('editors') ||
|
|
129
|
+
changedProperties.has('pinnedPluginIds') ||
|
|
130
|
+
changedProperties.has('locale')) {
|
|
131
|
+
const pinnedPlugins = filterByPinned(this.editors, this.pinnedPluginIds);
|
|
132
|
+
const pinnedChildren = pinnedPlugins.length
|
|
133
|
+
? buildTreeNodes(pinnedPlugins)
|
|
134
|
+
: [
|
|
135
|
+
{
|
|
136
|
+
kind: 'placeholder',
|
|
137
|
+
name: msg('Items you pin will appear here'),
|
|
138
|
+
id: 'pinned-placeholder',
|
|
139
|
+
children: [],
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
this.pinnedTreeNodes = [
|
|
143
|
+
{
|
|
144
|
+
id: 'pinned',
|
|
145
|
+
name: msg('Pinned'),
|
|
146
|
+
icon: 'keep',
|
|
147
|
+
plugins: [],
|
|
148
|
+
children: pinnedChildren,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
}
|
|
25
152
|
}
|
|
26
|
-
|
|
153
|
+
togglePin(id) {
|
|
154
|
+
if (this.pinnedPluginIds.includes(id)) {
|
|
155
|
+
this.pinnedPluginIds = this.pinnedPluginIds.filter(pinnedId => pinnedId !== id);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
this.pinnedPluginIds = [...this.pinnedPluginIds, id];
|
|
159
|
+
this.pinnedExpanded = ['pinned']; // Expand the pinned section when a new plugin is pinned
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
selectEditor(selectedIds) {
|
|
163
|
+
const [selectedId] = selectedIds;
|
|
164
|
+
if (!selectedId) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const editor = flattenPluginEntries(this.editors).find(editor => editor.tagName === selectedId);
|
|
168
|
+
if (!editor) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.dispatchEditorSelect(editor);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Dispatches the `editor-select` event and leaves transient search mode, if
|
|
175
|
+
* active. Used by both the expanded trees and the collapsed rail flyouts.
|
|
176
|
+
*/
|
|
177
|
+
dispatchEditorSelect(editor) {
|
|
178
|
+
this.dispatchEvent(new CustomEvent('editor-select', {
|
|
179
|
+
detail: { editor },
|
|
180
|
+
bubbles: true,
|
|
181
|
+
composed: true,
|
|
182
|
+
}));
|
|
183
|
+
if (this.searchMode) {
|
|
184
|
+
this.exitSearchMode();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
connectedCallback() {
|
|
188
|
+
super.connectedCallback();
|
|
189
|
+
this.addEventListener('keydown', this.handleKeydown);
|
|
190
|
+
}
|
|
191
|
+
disconnectedCallback() {
|
|
192
|
+
this.removeEventListener('keydown', this.handleKeydown);
|
|
193
|
+
super.disconnectedCallback();
|
|
194
|
+
}
|
|
195
|
+
/** Opens the panel transiently for searching (does not persist `expanded`). */
|
|
196
|
+
enterSearchMode() {
|
|
197
|
+
this.searchMode = true;
|
|
198
|
+
this.focusSearch(true);
|
|
199
|
+
}
|
|
200
|
+
/** Focuses the search field, optionally selecting its current query. */
|
|
201
|
+
focusSearch(selectQuery = false) {
|
|
202
|
+
const focusField = () => {
|
|
203
|
+
const searchField = this.shadowRoot?.querySelector('oscd-outlined-search-field');
|
|
204
|
+
searchField?.focus();
|
|
205
|
+
if (selectQuery) {
|
|
206
|
+
searchField?.select();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
this.updateComplete.then(() => {
|
|
210
|
+
focusField();
|
|
211
|
+
requestAnimationFrame(() => requestAnimationFrame(focusField));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/** Returns the panel to the collapsed rail and clears the search query. */
|
|
215
|
+
exitSearchMode() {
|
|
216
|
+
this.searchMode = false;
|
|
217
|
+
this.searchValue = '';
|
|
218
|
+
this.focusedTree = null;
|
|
219
|
+
}
|
|
220
|
+
getTree(kind) {
|
|
221
|
+
return (this.shadowRoot?.querySelector(`oscd-tree.${kind}-tree`) ?? null);
|
|
222
|
+
}
|
|
223
|
+
focusTree(kind, activeId) {
|
|
224
|
+
const tree = this.getTree(kind);
|
|
225
|
+
if (!tree || !activeId) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
tree.activeId = activeId;
|
|
229
|
+
this.focusedTree = kind;
|
|
230
|
+
tree.focus();
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
startTreeNavigation(direction) {
|
|
234
|
+
const first = direction === 'down';
|
|
235
|
+
const pinnedTree = this.getTree('pinned');
|
|
236
|
+
if (this.searchValue.trim().length === 0 && pinnedTree) {
|
|
237
|
+
const activeId = this.focusTree('pinned', first ? pinnedTree.getFirstNodeId() : pinnedTree.getLastNodeId());
|
|
238
|
+
if (activeId) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const editorsTree = this.getTree('editors');
|
|
243
|
+
this.focusTree('editors', editorsTree
|
|
244
|
+
? first
|
|
245
|
+
? editorsTree.getFirstNodeId()
|
|
246
|
+
: editorsTree.getLastNodeId()
|
|
247
|
+
: null);
|
|
248
|
+
}
|
|
249
|
+
handleTreeActiveChanged(kind) {
|
|
250
|
+
this.focusedTree = kind;
|
|
251
|
+
}
|
|
252
|
+
handleTreeFocus(kind) {
|
|
253
|
+
this.focusedTree = kind;
|
|
254
|
+
}
|
|
255
|
+
handleTreeSelection(kind, selectedIds) {
|
|
256
|
+
const tree = this.getTree(kind);
|
|
257
|
+
const selectedId = selectedIds[0];
|
|
258
|
+
const selectedNode = tree?.data.find(node => node.id === selectedId);
|
|
259
|
+
if (tree && selectedNode?.children?.length && selectedId) {
|
|
260
|
+
tree.toggle(selectedId);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
this.selectEditor(selectedIds);
|
|
264
|
+
}
|
|
265
|
+
handleTreeBoundary(kind, direction) {
|
|
266
|
+
if (kind === 'pinned' && direction === 'last') {
|
|
267
|
+
this.focusTree('editors', this.getTree('editors')?.getFirstNodeId() ?? null);
|
|
268
|
+
}
|
|
269
|
+
else if (kind === 'editors' &&
|
|
270
|
+
direction === 'first' &&
|
|
271
|
+
this.searchValue.trim().length === 0) {
|
|
272
|
+
this.focusTree('pinned', this.getTree('pinned')?.getLastNodeId() ?? null);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
activateKeyboardTarget() {
|
|
276
|
+
if (!this.focusedTree) {
|
|
277
|
+
if (this.shadowRoot?.activeElement?.localName ===
|
|
278
|
+
'oscd-outlined-search-field' &&
|
|
279
|
+
flattenPluginEntries(filterBySearchTerm(this.editors, this.searchValue, this.locale)).length === 1) {
|
|
280
|
+
this.dispatchEditorSelect(flattenPluginEntries(filterBySearchTerm(this.editors, this.searchValue, this.locale))[0]);
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Toggles the persisted expanded/collapsed state via the footer control. */
|
|
286
|
+
toggleExpanded() {
|
|
287
|
+
if (this.isOpen) {
|
|
288
|
+
this.expanded = false;
|
|
289
|
+
this.exitSearchMode();
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.expanded = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
toggleFlyout(anchorId) {
|
|
296
|
+
const menu = this.shadowRoot?.querySelector(`oscd-menu[data-flyout="${anchorId}"]`);
|
|
297
|
+
if (!menu) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (menu.open) {
|
|
301
|
+
menu.close();
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
menu.show();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
renderPluginItem({ node, level, disabled, active, }) {
|
|
308
|
+
const label = node.translations?.[this.locale] ?? node.name;
|
|
309
|
+
return html `<oscd-tree-item ?disabled=${disabled} ?active=${active}>
|
|
310
|
+
${level === 1 && !('kind' in node)
|
|
311
|
+
? html `<oscd-icon slot="start">${node.icon}</oscd-icon>`
|
|
312
|
+
: nothing}
|
|
313
|
+
<span slot="headline" title=${label}>${label}</span>
|
|
314
|
+
</oscd-tree-item>`;
|
|
315
|
+
}
|
|
316
|
+
renderLeafAccessory({ node, id }) {
|
|
317
|
+
const pinned = this.pinnedPluginIds.includes(id);
|
|
318
|
+
return html `<button
|
|
319
|
+
aria-label=${pinned
|
|
320
|
+
? msg(str `Unpin ${node.name}`)
|
|
321
|
+
: msg(str `Pin ${node.name}`)}
|
|
322
|
+
aria-pressed=${pinned}
|
|
323
|
+
@click=${() => this.togglePin(id)}
|
|
324
|
+
>
|
|
325
|
+
<oscd-icon>${pinned ? pinnedIcon : unpinnedIcon}</oscd-icon>
|
|
326
|
+
</button>`;
|
|
327
|
+
}
|
|
328
|
+
renderExpanded() {
|
|
27
329
|
return html `
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
bubbles: true,
|
|
36
|
-
composed: true,
|
|
37
|
-
}));
|
|
330
|
+
<div class="tree-container">
|
|
331
|
+
<oscd-outlined-search-field
|
|
332
|
+
placeholder=${msg('Search')}
|
|
333
|
+
clearLabel=${msg('Clear search')}
|
|
334
|
+
.value=${this.searchValue}
|
|
335
|
+
@input=${(event) => {
|
|
336
|
+
this.searchValue = event.target.value;
|
|
38
337
|
}}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
? html `<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
338
|
+
></oscd-outlined-search-field>
|
|
339
|
+
<div class="tree-scroll">
|
|
340
|
+
${this.searchValue.trim().length === 0
|
|
341
|
+
? html `<oscd-tree
|
|
342
|
+
.data=${this.pinnedTreeNodes}
|
|
343
|
+
.expandedIds=${this.pinnedExpanded}
|
|
344
|
+
.selectionMode=${'single'}
|
|
345
|
+
.selectedIds=${this.selectedEditor
|
|
346
|
+
? [this.selectedEditor.tagName]
|
|
347
|
+
: []}
|
|
348
|
+
.isDisabled=${(node) => 'kind' in node && node.kind === 'placeholder'}
|
|
349
|
+
.isSelectable=${(node) => !('kind' in node && node.kind === 'placeholder')}
|
|
350
|
+
class="pinned-tree"
|
|
351
|
+
?keyboard-active=${this.focusedTree === 'pinned'}
|
|
352
|
+
@focusin=${() => this.handleTreeFocus('pinned')}
|
|
353
|
+
.renderItem=${(context) => this.renderPluginItem(context)}
|
|
354
|
+
.renderLeafAccessory=${(context) => this.renderLeafAccessory(context)}
|
|
355
|
+
toggle-position="trailing"
|
|
356
|
+
collapse-icon="arrow_drop_up"
|
|
357
|
+
expand-icon="arrow_drop_down"
|
|
358
|
+
@selected-ids-changed=${(event) => this.handleTreeSelection('pinned', event.detail.selectedIds)}
|
|
359
|
+
@expanded-ids-changed=${(event) => {
|
|
360
|
+
this.pinnedExpanded = event.detail.expandedIds;
|
|
361
|
+
}}
|
|
362
|
+
@active-changed=${() => this.handleTreeActiveChanged('pinned')}
|
|
363
|
+
@navigation-boundary=${(event) => this.handleTreeBoundary('pinned', event.detail.direction)}
|
|
364
|
+
></oscd-tree>
|
|
365
|
+
<oscd-divider></oscd-divider>`
|
|
46
366
|
: nothing}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
367
|
+
<oscd-tree
|
|
368
|
+
class="editors-tree"
|
|
369
|
+
?keyboard-active=${this.focusedTree === 'editors'}
|
|
370
|
+
@focusin=${() => this.handleTreeFocus('editors')}
|
|
371
|
+
.data=${this.editorTreeNodes}
|
|
372
|
+
.expandedIds=${this.searchValue.length === 0
|
|
373
|
+
? this.expandedIds
|
|
374
|
+
: this.editorTreeNodes.map(node => node.id)}
|
|
375
|
+
.selectionMode=${'single'}
|
|
376
|
+
.selectedIds=${this.selectedEditor
|
|
377
|
+
? [this.selectedEditor.tagName]
|
|
378
|
+
: []}
|
|
379
|
+
.isDisabled=${(node) => 'kind' in node && node.kind === 'placeholder'}
|
|
380
|
+
.isSelectable=${(node) => !('kind' in node && node.kind === 'placeholder')}
|
|
381
|
+
.renderItem=${(context) => this.renderPluginItem(context)}
|
|
382
|
+
.renderLeafAccessory=${(context) => this.renderLeafAccessory(context)}
|
|
383
|
+
toggle-position="trailing"
|
|
384
|
+
collapse-icon="arrow_drop_down"
|
|
385
|
+
expand-icon="arrow_drop_up"
|
|
386
|
+
@selected-ids-changed=${(event) => this.handleTreeSelection('editors', event.detail.selectedIds)}
|
|
387
|
+
@expanded-ids-changed=${(event) => {
|
|
388
|
+
this.expandedIds = event.detail.expandedIds;
|
|
54
389
|
}}
|
|
390
|
+
@active-changed=${() => this.handleTreeActiveChanged('editors')}
|
|
391
|
+
@navigation-boundary=${(event) => this.handleTreeBoundary('editors', event.detail.direction)}
|
|
392
|
+
></oscd-tree>
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
`;
|
|
396
|
+
}
|
|
397
|
+
renderRail() {
|
|
398
|
+
const pinnedPlugins = flattenPluginEntries(filterByPinned(this.editors, this.pinnedPluginIds));
|
|
399
|
+
return html `
|
|
400
|
+
<div class="rail">
|
|
401
|
+
<oscd-icon-button
|
|
402
|
+
class="rail-item"
|
|
403
|
+
aria-label=${msg('Search')}
|
|
404
|
+
@click=${() => this.enterSearchMode()}
|
|
55
405
|
>
|
|
56
|
-
<oscd-icon
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
406
|
+
<oscd-icon>search</oscd-icon>
|
|
407
|
+
</oscd-icon-button>
|
|
408
|
+
${this.renderRailGroup({ name: msg('Pinned'), icon: 'keep', plugins: pinnedPlugins }, 'pinned', true)}
|
|
409
|
+
<oscd-divider></oscd-divider>
|
|
410
|
+
${this.editors.map((entry, index) => isPluginGroup(entry)
|
|
411
|
+
? this.renderRailGroup(entry, `group-${index}`)
|
|
412
|
+
: this.renderRailLeaf(entry))}
|
|
62
413
|
</div>
|
|
63
414
|
`;
|
|
64
415
|
}
|
|
416
|
+
renderRailGroup(group, anchorId, showEmptyPlaceholder = false) {
|
|
417
|
+
const label = group.translations?.[this.locale] ?? group.name;
|
|
418
|
+
const active = group.plugins.some(plugin => plugin.tagName === this.selectedEditor?.tagName);
|
|
419
|
+
const anchor = `rail-${anchorId}`;
|
|
420
|
+
return html `
|
|
421
|
+
<oscd-icon-button
|
|
422
|
+
id=${anchor}
|
|
423
|
+
class=${classMap({ 'rail-item': true, active })}
|
|
424
|
+
aria-haspopup="menu"
|
|
425
|
+
aria-label=${label}
|
|
426
|
+
@click=${() => this.toggleFlyout(anchorId)}
|
|
427
|
+
>
|
|
428
|
+
<oscd-icon>${group.icon}</oscd-icon>
|
|
429
|
+
</oscd-icon-button>
|
|
430
|
+
<oscd-menu
|
|
431
|
+
class="rail-flyout"
|
|
432
|
+
data-flyout=${anchorId}
|
|
433
|
+
anchor=${anchor}
|
|
434
|
+
anchor-corner="start-end"
|
|
435
|
+
menu-corner="start-start"
|
|
436
|
+
positioning="popover"
|
|
437
|
+
quick
|
|
438
|
+
>
|
|
439
|
+
<div class="flyout-header" role="presentation">${label}</div>
|
|
440
|
+
<oscd-divider class="flyout-divider"></oscd-divider>
|
|
441
|
+
${group.plugins.length > 0
|
|
442
|
+
? group.plugins.map(plugin => this.renderFlyoutItem(plugin))
|
|
443
|
+
: showEmptyPlaceholder
|
|
444
|
+
? renderFlyoutPlaceholder()
|
|
445
|
+
: nothing}
|
|
446
|
+
</oscd-menu>
|
|
447
|
+
`;
|
|
448
|
+
}
|
|
449
|
+
renderRailLeaf(plugin) {
|
|
450
|
+
const label = plugin.translations?.[this.locale] ?? plugin.name;
|
|
451
|
+
const active = plugin.tagName === this.selectedEditor?.tagName;
|
|
452
|
+
return html `
|
|
453
|
+
<oscd-icon-button
|
|
454
|
+
class=${classMap({ 'rail-item': true, active })}
|
|
455
|
+
aria-label=${label}
|
|
456
|
+
@click=${() => this.dispatchEditorSelect(plugin)}
|
|
457
|
+
>
|
|
458
|
+
<oscd-icon>${plugin.icon}</oscd-icon>
|
|
459
|
+
</oscd-icon-button>
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
renderFlyoutItem(plugin) {
|
|
463
|
+
const label = plugin.translations?.[this.locale] ?? plugin.name;
|
|
464
|
+
const selected = plugin.tagName === this.selectedEditor?.tagName;
|
|
465
|
+
return html `
|
|
466
|
+
<oscd-menu-item
|
|
467
|
+
.selected=${selected}
|
|
468
|
+
@click=${() => this.dispatchEditorSelect(plugin)}
|
|
469
|
+
>
|
|
470
|
+
<div slot="headline">${label}</div>
|
|
471
|
+
</oscd-menu-item>
|
|
472
|
+
`;
|
|
473
|
+
}
|
|
474
|
+
renderFooter() {
|
|
475
|
+
const open = this.isOpen;
|
|
476
|
+
const label = open ? msg('Collapse sidebar') : msg('Expand sidebar');
|
|
477
|
+
const icon = open ? 'left_panel_close' : 'left_panel_open';
|
|
478
|
+
return html `
|
|
479
|
+
<div class="footer">
|
|
480
|
+
${open
|
|
481
|
+
? html `<oscd-list-item
|
|
482
|
+
class="toggle-button"
|
|
483
|
+
type="button"
|
|
484
|
+
aria-label=${label}
|
|
485
|
+
@click=${() => this.toggleExpanded()}
|
|
486
|
+
>
|
|
487
|
+
<oscd-icon slot="start">${icon}</oscd-icon>
|
|
488
|
+
<span slot="headline">${label}</span>
|
|
489
|
+
</oscd-list-item>`
|
|
490
|
+
: html `<oscd-icon-button
|
|
491
|
+
class="toggle-button"
|
|
492
|
+
aria-label=${label}
|
|
493
|
+
@click=${() => this.toggleExpanded()}
|
|
494
|
+
>
|
|
495
|
+
<oscd-icon>${icon}</oscd-icon>
|
|
496
|
+
</oscd-icon-button>`}
|
|
497
|
+
</div>
|
|
498
|
+
`;
|
|
499
|
+
}
|
|
500
|
+
render() {
|
|
501
|
+
return html `
|
|
502
|
+
${this.isOpen ? this.renderExpanded() : this.renderRail()}
|
|
503
|
+
<oscd-divider class="footer-divider"></oscd-divider>
|
|
504
|
+
${this.renderFooter()}
|
|
505
|
+
`;
|
|
506
|
+
}
|
|
65
507
|
};
|
|
66
508
|
EditorPluginsPanel.scopedElements = {
|
|
67
509
|
'oscd-icon-button': OscdIconButton,
|
|
68
510
|
'oscd-icon': OscdIcon,
|
|
69
|
-
'oscd-list': OscdList,
|
|
70
511
|
'oscd-list-item': OscdListItem,
|
|
512
|
+
'oscd-tree': OscdTree,
|
|
513
|
+
'oscd-tree-item': OscdTreeItem,
|
|
514
|
+
'oscd-outlined-search-field': OscdOutlinedSearchField,
|
|
515
|
+
'oscd-divider': OscdDivider,
|
|
516
|
+
'oscd-menu': OscdMenu,
|
|
517
|
+
'oscd-menu-item': OscdMenuItem,
|
|
71
518
|
};
|
|
72
519
|
EditorPluginsPanel.styles = css `
|
|
73
520
|
:host {
|
|
74
|
-
width
|
|
521
|
+
/* Collapsed rail is the default width; the panel widens to its full width
|
|
522
|
+
when persistently expanded OR while in transient search mode. The shell
|
|
523
|
+
grid column follows this intrinsic width (grid-template-columns: auto). */
|
|
524
|
+
width: var(--editor-plugins-panel-collapsed-width);
|
|
75
525
|
height: calc(100% - var(--editor-plugins-panel-padding-top));
|
|
76
526
|
display: grid;
|
|
77
|
-
|
|
527
|
+
/* Row 1 (tree-container/rail) is the only scrollable region; the divider
|
|
528
|
+
and footer rows are sized to content so they stay pinned and always
|
|
529
|
+
visible below it, however tall the tree content grows. */
|
|
530
|
+
grid-template-rows: 1fr auto auto;
|
|
78
531
|
min-height: 0;
|
|
79
532
|
padding-top: var(--editor-plugins-panel-padding-top);
|
|
80
533
|
transition: width 0.1s ease-in-out;
|
|
81
|
-
overflow
|
|
534
|
+
/* Clip transient horizontal overflow while the width animates on
|
|
535
|
+
expand/collapse: the content swaps to its full width before the host
|
|
536
|
+
finishes resizing, and this stops that from forcing horizontal reflow /
|
|
537
|
+
scrollbar churn every animation frame. Steady-state label overrun is
|
|
538
|
+
handled by the tree row ellipsis, not by this. */
|
|
82
539
|
overflow-x: hidden;
|
|
540
|
+
/* The panel paints its own surface color; it must never depend on
|
|
541
|
+
page/body background showing through, or hosts that don't set a
|
|
542
|
+
background image get a blank panel. */
|
|
543
|
+
background-color: var(--editor-plugins-panel-background-color);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/* Material colour scheme for the panel's content. By default the panel's
|
|
547
|
+
background-color (above) is the dark --oscd-primary surface, so its
|
|
548
|
+
content is light ("white"/--oscd-base3) on a dark surface. We set the
|
|
549
|
+
*system* colours once here — not each component's final colour — so
|
|
550
|
+
resting text/icons AND every derived hover/pressed state layer resolve
|
|
551
|
+
to the light content colour from one place. (The flyout menus are a
|
|
552
|
+
light surface and reset these back to the shell defaults; see
|
|
553
|
+
.rail-flyout.)
|
|
554
|
+
|
|
555
|
+
NB: these are set on the content containers rather than :host on
|
|
556
|
+
purpose. A :host declaration would sit on the same element that
|
|
557
|
+
inherits --editor-plugins-panel-item-text-color, which is itself
|
|
558
|
+
derived from --md-sys-color-on-surface; declaring on the containers
|
|
559
|
+
keeps the source token a plain inherited value and so cannot form a
|
|
560
|
+
reference cycle. See "Layers are read upward" in THEMING.md. */
|
|
561
|
+
.rail,
|
|
562
|
+
.tree-container,
|
|
563
|
+
.footer {
|
|
564
|
+
--md-sys-color-on-surface: var(--editor-plugins-panel-item-text-color);
|
|
565
|
+
--md-sys-color-on-surface-variant: var(
|
|
566
|
+
--editor-plugins-panel-item-icon-color
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
--md-sys-color-surface: var(--editor-plugins-panel-background-color);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
:host([expanded]),
|
|
573
|
+
:host([search-mode]) {
|
|
574
|
+
width: var(--editor-plugins-panel-width);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/* Base divider styling, overridden by the more specific rail/flyout/footer
|
|
578
|
+
dividers below where the Figma spec calls for different sizing. */
|
|
579
|
+
oscd-divider {
|
|
580
|
+
--md-divider-color: var(--editor-plugins-panel-divider-color);
|
|
581
|
+
/* No margin: .tree-container's 12px gap owns the spacing on both sides. */
|
|
582
|
+
margin-block: 0;
|
|
83
583
|
}
|
|
84
584
|
|
|
85
|
-
|
|
585
|
+
/* --- Expanded panel: search field + scrollable tree --- */
|
|
586
|
+
|
|
587
|
+
.tree-container {
|
|
588
|
+
margin-inline: 16px;
|
|
589
|
+
min-width: 0;
|
|
86
590
|
min-height: 0;
|
|
87
|
-
|
|
88
|
-
|
|
591
|
+
display: flex;
|
|
592
|
+
flex-direction: column;
|
|
593
|
+
gap: 12px;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
oscd-outlined-search-field * {
|
|
597
|
+
--md-icon-button-state-layer-width: 32px;
|
|
598
|
+
--md-icon-button-state-layer-height: 32px;
|
|
599
|
+
--md-icon-button-state-layer-shape: 50%;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
oscd-outlined-search-field {
|
|
603
|
+
background-color: color-mix(
|
|
604
|
+
in srgb,
|
|
605
|
+
var(
|
|
606
|
+
--md-outlined-text-field-input-text-color,
|
|
607
|
+
var(--md-sys-color-on-surface, #1d1b20)
|
|
608
|
+
)
|
|
609
|
+
20%,
|
|
610
|
+
transparent
|
|
611
|
+
);
|
|
612
|
+
border-radius: 5px;
|
|
613
|
+
--md-outlined-text-field-top-space: 6px;
|
|
614
|
+
--md-outlined-text-field-bottom-space: 6px;
|
|
615
|
+
--md-outlined-text-field-leading-space: 8px;
|
|
616
|
+
--md-outlined-text-field-trailing-space: 8px;
|
|
617
|
+
--md-outlined-text-field-with-leading-icon-leading-space: 10px;
|
|
618
|
+
--md-outlined-field-with-trailing-content-trailing-space: 8px;
|
|
619
|
+
--md-outlined-text-field-icon-input-space: 10px;
|
|
620
|
+
--md-icon-button-state-layer-width: 32px;
|
|
621
|
+
--md-icon-button-state-layer-height: 32px;
|
|
622
|
+
--md-icon-button-state-layer-shape: 50%;
|
|
623
|
+
|
|
624
|
+
--md-outlined-text-field-trailing-icon-size: 32px;
|
|
625
|
+
|
|
626
|
+
--md-icon-button-icon-size: 24px;
|
|
627
|
+
|
|
628
|
+
/* keep the outline light in every state */
|
|
629
|
+
--md-outlined-text-field-focus-outline-color: var(
|
|
630
|
+
--editor-plugins-panel-item-text-color
|
|
89
631
|
);
|
|
90
|
-
--md-
|
|
91
|
-
--editor-plugins-panel-item-
|
|
632
|
+
--md-outlined-text-field-hover-outline-color: var(
|
|
633
|
+
--editor-plugins-panel-item-text-color
|
|
92
634
|
);
|
|
93
|
-
--md-
|
|
94
|
-
--md-list-container-color: rgba(0, 0, 0, 0);
|
|
95
|
-
--md-list-item-label-text-color: var(
|
|
635
|
+
--md-outlined-text-field-outline-color: var(
|
|
96
636
|
--editor-plugins-panel-item-text-color
|
|
97
637
|
);
|
|
98
|
-
|
|
99
|
-
|
|
638
|
+
/* hide the outline border entirely, in every state */
|
|
639
|
+
--md-outlined-text-field-outline-width: 0px;
|
|
640
|
+
--md-outlined-text-field-hover-outline-width: 0px;
|
|
641
|
+
--md-outlined-text-field-focus-outline-width: 0px;
|
|
642
|
+
|
|
643
|
+
/* caret follows the light text instead of the primary accent */
|
|
644
|
+
--md-outlined-text-field-caret-color: var(
|
|
645
|
+
--editor-plugins-panel-item-text-color
|
|
646
|
+
);
|
|
647
|
+
--md-outlined-text-field-focus-caret-color: var(
|
|
648
|
+
--editor-plugins-panel-item-text-color
|
|
100
649
|
);
|
|
101
650
|
}
|
|
102
651
|
|
|
103
|
-
.
|
|
104
|
-
|
|
652
|
+
.tree-scroll {
|
|
653
|
+
display: flex;
|
|
654
|
+
flex-direction: column;
|
|
655
|
+
gap: 12px;
|
|
656
|
+
min-height: 0;
|
|
657
|
+
overflow-y: auto;
|
|
658
|
+
|
|
659
|
+
/* Recolour the scrollbar to sit on the panel's dark-blue surface instead
|
|
660
|
+
of the browser's default light-themed bar. Firefox reads
|
|
661
|
+
scrollbar-color; Chromium/Safari (and Firefox with the layout flag
|
|
662
|
+
off) read the ::-webkit-scrollbar-* pseudo-elements below. */
|
|
663
|
+
scrollbar-color: var(--editor-plugins-panel-divider-color) transparent;
|
|
664
|
+
scrollbar-width: thin;
|
|
105
665
|
}
|
|
106
666
|
|
|
107
|
-
.
|
|
108
|
-
|
|
109
|
-
white-space: nowrap;
|
|
667
|
+
.tree-scroll::-webkit-scrollbar {
|
|
668
|
+
width: 8px;
|
|
110
669
|
}
|
|
111
670
|
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
display: none;
|
|
115
|
-
/* justify-self: center;
|
|
116
|
-
justify-content: center;
|
|
117
|
-
padding-block: 22px; */
|
|
671
|
+
.tree-scroll::-webkit-scrollbar-track {
|
|
672
|
+
background: transparent;
|
|
118
673
|
}
|
|
119
674
|
|
|
120
|
-
.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
675
|
+
.tree-scroll::-webkit-scrollbar-thumb {
|
|
676
|
+
background-color: var(--editor-plugins-panel-divider-color);
|
|
677
|
+
border-radius: 4px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
oscd-tree {
|
|
681
|
+
margin-inline: 0;
|
|
682
|
+
min-height: 0;
|
|
683
|
+
/* .tree-scroll is the sole flex item allowed to shrink/scroll; without
|
|
684
|
+
this, a too-short container shrinks each tree's box below its
|
|
685
|
+
rendered content instead of letting the scroll container overflow,
|
|
686
|
+
and the (unclipped) shadow-DOM rows bleed past the shrunk box into
|
|
687
|
+
whichever tree follows. */
|
|
688
|
+
flex-shrink: 0;
|
|
689
|
+
--oscd-tree-row-height: 36px;
|
|
690
|
+
--oscd-tree-item-min-height: 36px;
|
|
691
|
+
--oscd-tree-row-gap: 4px;
|
|
692
|
+
--oscd-tree-row-padding-start: 8px;
|
|
693
|
+
--oscd-tree-row-padding-end: 8px;
|
|
694
|
+
--oscd-tree-indent-step: 36px;
|
|
695
|
+
--oscd-tree-row-shape: 5px;
|
|
696
|
+
--oscd-tree-toggle-icon-size: 28px;
|
|
697
|
+
--oscd-tree-trailing-toggle-gap: 8px;
|
|
698
|
+
/* Pin accessory hidden at rest, revealed on row hover / keyboard focus
|
|
699
|
+
(see Figma side-panel spec); its icon size inherits the 28px toggle. */
|
|
700
|
+
--oscd-tree-accessory-rest-opacity: 0;
|
|
701
|
+
--md-icon-size: var(--editor-plugins-panel-item-icon-size);
|
|
702
|
+
/* oscd-tree-item colours its leading/trailing icon slots explicitly
|
|
703
|
+
(not via inherited 'color'), so — unlike the label — they don't
|
|
704
|
+
automatically pick up the selected-row text colour above. Tying them
|
|
705
|
+
to currentColor makes the icon track whatever colour the row itself
|
|
706
|
+
resolves to (resting on-surface ink, or the active/selected colour),
|
|
707
|
+
matching the label in both states. */
|
|
708
|
+
--oscd-tree-item-leading-icon-color: currentColor;
|
|
709
|
+
--oscd-tree-item-trailing-icon-color: currentColor;
|
|
710
|
+
--oscd-tree-row-selected-color: var(
|
|
711
|
+
--editor-plugins-panel-item-active-bg
|
|
125
712
|
);
|
|
126
|
-
--
|
|
127
|
-
|
|
128
|
-
--md-icon-button-hover-icon-color: var(
|
|
129
|
-
--editor-plugins-panel-item-icon-color
|
|
713
|
+
--oscd-tree-row-selected-text-color: var(
|
|
714
|
+
--editor-plugins-panel-item-active-color
|
|
130
715
|
);
|
|
131
|
-
--
|
|
132
|
-
|
|
716
|
+
--oscd-tree-row-active-border-width: 0px;
|
|
717
|
+
/* The keyboard-active row highlight (outline + optional text override)
|
|
718
|
+
must stay visible whether that row is merely keyboard-highlighted at
|
|
719
|
+
rest (panel surface bg, on-surface ink text) or also selected
|
|
720
|
+
(--editor-plugins-panel-item-active-bg, active-color text). Using
|
|
721
|
+
currentColor/inherit ties the outline and text to whichever of those
|
|
722
|
+
two colours the row already resolves to, instead of hardcoding the
|
|
723
|
+
selected-row colour (which vanishes against the resting surface). */
|
|
724
|
+
--oscd-tree-row-active-border-color: currentColor;
|
|
725
|
+
--oscd-tree-row-active-text-color: inherit;
|
|
726
|
+
--oscd-tree-row-focus-ring-color: currentColor;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
oscd-tree.keyboard-active {
|
|
730
|
+
--oscd-tree-row-active-border-width: 1px;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
oscd-tree.pinned-tree {
|
|
734
|
+
--oscd-tree-leaf-toggle-size: 0px;
|
|
735
|
+
--oscd-tree-leaf-toggle-gap: 0px;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/* --- Collapsed icon rail --- */
|
|
739
|
+
|
|
740
|
+
/* Collapsed icon rail: a flat column of 44px icon buttons (28px glyph + 8px
|
|
741
|
+
padding), inset 16px so the glyphs land at the same x (24px) as both the
|
|
742
|
+
search icon and the tree-item icons in the expanded panel. */
|
|
743
|
+
.rail {
|
|
744
|
+
display: flex;
|
|
745
|
+
flex-direction: column;
|
|
746
|
+
align-items: flex-start;
|
|
747
|
+
padding-inline: 16px;
|
|
748
|
+
min-width: 0;
|
|
749
|
+
min-height: 0;
|
|
750
|
+
overflow-y: auto;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.rail-item {
|
|
754
|
+
--md-icon-button-icon-size: 28px;
|
|
755
|
+
--md-icon-button-state-layer-height: 44px;
|
|
756
|
+
--md-icon-button-state-layer-width: 44px;
|
|
757
|
+
border-radius: 5px;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/* Active group/editor: dark rounded square behind the glyph. */
|
|
761
|
+
.rail-item.active {
|
|
762
|
+
background: var(--editor-plugins-panel-item-active-bg);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.rail oscd-divider {
|
|
766
|
+
/* 44px wide (aligned with the icon column), 12px clearance above and
|
|
767
|
+
below, per the Figma collapsed spec. */
|
|
768
|
+
width: 44px;
|
|
769
|
+
margin-block: 12px;
|
|
770
|
+
--md-divider-color: var(--editor-plugins-panel-divider-color);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* Flyout menu opened from a collapsed group icon. Figma "Links container":
|
|
774
|
+
padding 8px, gap 4px, 1px border, 5px radius, light surface + shadow.
|
|
775
|
+
Unlike the rest of the panel this is a LIGHT surface, so it resets the
|
|
776
|
+
system colours back to the shell defaults (dark content on a light
|
|
777
|
+
surface); everything inside then derives correctly. */
|
|
778
|
+
.rail-flyout {
|
|
779
|
+
--md-sys-color-surface: var(--plugins-menu-container-color);
|
|
780
|
+
--md-sys-color-surface-container: var(--plugins-menu-container-color);
|
|
781
|
+
--md-sys-color-on-surface: var(--plugins-menu-item-label-color);
|
|
782
|
+
--md-sys-color-on-surface-variant: var(
|
|
783
|
+
--plugins-menu-item-leading-icon-color
|
|
133
784
|
);
|
|
134
|
-
--md-
|
|
135
|
-
|
|
785
|
+
--md-menu-container-color: var(--plugins-menu-container-color);
|
|
786
|
+
min-width: 200px;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
.flyout-header {
|
|
790
|
+
/* Group name heading: Material label-large, in the secondary blue, per the
|
|
791
|
+
Figma "Links container" header. */
|
|
792
|
+
padding: 8px 12px;
|
|
793
|
+
font-family: var(--oscd-text-font, Roboto), sans-serif;
|
|
794
|
+
font-size: 14px;
|
|
795
|
+
font-weight: 500;
|
|
796
|
+
line-height: 20px;
|
|
797
|
+
letter-spacing: 0.1px;
|
|
798
|
+
color: var(--editor-plugins-panel-flyout-header-text-color);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.rail-flyout .flyout-divider {
|
|
802
|
+
/* Separates the group heading from its editor items. */
|
|
803
|
+
--md-divider-color: var(--editor-plugins-panel-divider-color);
|
|
804
|
+
margin-block: 4px;
|
|
805
|
+
width: 80%;
|
|
806
|
+
margin: auto;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
.rail-flyout oscd-menu-item {
|
|
810
|
+
width: 100%;
|
|
811
|
+
--md-menu-item-selected-container-color: var(
|
|
812
|
+
--plugins-menu-item-selected-container-color
|
|
813
|
+
);
|
|
814
|
+
--md-menu-item-selected-label-text-color: var(
|
|
815
|
+
--plugins-menu-item-selected-label-color
|
|
136
816
|
);
|
|
137
|
-
--md-icon-button-state-layer-height: 48px;
|
|
138
|
-
--md-icon-button-state-layer-width: 48px;
|
|
139
817
|
}
|
|
140
818
|
|
|
141
|
-
:
|
|
142
|
-
|
|
819
|
+
/* --- Footer: collapse/expand toggle --- */
|
|
820
|
+
|
|
821
|
+
.footer-divider {
|
|
822
|
+
padding-inline: 16px;
|
|
823
|
+
margin-block: 12px 0;
|
|
143
824
|
}
|
|
144
825
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
826
|
+
.footer {
|
|
827
|
+
/* Persistent collapse/expand control, bottom-left in both modes. The 16px
|
|
828
|
+
inline inset matches the tree-container (so the expanded list-item spans
|
|
829
|
+
the same width as the rows) and the rail items; 16px below is the Figma
|
|
830
|
+
bottom margin. */
|
|
831
|
+
display: flex;
|
|
832
|
+
align-items: center;
|
|
833
|
+
justify-content: flex-start;
|
|
834
|
+
padding-inline: 16px;
|
|
835
|
+
padding-block: 0 16px;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/* Expanded: an interactive list item so it reads as "one more row" — full
|
|
839
|
+
width with a full-width hover/ripple state layer, left-aligned, no
|
|
840
|
+
container fill at rest. 8px leading space lands the 28px glyph at x24,
|
|
841
|
+
exactly like the search field and tree rows above it. */
|
|
842
|
+
oscd-list-item.toggle-button {
|
|
843
|
+
width: 100%;
|
|
844
|
+
border-radius: 5px;
|
|
845
|
+
--md-list-item-leading-space: 8px;
|
|
846
|
+
--md-list-item-trailing-space: 8px;
|
|
847
|
+
--md-list-item-one-line-container-height: 40px;
|
|
848
|
+
--md-list-item-top-space: 8px;
|
|
849
|
+
--md-list-item-bottom-space: 8px;
|
|
850
|
+
--md-list-item-label-text-size: 16px;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
oscd-list-item.toggle-button oscd-icon[slot='start'] {
|
|
854
|
+
/* Every icon in the side panel is 28px (matches the tree icons). */
|
|
855
|
+
--md-icon-size: 28px;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/* Collapsed rail: icon-only toggle, matching the rail items (44px target,
|
|
859
|
+
28px glyph, glyph lands at x24 like the other rail icons). */
|
|
860
|
+
oscd-icon-button.toggle-button {
|
|
861
|
+
--md-icon-button-icon-size: 28px;
|
|
862
|
+
--md-icon-button-state-layer-height: 44px;
|
|
863
|
+
--md-icon-button-state-layer-width: 44px;
|
|
864
|
+
}
|
|
150
865
|
`;
|
|
151
866
|
__decorate([
|
|
152
867
|
property({ type: Array })
|
|
153
868
|
], EditorPluginsPanel.prototype, "editors", void 0);
|
|
154
869
|
__decorate([
|
|
155
870
|
property({ type: Number })
|
|
156
|
-
], EditorPluginsPanel.prototype, "
|
|
871
|
+
], EditorPluginsPanel.prototype, "selectedEditor", void 0);
|
|
157
872
|
__decorate([
|
|
158
873
|
property({ type: String })
|
|
159
874
|
], EditorPluginsPanel.prototype, "locale", void 0);
|
|
160
875
|
__decorate([
|
|
876
|
+
localstorage({ default: true }),
|
|
161
877
|
property({ type: Boolean, reflect: true })
|
|
162
|
-
], EditorPluginsPanel.prototype, "expanded",
|
|
878
|
+
], EditorPluginsPanel.prototype, "expanded", void 0);
|
|
879
|
+
__decorate([
|
|
880
|
+
property({ type: Boolean, reflect: true, attribute: 'search-mode' })
|
|
881
|
+
], EditorPluginsPanel.prototype, "searchMode", void 0);
|
|
882
|
+
__decorate([
|
|
883
|
+
state()
|
|
884
|
+
], EditorPluginsPanel.prototype, "searchValue", void 0);
|
|
885
|
+
__decorate([
|
|
886
|
+
state()
|
|
887
|
+
], EditorPluginsPanel.prototype, "focusedTree", void 0);
|
|
888
|
+
__decorate([
|
|
889
|
+
localstorage({ default: [] }),
|
|
890
|
+
state()
|
|
891
|
+
], EditorPluginsPanel.prototype, "expandedIds", void 0);
|
|
892
|
+
__decorate([
|
|
893
|
+
localstorage({ default: [] }),
|
|
894
|
+
state()
|
|
895
|
+
], EditorPluginsPanel.prototype, "pinnedPluginIds", void 0);
|
|
896
|
+
__decorate([
|
|
897
|
+
localstorage({ default: [] }),
|
|
898
|
+
state()
|
|
899
|
+
], EditorPluginsPanel.prototype, "pinnedExpanded", void 0);
|
|
163
900
|
EditorPluginsPanel = __decorate([
|
|
164
901
|
localized()
|
|
165
902
|
], EditorPluginsPanel);
|