@iyulab/modern-app 0.42.2 → 0.43.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 +10 -0
- package/dist/components/SidebarButton.d.ts +9 -0
- package/dist/components/SidebarButton.js +6 -2
- package/dist/components/SidebarButton.styles.js +8 -0
- package/dist/layouts/SidebarLayout.d.ts +5 -0
- package/dist/layouts/SidebarLayout.js +7 -1
- package/package.json +1 -1
- package/skills/modern-app/references/layout.md +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.43.0] - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Sidebar toggle buttons** — `SidebarButtonConfig.pressed` (`boolean` or a function). The button carries
|
|
8
|
+
`aria-pressed` and, when on, a soft surface and an accented icon (`--app-sidebar-pressed-bg`,
|
|
9
|
+
`--app-sidebar-pressed-icon-color`), visible in the slim sidebar and distinct from the current page's link. A
|
|
10
|
+
setting such as a field or dark mode no longer has to rename itself "turn on / turn off" to show its state. The
|
|
11
|
+
shell reads `pressed` on every render and re-renders after the button's `onClick`.
|
|
12
|
+
|
|
3
13
|
## [0.42.2] - 2026-10-07
|
|
4
14
|
|
|
5
15
|
### Fixed
|
|
@@ -12,6 +12,13 @@ export interface SidebarButtonConfig extends SidebarPermissionGuard {
|
|
|
12
12
|
lib?: string;
|
|
13
13
|
label?: string | DirectiveResult;
|
|
14
14
|
styles?: StyleMap<ElementParts>;
|
|
15
|
+
/**
|
|
16
|
+
* 켬/끔 버튼(토글)의 지금 상태 — 지정하면 안쪽 버튼이 `aria-pressed` 를 싣고 켜졌을 때 눌린 모양(옅은 면 · 강조 아이콘)으로
|
|
17
|
+
* 그려진다(슬림 모드에서도 아이콘으로 보인다). 이름은 상태와 무관하게 하나로 둔다(«현장 모드» — «켜기/끄기» 가 아니라).
|
|
18
|
+
* 셸은 렌더마다 읽는다: 값이면 설정 객체의 그 값을, 함수면 그 결과를. 그 버튼의 `onClick` 뒤에는 셸이 다시 그리므로,
|
|
19
|
+
* 클릭이 상태를 바꾸는 흔한 경우는 따로 할 일이 없다. 미지정이면 일반 버튼이다.
|
|
20
|
+
*/
|
|
21
|
+
pressed?: boolean | (() => boolean);
|
|
15
22
|
onClick?: (event?: Event) => void;
|
|
16
23
|
}
|
|
17
24
|
/**
|
|
@@ -25,6 +32,8 @@ export declare class SidebarButton extends StyledElement<ElementParts> {
|
|
|
25
32
|
icon?: string;
|
|
26
33
|
/** `icon`을 해석할 등록 라이브러리 이름 */
|
|
27
34
|
lib?: string;
|
|
35
|
+
/** 켬/끔 상태 — 지정되면 토글 버튼(`aria-pressed`). 미지정이면 일반 버튼. */
|
|
36
|
+
pressed?: boolean;
|
|
28
37
|
/** 버튼 텍스트 라벨 */
|
|
29
38
|
label?: string | DirectiveResult;
|
|
30
39
|
/** 이 버튼이 여는 팝업의 종류 — 안쪽 `<button>` 의 `aria-haspopup` 으로 간다(셸의 `type: 'menu'` 트리거). */
|
|
@@ -22,7 +22,8 @@ var l = class extends n {
|
|
|
22
22
|
return a`
|
|
23
23
|
<button part="base" ?compact=${this.compact}
|
|
24
24
|
aria-haspopup=${c(this.haspopup)}
|
|
25
|
-
aria-expanded=${c(this.haspopup ? String(this.expanded) : void 0)}
|
|
25
|
+
aria-expanded=${c(this.haspopup ? String(this.expanded) : void 0)}
|
|
26
|
+
aria-pressed=${c(this.pressed === void 0 ? void 0 : String(this.pressed))}>
|
|
26
27
|
<u-icon part="icon"
|
|
27
28
|
.lib=${this.lib}
|
|
28
29
|
.name=${this.icon}
|
|
@@ -38,6 +39,9 @@ var l = class extends n {
|
|
|
38
39
|
t([s({
|
|
39
40
|
type: Boolean,
|
|
40
41
|
reflect: !0
|
|
41
|
-
}), e("design:type", Object)], l.prototype, "compact", void 0), t([s({ type: String }), e("design:type", String)], l.prototype, "icon", void 0), t([s({ type: String }), e("design:type", String)], l.prototype, "lib", void 0), t([s({
|
|
42
|
+
}), e("design:type", Object)], l.prototype, "compact", void 0), t([s({ type: String }), e("design:type", String)], l.prototype, "icon", void 0), t([s({ type: String }), e("design:type", String)], l.prototype, "lib", void 0), t([s({
|
|
43
|
+
type: Boolean,
|
|
44
|
+
reflect: !0
|
|
45
|
+
}), e("design:type", Boolean)], l.prototype, "pressed", void 0), t([s({ type: String }), e("design:type", Object)], l.prototype, "label", void 0), t([s({ type: String }), e("design:type", String)], l.prototype, "haspopup", void 0), t([s({ type: Boolean }), e("design:type", Object)], l.prototype, "expanded", void 0), l = t([o("u-sidebar-button")], l);
|
|
42
46
|
//#endregion
|
|
43
47
|
export { l as SidebarButton };
|
|
@@ -18,6 +18,14 @@ var t = e`
|
|
|
18
18
|
:host(:active) {
|
|
19
19
|
background-color: var(--u-bg-color-active, #EEEEEE);
|
|
20
20
|
}
|
|
21
|
+
/* 켜진 토글 — «현재 페이지»(활성 링크의 진한 면)와 구별되는 옅은 면 + 강조 아이콘. 위치와 설정이 같은 모양이면 섞인다.
|
|
22
|
+
아이콘 색이 상태를 나르므로 라벨이 숨는 슬림 모드에서도 보인다. */
|
|
23
|
+
:host([pressed]) {
|
|
24
|
+
background-color: var(--app-sidebar-pressed-bg, var(--u-bg-color-active, #EEEEEE));
|
|
25
|
+
}
|
|
26
|
+
:host([pressed]) u-icon {
|
|
27
|
+
color: var(--app-sidebar-pressed-icon-color, var(--u-primary-color, #1976D2));
|
|
28
|
+
}
|
|
21
29
|
|
|
22
30
|
button {
|
|
23
31
|
all: unset;
|
|
@@ -97,6 +97,11 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
|
|
|
97
97
|
private renderItem;
|
|
98
98
|
/** 현재 경로와 패턴 매칭 여부 확인 */
|
|
99
99
|
private isMatchedLink;
|
|
100
|
+
/**
|
|
101
|
+
* 버튼 항목 클릭 — 소비자의 `onClick` 을 부른 뒤 다시 그린다. 켬/끔 버튼(`pressed`)은 그 클릭이 상태를 바꾸는 것이
|
|
102
|
+
* 흔하고, 설정 객체의 값이나 함수의 결과는 셸이 렌더할 때만 읽힌다.
|
|
103
|
+
*/
|
|
104
|
+
private handleButtonItemClick;
|
|
100
105
|
/** 브랜드 로고 클릭 핸들러: `href` 지정 시 해당 경로로, 아니면 홈으로 이동 */
|
|
101
106
|
/**
|
|
102
107
|
* 로고 렌더링: 아이콘명(문자열, 기존 동작) | 이미지({src,alt,href}) | 커스텀 렌더 함수.
|
|
@@ -328,7 +328,8 @@ var M = class extends i {
|
|
|
328
328
|
.lib="${e.lib}"
|
|
329
329
|
.label="${e.label}"
|
|
330
330
|
.styles="${e.styles}"
|
|
331
|
-
|
|
331
|
+
.pressed=${typeof e.pressed == "function" ? e.pressed() : e.pressed}
|
|
332
|
+
@click=${(t) => this.handleButtonItemClick(e, t)}
|
|
332
333
|
></u-sidebar-button>
|
|
333
334
|
`;
|
|
334
335
|
if (e.type === "menu") return this.renderMenu(e);
|
|
@@ -376,6 +377,10 @@ var M = class extends i {
|
|
|
376
377
|
}
|
|
377
378
|
return d;
|
|
378
379
|
}
|
|
380
|
+
handleButtonItemClick(e, t) {
|
|
381
|
+
let n = e.onClick?.(t);
|
|
382
|
+
this.requestUpdate(), n instanceof Promise && n.finally(() => this.requestUpdate());
|
|
383
|
+
}
|
|
379
384
|
static {
|
|
380
385
|
this.DEFAULT_ICONS = {
|
|
381
386
|
lib: "internal",
|
|
@@ -403,6 +408,7 @@ var M = class extends i {
|
|
|
403
408
|
return u`
|
|
404
409
|
<u-link class="logo-link" aria-label=${t}>
|
|
405
410
|
<span class="logo">
|
|
411
|
+
<!-- html-sink: documented HTML contract — a logo function returns markup the developer writes -->
|
|
406
412
|
${typeof n == "string" ? g(n) : n}
|
|
407
413
|
</span>
|
|
408
414
|
</u-link>
|
package/package.json
CHANGED
|
@@ -181,6 +181,7 @@ interface SidebarButtonConfig {
|
|
|
181
181
|
label: string | DirectiveResult;
|
|
182
182
|
onClick: () => void;
|
|
183
183
|
styles?: StyleMap<string>;
|
|
184
|
+
pressed?: boolean | (() => boolean); // a toggle: aria-pressed + the pressed look
|
|
184
185
|
}
|
|
185
186
|
```
|
|
186
187
|
|
|
@@ -190,6 +191,21 @@ Example:
|
|
|
190
191
|
{ type: 'button', icon: 'logout', label: 'Sign Out', onClick: () => auth.signOut() }
|
|
191
192
|
```
|
|
192
193
|
|
|
194
|
+
A setting the person turns on and off (dark mode, a field or compact mode, notifications) is a toggle: give it
|
|
195
|
+
`pressed`. The button then carries `aria-pressed`, and when on it shows a soft surface and an accented icon
|
|
196
|
+
(`--app-sidebar-pressed-bg`, `--app-sidebar-pressed-icon-color`) — visible when the sidebar is slim, and distinct
|
|
197
|
+
from the current page's link. Keep one name for both states. The shell reads `pressed` on every render — a value
|
|
198
|
+
from the config object, or a function's result — and re-renders after the button's `onClick`, so a click that
|
|
199
|
+
flips the state needs nothing more:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const fieldMode = {
|
|
203
|
+
type: 'button', icon: 'hand', label: 'Field mode',
|
|
204
|
+
pressed: () => density.isField(),
|
|
205
|
+
onClick: () => density.toggleField(),
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
193
209
|
For a button that opens a menu, use `type: 'menu'` below — not a button plus your own popover.
|
|
194
210
|
|
|
195
211
|
---
|