@design.estate/dees-catalog 12.2.0 → 12.4.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/dist_bundle/bundle.js +120 -18
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-appui/dees-appui-mainmenu/dees-appui-mainmenu.d.ts +5 -0
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.d.ts +18 -0
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.demo.js +10 -1
- package/dist_ts_web/elements/00group-button/dees-button/dees-button.js +103 -5
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.d.ts +2 -1
- package/dist_ts_web/elements/00group-form/dees-form/dees-form.js +2 -1
- package/dist_ts_web/elements/00group-input/dees-input-wysiwyg/dees-input-wysiwyg.d.ts +12 -2
- package/dist_ts_web/elements/00group-input/dees-input-wysiwyg/dees-input-wysiwyg.js +11 -1
- package/dist_ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.js +7 -2
- package/dist_ts_web/elements/00group-workspace/dees-workspace/dees-workspace.js +3 -3
- package/dist_ts_web/elements/00group-workspace/dees-workspace/styles.js +10 -2
- package/package.json +1 -1
- package/readme.md +27 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-appui/dees-appui-mainmenu/dees-appui-mainmenu.ts +6 -0
- package/ts_web/elements/00group-button/dees-button/dees-button.demo.ts +10 -0
- package/ts_web/elements/00group-button/dees-button/dees-button.ts +96 -3
- package/ts_web/elements/00group-form/dees-form/dees-form.ts +2 -1
- package/ts_web/elements/00group-input/dees-input-wysiwyg/dees-input-wysiwyg.ts +11 -1
- package/ts_web/elements/00group-simple/dees-simple-appdash/dees-simple-appdash.ts +6 -1
- package/ts_web/elements/00group-workspace/dees-workspace/dees-workspace.ts +2 -2
- package/ts_web/elements/00group-workspace/dees-workspace/styles.ts +9 -1
package/readme.md
CHANGED
|
@@ -20,7 +20,7 @@ switcher**, **view-toggle**, and **single-selection toggle** all lead to
|
|
|
20
20
|
| Switch between a few views, modes, or time periods; segmented control | [`dees-input-multitoggle`](#deesinputmultitoggle) | One selected value in a shared track. Use keyed options when labels differ from stored values. |
|
|
21
21
|
| Choose one form answer from visible radio options | [`dees-input-radiogroup`](#deesinputradiogroup) | Radio indicators, required validation, optional payload values. |
|
|
22
22
|
| Turn one setting on or off | [`dees-input-toggle`](#deesinputtoggle), [`dees-input-checkbox`](#deesinputcheckbox) | Boolean setting or checkable form choice. Multitoggle's boolean mode is useful when both alternatives need explicit labels. |
|
|
23
|
-
| Toggle an independent toolbar option | [`dees-input-toggle`](#deesinputtoggle) |
|
|
23
|
+
| Toggle an independent toolbar option | [`dees-button`](#deesbutton) with `pressed`, [`dees-input-toggle`](#deesinputtoggle) | A toolbar or app-bar command that stays on uses a button with `pressed`; a labelled setting uses the switch. A set of independent options is not a single-selection multitoggle. |
|
|
24
24
|
| Choose from a longer menu of values | [`dees-input-dropdown`](#deesinputdropdown) | Searchable single selection; `enableSearch` defaults to true. |
|
|
25
25
|
| Group related commands | [`dees-button-group`](#deesbuttongroup), [`dees-actionbar`](#deesactionbar) | Layout for actions. A button group does not own a selected value. |
|
|
26
26
|
| Navigate open content panels, with optional close controls | [`dees-appui-tabs`](#deesappuitabs) | Tab selection, close actions and keyboard focus. Use multitoggle for changing a display mode within a panel. |
|
|
@@ -299,10 +299,36 @@ button.text = 'Click me';
|
|
|
299
299
|
status="pending" // Options: normal, pending, success, error
|
|
300
300
|
disabled={false} // Optional: disables the button
|
|
301
301
|
full-width // Optional: stretches the button face to the host width
|
|
302
|
+
.pressed=${true} // Optional: makes this button a toggle — true (on), false (off), unset (not a toggle)
|
|
302
303
|
@click=${handleClick}
|
|
303
304
|
>Click me</dees-button>
|
|
304
305
|
```
|
|
305
306
|
|
|
307
|
+
`pressed` turns a button into a toggle and is what assistive technology announces: the face
|
|
308
|
+
carries `aria-pressed="true"`/`"false"` while `pressed` is set, and carries no `aria-pressed`
|
|
309
|
+
at all while it is unset, so a plain button is never announced as an unpressed toggle. Use it
|
|
310
|
+
for toolbar options — a DevTools or sidebar button in an app bar — rather than expressing the
|
|
311
|
+
state through `type`, which decides the button's role colour. The on face is an accent wash,
|
|
312
|
+
ring and label layered over whatever `type` paints (a filled `accent`/`destructive` face
|
|
313
|
+
recesses behind a scrim instead), it stays on when the pointer leaves, and a status face keeps
|
|
314
|
+
its own surface while it shows; `type="link"` has no surface at all, so its on state is only a
|
|
315
|
+
permanent underline — prefer `ghost` or `outline` for a link-styled toggle. At `size="icon"`
|
|
316
|
+
the accessible name comes from `text`, so an icon-only toggle must still set it.
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
<dees-button
|
|
320
|
+
size="icon" icon="lucide:terminal" text="DevTools"
|
|
321
|
+
.pressed=${this.devtoolsOpen}
|
|
322
|
+
@clicked=${() => { this.devtoolsOpen = !this.devtoolsOpen; }}
|
|
323
|
+
></dees-button>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
The button is stateless: a click never flips `pressed`, so the consumer owns the state. Only
|
|
327
|
+
the on state reflects to the host, which makes `dees-button[pressed]` and `:host([pressed])`
|
|
328
|
+
select a toggle that is on; the attribute form (`<dees-button pressed>`) therefore means on —
|
|
329
|
+
any value, including `pressed="false"`, means on, as for every HTML boolean attribute — and off
|
|
330
|
+
has to be set through the property.
|
|
331
|
+
|
|
306
332
|
The button is keyboard operable: its face carries `role="button"` and `tabindex`, Enter and
|
|
307
333
|
Space activate it (dispatching a real `click`, so both `@click` and `@clicked` fire), a
|
|
308
334
|
disabled button leaves the tab order, and keyboard focus draws a `:focus-visible` ring.
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: "@design.estate/dees-catalog",
|
|
6
|
-
version: "12.
|
|
6
|
+
version: "12.4.0",
|
|
7
7
|
description: "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript."
|
|
8
8
|
}
|
|
@@ -5,6 +5,13 @@ import type { DeesButton } from './dees-button.js';
|
|
|
5
5
|
|
|
6
6
|
const clicked = (event: Event) => showDemoResult(event.currentTarget as Element, `${(event.target as DeesButton).text || 'Button'} selected.`);
|
|
7
7
|
|
|
8
|
+
/** The button never flips `pressed` itself — the consumer owns the state, as it does here. */
|
|
9
|
+
const toggled = (event: Event) => {
|
|
10
|
+
const button = event.target as DeesButton;
|
|
11
|
+
button.pressed = !button.pressed;
|
|
12
|
+
showDemoResult(button, `${button.text}: ${button.pressed ? 'on' : 'off'}`);
|
|
13
|
+
};
|
|
14
|
+
|
|
8
15
|
export const demoFunc = () => demoPage('Button', 'A shared shape and rhythm for actions, with a clear hierarchy of intent.', html`
|
|
9
16
|
${demoSection('Intent', 'Accent marks the primary action. Quieter treatments keep supporting actions available.', html`
|
|
10
17
|
<div class="dees-demo-row" @clicked=${clicked}>${(['default', 'accent', 'secondary', 'destructive', 'outline', 'ghost', 'link'] as const).map((type) => html`<dees-button .type=${type} .text=${type}></dees-button>`)}</div>
|
|
@@ -18,6 +25,9 @@ export const demoFunc = () => demoPage('Button', 'A shared shape and rhythm for
|
|
|
18
25
|
${demoOutput()}${demoCode('<dees-button size="icon" text="Settings" icon="lucide:settings"></dees-button>')}`)}
|
|
19
26
|
${demoSection('Status', 'Pending, success, and error faces are inert. Disabled actions stay visible.', html`
|
|
20
27
|
<div class="dees-demo-row">${(['normal', 'pending', 'success', 'error'] as const).map((status) => html`<dees-button .status=${status} .text=${status}></dees-button>`)}<dees-button disabled text="Unavailable"></dees-button></div>`)}
|
|
28
|
+
${demoSection('Toggle', 'Setting pressed makes a button a toggle and announces it as one. The on face layers over the type face and stays on when the pointer leaves. A button without pressed stays a plain action.', html`
|
|
29
|
+
<div class="dees-demo-row" @clicked=${toggled}><dees-button type="ghost" size="icon" icon="lucide:terminal" text="DevTools" .pressed=${true}></dees-button><dees-button type="ghost" icon="lucide:panel-left" text="Sidebar" .pressed=${false}></dees-button><dees-button type="outline" text="Wrap lines" .pressed=${true}></dees-button><dees-button text="Live preview" .pressed=${false}></dees-button><dees-button type="accent" text="Recording" .pressed=${true}></dees-button></div>
|
|
30
|
+
${demoOutput()}${demoCode('<dees-button size="icon" icon="lucide:terminal" text="DevTools" .pressed=${this.devtoolsOpen} @clicked=${() => this.devtoolsOpen = !this.devtoolsOpen}></dees-button>')}`)}
|
|
21
31
|
${demoSection('Event data', 'Activation uses the clicked event for pointer and keyboard input.', html`
|
|
22
32
|
<div class="dees-demo-row"><dees-button text="Inspect event" .eventDetailData=${'workspace-preview'} @clicked=${(event: CustomEvent) => showDemoResult(event.currentTarget as Element, event.detail)}></dees-button></div>${demoOutput()}`)}
|
|
23
33
|
${demoSection('In a form', 'The same button proportions carry through to form submission.', workspaceForm())}
|
|
@@ -63,6 +63,29 @@ export class DeesButton extends DeesElement {
|
|
|
63
63
|
})
|
|
64
64
|
accessor status: 'normal' | 'pending' | 'success' | 'error' = 'normal';
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Toggle state, reported to assistive technology as `aria-pressed`.
|
|
68
|
+
*
|
|
69
|
+
* Tri-state by design: `undefined` (the default) means this button is not a toggle at all
|
|
70
|
+
* and nothing is announced, `false` a toggle that is off, `true` a toggle that is on. A
|
|
71
|
+
* plain boolean defaulting to `false` would announce every button in the catalog as an
|
|
72
|
+
* unpressed toggle.
|
|
73
|
+
*
|
|
74
|
+
* The element stays stateless — a click never flips `pressed`. The consumer owns the state
|
|
75
|
+
* and sets it from its `clicked` handler.
|
|
76
|
+
*
|
|
77
|
+
* Only the on state reflects to the host, so `:host([pressed])` and `dees-button[pressed]`
|
|
78
|
+
* select a toggle that is on. Off and not-a-toggle are indistinguishable as attributes, so
|
|
79
|
+
* the attribute form (`<dees-button pressed>`) can only express "on" — any value, including
|
|
80
|
+
* `pressed="false"`, means on, as for every HTML boolean attribute. The off state has to be
|
|
81
|
+
* set through the property.
|
|
82
|
+
*/
|
|
83
|
+
@property({
|
|
84
|
+
type: Boolean,
|
|
85
|
+
reflect: true
|
|
86
|
+
})
|
|
87
|
+
accessor pressed: boolean | undefined = undefined;
|
|
88
|
+
|
|
66
89
|
/**
|
|
67
90
|
* Corner treatment. 'squircle' (default) is the standard rounded rect for
|
|
68
91
|
* controls that align with fields and other rectangular siblings; 'pill'
|
|
@@ -139,6 +162,12 @@ export class DeesButton extends DeesElement {
|
|
|
139
162
|
--button-success-fg: var(--dees-color-text-success);
|
|
140
163
|
--button-error-bg: color-mix(in srgb, var(--dees-color-accent-error) 20%, transparent);
|
|
141
164
|
--button-error-fg: var(--dees-color-text-error);
|
|
165
|
+
/* toggle-on face: the shared selected-surface wash, an accent ring and the accent
|
|
166
|
+
label colour. All three inputs are theme-aware tokens, so this face needs no
|
|
167
|
+
bright/dark split of its own. */
|
|
168
|
+
--button-pressed-wash: var(--dees-color-nav-selected);
|
|
169
|
+
--button-pressed-ring: color-mix(in srgb, var(--dees-color-accent-primary) 50%, transparent);
|
|
170
|
+
--button-pressed-fg: var(--dees-color-link);
|
|
142
171
|
}
|
|
143
172
|
:host([gobright]) {
|
|
144
173
|
--button-pending-bg: var(--dees-color-accent-primary);
|
|
@@ -158,11 +187,15 @@ export class DeesButton extends DeesElement {
|
|
|
158
187
|
}
|
|
159
188
|
|
|
160
189
|
/* A full-width button is sized by its container, so its label has to yield rather
|
|
161
|
-
than spill out of the face when the container is narrower than the text.
|
|
190
|
+
than spill out of the face when the container is narrower than the text. It clips with
|
|
191
|
+
a 1px margin rather than hiding: a button face is no scroll container, and the
|
|
192
|
+
toggle-on overlay sits 1px outside the padding box so that its ring lands on the
|
|
193
|
+
type's own border edge — overflow: hidden would cut exactly that pixel away. */
|
|
162
194
|
:host([full-width]) .button {
|
|
163
195
|
width: 100%;
|
|
164
196
|
min-width: 0;
|
|
165
|
-
overflow:
|
|
197
|
+
overflow: clip;
|
|
198
|
+
overflow-clip-margin: 1px;
|
|
166
199
|
}
|
|
167
200
|
|
|
168
201
|
:host([full-width]) .textbox {
|
|
@@ -366,6 +399,59 @@ export class DeesButton extends DeesElement {
|
|
|
366
399
|
text-decoration-color: currentColor;
|
|
367
400
|
}
|
|
368
401
|
|
|
402
|
+
/* Toggle-on face (the pressed property).
|
|
403
|
+
A toggle has to stay visibly on once the pointer leaves it, so this cannot be the
|
|
404
|
+
hover face; it layers on top of whatever the type paints instead of replacing it. The
|
|
405
|
+
wash rides on an overlay rather than on the face's own background, because every
|
|
406
|
+
type, hover and active rule sets the background shorthand and would reset a layer
|
|
407
|
+
declared on the face itself. isolation scopes the overlay's negative layer to this
|
|
408
|
+
face, which paints it above the type's background and below the label and icon. */
|
|
409
|
+
.button.pressed {
|
|
410
|
+
isolation: isolate;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.button.pressed::after {
|
|
414
|
+
content: '';
|
|
415
|
+
position: absolute;
|
|
416
|
+
/* the face's border box, so the ring lands on the type's own border edge */
|
|
417
|
+
inset: -1px;
|
|
418
|
+
z-index: -1;
|
|
419
|
+
border-radius: inherit;
|
|
420
|
+
corner-shape: inherit;
|
|
421
|
+
background: var(--button-pressed-wash);
|
|
422
|
+
box-shadow: inset 0 0 0 1px var(--button-pressed-ring);
|
|
423
|
+
pointer-events: none;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* A filled face already carries the accent as its surface, where an accent wash cannot
|
|
427
|
+
read, so it recesses behind a scrim with a light inner ring instead. Both faces are
|
|
428
|
+
dark in either theme, which is why these two values do not follow the theme. */
|
|
429
|
+
.button.accent,
|
|
430
|
+
.button.destructive {
|
|
431
|
+
--button-pressed-wash: color-mix(in srgb, black 24%, transparent);
|
|
432
|
+
--button-pressed-ring: color-mix(in srgb, white 40%, transparent);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* At size="icon" there is no label, so the foreground carries the state as well. Filled
|
|
436
|
+
faces keep their on-accent label, and a status face keeps its own. */
|
|
437
|
+
.button.pressed:not(.accent):not(.destructive):not(.pending):not(.success):not(.error) {
|
|
438
|
+
color: var(--button-pressed-fg);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* A status face owns the whole surface for as long as it shows, and type="link" has no
|
|
442
|
+
surface to wash at all (no border, no padding) — its on state is the hover underline
|
|
443
|
+
made permanent. aria-pressed reports the state in every one of these cases. */
|
|
444
|
+
.button.pressed.pending::after,
|
|
445
|
+
.button.pressed.success::after,
|
|
446
|
+
.button.pressed.error::after,
|
|
447
|
+
.button.pressed.link::after {
|
|
448
|
+
content: none;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.button.pressed.link {
|
|
452
|
+
text-decoration-color: currentColor;
|
|
453
|
+
}
|
|
454
|
+
|
|
369
455
|
/* Status states */
|
|
370
456
|
.button.pending,
|
|
371
457
|
.button.success,
|
|
@@ -585,10 +671,16 @@ export class DeesButton extends DeesElement {
|
|
|
585
671
|
// size-icon additionally suppresses text to keep its square geometry.
|
|
586
672
|
const showText = Boolean(effectiveText) && !(actualSize === 'icon' && effectiveIcon);
|
|
587
673
|
|
|
674
|
+
// A button that is not a toggle must carry no `aria-pressed` attribute at all, and an
|
|
675
|
+
// attribute binding cannot remove one — lit only removes on its `nothing` sentinel, which
|
|
676
|
+
// `@design.estate/dees-element` does not re-export. The state therefore goes through the
|
|
677
|
+
// reflected ARIA property, the same channel `.ariaLabel` uses below: `null` removes it.
|
|
588
678
|
return html`
|
|
589
679
|
<div
|
|
590
680
|
part="button"
|
|
591
|
-
class="button ${this.isHidden ? 'hidden' : ''} ${actualType} size-${actualSize} shape-${this.shape} ${this.status} ${this.
|
|
681
|
+
class="button ${this.isHidden ? 'hidden' : ''} ${actualType} size-${actualSize} shape-${this.shape} ${this.status} ${this.pressed
|
|
682
|
+
? 'pressed'
|
|
683
|
+
: ''} ${this.disabled
|
|
592
684
|
? 'disabled'
|
|
593
685
|
: ''}"
|
|
594
686
|
role="button"
|
|
@@ -596,6 +688,7 @@ export class DeesButton extends DeesElement {
|
|
|
596
688
|
aria-disabled=${this.disabled ? 'true' : 'false'}
|
|
597
689
|
aria-busy=${this.status === 'pending' ? 'true' : 'false'}
|
|
598
690
|
.ariaLabel=${showText ? null : effectiveText || null}
|
|
691
|
+
.ariaPressed=${this.pressed === undefined ? null : String(this.pressed)}
|
|
599
692
|
@click="${this.dispatchClick}"
|
|
600
693
|
@keydown=${this.handleKeydown}
|
|
601
694
|
@keyup=${this.handleKeyup}
|
|
@@ -157,7 +157,8 @@ export class DeesForm extends DeesElement {
|
|
|
157
157
|
public static demoGroups = ['Form'];
|
|
158
158
|
|
|
159
159
|
public name: string = 'myform';
|
|
160
|
-
|
|
160
|
+
/** emits the collected form data on every child change; the only producer is `collectFormData()` */
|
|
161
|
+
public changeSubject = new domtools.plugins.smartrx.rxjs.Subject<TFormData>();
|
|
161
162
|
public readyDeferred = domtools.plugins.smartpromise.defer();
|
|
162
163
|
|
|
163
164
|
/**
|
|
@@ -85,7 +85,17 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
|
|
85
85
|
public blockOperations: WysiwygBlockOperations;
|
|
86
86
|
private inputHandler: WysiwygInputHandler;
|
|
87
87
|
private keyboardHandler: WysiwygKeyboardHandler;
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Block drag and drop, public alongside `blockOperations` because the drag *interaction* has
|
|
90
|
+
* no other seam: `dragState`, the drop indicator and the global drag-over listener all live in
|
|
91
|
+
* this handler, and a native HTML5 drag sequence cannot be synthesised from script, so a host
|
|
92
|
+
* or a test that exercises dragging has to drive `handleDragStart` / `handleDragEnd` itself.
|
|
93
|
+
* Reordering a block programmatically needs none of that: `blockOperations.moveBlock(blockId,
|
|
94
|
+
* targetIndex)` is the public path for that, on the public `blocks` accessor. The remaining
|
|
95
|
+
* handlers stay private because everything they own is reachable through ordinary input and
|
|
96
|
+
* keyboard events.
|
|
97
|
+
*/
|
|
98
|
+
public dragDropHandler: WysiwygDragDropHandler;
|
|
89
99
|
private history: WysiwygHistory;
|
|
90
100
|
|
|
91
101
|
public static styles = [
|
|
@@ -109,7 +109,12 @@ export class DeesSimpleAppDash extends DeesElement {
|
|
|
109
109
|
.controlbar { grid-column: 1 / -1; display: flex; align-items: center; justify-content: flex-end; border-top: 1px solid var(--dees-color-border-subtle); background: var(--dees-color-bg-secondary); font-size: 11px; color: var(--dees-color-text-secondary); }
|
|
110
110
|
.control { display: flex; align-items: center; gap: 6px; padding: 0 12px; height: 100%; }
|
|
111
111
|
.status-terminal { border-left: 1px solid var(--dees-color-border-subtle); }
|
|
112
|
-
|
|
112
|
+
/* The on face is the catalog's toggle-on face: the shared selected surface plus the accent
|
|
113
|
+
*text* colour, not --dees-color-accent-primary, which only reaches 3.6:1 as a label. The
|
|
114
|
+
hovered rule exists because the shared button:hover rule below outranks a plain
|
|
115
|
+
[aria-pressed] rule and would wash the on state away under the pointer. */
|
|
116
|
+
.status-terminal[aria-pressed='true'] { color: var(--dees-color-link); background: var(--dees-color-nav-selected); }
|
|
117
|
+
.status-terminal[aria-pressed='true']:hover { color: var(--dees-color-link); background: color-mix(in srgb, var(--dees-color-nav-selected) 88%, var(--dees-color-text-primary)); }
|
|
113
118
|
.collapsed .sidebar-header { justify-content: center; padding: 0; }
|
|
114
119
|
.collapsed .sidebar-header > dees-icon, .collapsed .appName, .collapsed .section-label, .collapsed .viewTab span, .collapsed .viewTab .chevron, .collapsed .action span { display: none; }
|
|
115
120
|
.collapsed .collapseToggle { margin: 0; width: 40px; height: 38px; }
|
|
@@ -281,8 +281,8 @@ export class DeesWorkspace extends DeesElement {
|
|
|
281
281
|
<span class="save-state" role="status">${this.isInitializing ? 'Starting…' : this.initializationError ? 'Connection failed' : !ready ? 'Not connected' : modifiedCount ? `${modifiedCount} unsaved` : 'All changes saved'}</span>
|
|
282
282
|
<div class="toolbar-actions">
|
|
283
283
|
<button class="panel-action" type="button" aria-label="Save all files" title="Save all files" ?disabled=${!ready || !modifiedCount} @click=${this.saveAllFiles}><dees-icon icon="lucide:Save" iconSize="16"></dees-icon></button>
|
|
284
|
-
${this.showFileTree ? html`<button class="panel-action" type="button" aria-label="Toggle files" aria-controls="workspace-files" aria-expanded=${!this.isFileTreeCollapsed}
|
|
285
|
-
${this.showTerminal ? html`<button class="panel-action" type="button" aria-label="Toggle terminal" aria-controls="workspace-terminal" aria-expanded=${!this.isTerminalCollapsed}
|
|
284
|
+
${this.showFileTree ? html`<button class="panel-action panel-toggle" type="button" aria-label="Toggle files" aria-controls="workspace-files" aria-expanded=${!this.isFileTreeCollapsed} title="Toggle files" @click=${() => this.isFileTreeCollapsed = !this.isFileTreeCollapsed}><dees-icon icon="lucide:PanelLeft" iconSize="16"></dees-icon></button>` : ''}
|
|
285
|
+
${this.showTerminal ? html`<button class="panel-action panel-toggle" type="button" aria-label="Toggle terminal" aria-controls="workspace-terminal" aria-expanded=${!this.isTerminalCollapsed} title="Toggle terminal" @click=${this.toggleTerminal}><dees-icon icon="lucide:PanelBottom" iconSize="16"></dees-icon></button>` : ''}
|
|
286
286
|
<button class="panel-action" type="button" aria-label="Editor options" aria-haspopup="menu" title="Editor options" @click=${this.showEditorMenu}><dees-icon icon="lucide:Ellipsis" iconSize="16"></dees-icon></button>
|
|
287
287
|
</div>
|
|
288
288
|
</header>
|
|
@@ -15,7 +15,15 @@ export const workspaceStyles = css`
|
|
|
15
15
|
.toolbar-actions, .panel-header-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
|
|
16
16
|
.panel-action { width: 30px; height: 30px; display: grid; place-items: center; border-radius: var(--dees-radius-md); color: var(--dees-color-text-secondary); transition: color 140ms ease, background-color 140ms ease; }
|
|
17
17
|
.panel-action:hover:not(:disabled) { color: var(--dees-color-text-primary); background: var(--dees-color-hover); }
|
|
18
|
-
|
|
18
|
+
/* A panel toggle announces one state, aria-expanded, and paints it with the shared selected
|
|
19
|
+
surface — the same wash and accent label dees-button gives its pressed state. It cannot
|
|
20
|
+
reuse the hover background: an expanded toggle would then be indistinguishable from a
|
|
21
|
+
hovered collapsed one. Both on-state rules repeat the hover rule's :not(:disabled), which
|
|
22
|
+
puts them at (0,4,0) and (0,5,0) against that rule's (0,3,0), so the on face outranks hover
|
|
23
|
+
on specificity instead of on source order, and the hovered-expanded rule keeps it under the
|
|
24
|
+
pointer. A disabled toggle falls back to the base face, the way it does for hover. */
|
|
25
|
+
.panel-action.panel-toggle[aria-expanded='true']:not(:disabled) { color: var(--dees-color-link); background: var(--dees-color-nav-selected); }
|
|
26
|
+
.panel-action.panel-toggle[aria-expanded='true']:not(:disabled):hover { color: var(--dees-color-link); background: color-mix(in srgb, var(--dees-color-nav-selected) 88%, var(--dees-color-text-primary)); }
|
|
19
27
|
.workspace-container { display: flex; flex: 1; min-height: 0; min-width: 0; position: relative; }
|
|
20
28
|
.editor-area { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; overflow: hidden; }
|
|
21
29
|
.filetree-panel { position: relative; flex-shrink: 0; min-width: 0; overflow: hidden; background: var(--dees-color-bg-secondary); }
|