@iyulab/modern-app 0.54.0 → 0.55.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 +23 -0
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.154.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/components/ActionBar.js +2 -2
- package/dist/components/EmptyState.js +2 -2
- package/dist/components/GroupBox.js +2 -2
- package/dist/components/InfoField.js +2 -2
- package/dist/components/InfoSection.js +2 -2
- package/dist/components/MasterDetailLayout.d.ts +9 -1
- package/dist/components/MasterDetailLayout.js +16 -8
- package/dist/components/MasterDetailLayout.styles.js +17 -0
- package/dist/components/PageHeader.js +2 -2
- package/dist/components/SidebarButton.js +2 -2
- package/dist/components/SidebarGroup.js +2 -2
- package/dist/components/SidebarGroup.styles.js +0 -3
- package/dist/components/SidebarLink.js +2 -2
- package/dist/components/SidebarSection.js +2 -2
- package/dist/components/Timeline.js +1 -1
- package/dist/components/TimelineItem.js +2 -2
- package/dist/components/Wizard.js +2 -2
- package/dist/internals/StyledElement.js +2 -2
- package/dist/layouts/SidebarLayout.js +4 -4
- package/dist/react.d.ts +15 -2
- package/dist/react.js +7 -1
- package/dist/unsaved.d.ts +7 -1
- package/dist/unsaved.js +9 -3
- package/package.json +1 -1
- package/skills/modern-app/SKILL.md +2 -1
- package/skills/modern-app/references/components/master-detail-layout.md +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.55.0] - 2026-10-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `u-master-detail-layout` `detail-size`: the detail pane takes that width and the master fills the rest — a wide
|
|
8
|
+
list with a record summary beside it. `masterSize` keeps the opposite shape (narrow list, wide detail).
|
|
9
|
+
- `u-master-detail-layout` `slot="detail-placeholder"`: shown in the detail place while nothing is chosen (side by side).
|
|
10
|
+
- `useUnsavedChanges(dirty)` returns `{ markClean }`, and `UnsavedChangesController` has `markClean()`: the current state
|
|
11
|
+
is saved, so navigations until the next render (React) or update (Lit) do not ask. A save handler that navigates in
|
|
12
|
+
the same tick — `await post(draft); navigate(...)` — was asked «Discard unsaved changes?», because the check still
|
|
13
|
+
read the pre-save render’s `dirty`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Console diagnostics begin with `[@iyulab/modern-app]`, so a message in a console shared by several packages says which one wrote it (several also say what to do next).
|
|
18
|
+
- **Breaking:** `u-master-detail-layout`: while the `detail` slot is empty the master takes the whole width (it stayed at
|
|
19
|
+
`masterSize` beside an empty area). To keep the narrow master with a message beside it, use `detail-placeholder`.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
|
|
23
|
+
- An unused custom property on `u-sidebar-group` (`--group-accent`) — nothing read it; group colours come from the
|
|
24
|
+
`--app-sidebar-group-*` tokens.
|
|
25
|
+
|
|
3
26
|
## [0.54.0] - 2026-10-14
|
|
4
27
|
|
|
5
28
|
### Added
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/decorate.js
|
|
2
2
|
function e(e, t, n, r) {
|
|
3
3
|
var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
|
|
4
4
|
if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/decorateMetadata.js
|
|
2
2
|
function e(e, t) {
|
|
3
3
|
if (typeof Reflect == "object" && typeof Reflect.metadata == "function") return Reflect.metadata(e, t);
|
|
4
4
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { styles as i } from "./ActionBar.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { getLocaleStrings as i } from "../internals/locale.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { styles as i } from "./GroupBox.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./InfoField.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./InfoSection.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -37,8 +37,13 @@ type ElementParts = 'host' | 'master' | 'divider' | 'detail' | 'detail-close';
|
|
|
37
37
|
*/
|
|
38
38
|
export declare class MasterDetailLayout extends StyledElement<ElementParts> {
|
|
39
39
|
static styles: import('lit').CSSResultGroup[];
|
|
40
|
-
/** master 영역의 고정 폭(CSS 길이). detail 은 나머지 공간을 채운다. */
|
|
40
|
+
/** master 영역의 고정 폭(CSS 길이). detail 은 나머지 공간을 채운다. detail 이 비면 master 가 전부 쓴다. */
|
|
41
41
|
masterSize: string;
|
|
42
|
+
/**
|
|
43
|
+
* detail 영역의 고정 폭(CSS 길이) — 주면 detail 이 이 폭이고 master 가 나머지를 채운다(`masterSize` 는 쓰지 않는다).
|
|
44
|
+
* 넓은 목록 곁에 레코드 요약을 붙이는 «곁 패널» 배치. 비우면 종전대로 master 가 고정 폭이다.
|
|
45
|
+
*/
|
|
46
|
+
detailSize?: string;
|
|
42
47
|
/**
|
|
43
48
|
* 이 값(px, 자기 폭 기준) 미만이면 detail 이 전체 오버레이로 전환된다.
|
|
44
49
|
* `masterSize` + 최소한의 읽기 가능한 detail 폭을 더한 값보다 커야 두 패널이
|
|
@@ -52,6 +57,8 @@ export declare class MasterDetailLayout extends StyledElement<ElementParts> {
|
|
|
52
57
|
locale: string;
|
|
53
58
|
/** detail 슬롯 배정 상태 — CSS `:has()` 로는 알 수 없다(`internals/slotted.ts` 참조). */
|
|
54
59
|
private hasDetail;
|
|
60
|
+
/** `detail-placeholder` 슬롯 배정 상태 — detail 이 비었을 때 그 자리에 보인다. */
|
|
61
|
+
private hasPlaceholder;
|
|
55
62
|
/**
|
|
56
63
|
* `:state(detail)` 을 싣는 자리. 인쇄 규칙이 «한 판만 보이는가» 를 호스트에서 갈라야 해서
|
|
57
64
|
* 생겼다(styles 의 인쇄 절 참조).
|
|
@@ -84,6 +91,7 @@ export declare class MasterDetailLayout extends StyledElement<ElementParts> {
|
|
|
84
91
|
*/
|
|
85
92
|
private handleDetailClose;
|
|
86
93
|
private handleDetailSlotChange;
|
|
94
|
+
private handlePlaceholderSlotChange;
|
|
87
95
|
render(): import('lit-html').TemplateResult<1>;
|
|
88
96
|
}
|
|
89
97
|
/** Events `<u-master-detail-layout>` dispatches — bubbling and composed. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { getLocaleStrings as i } from "../internals/locale.js";
|
|
@@ -11,10 +11,12 @@ import "@iyulab/components/dist/components/button/UButton.js";
|
|
|
11
11
|
//#region src/components/MasterDetailLayout.ts
|
|
12
12
|
var u = class extends n {
|
|
13
13
|
constructor(...e) {
|
|
14
|
-
super(...e), this.masterSize = "22rem", this.overlayBreakpoint = 760, this.locale = "", this.hasDetail = !1, this.internals = typeof this.attachInternals == "function" ? this.attachInternals() : void 0, this.handleDetailClose = () => {
|
|
14
|
+
super(...e), this.masterSize = "22rem", this.overlayBreakpoint = 760, this.locale = "", this.hasDetail = !1, this.hasPlaceholder = !1, this.internals = typeof this.attachInternals == "function" ? this.attachInternals() : void 0, this.handleDetailClose = () => {
|
|
15
15
|
this.fire("detail-close", { cancelable: !1 });
|
|
16
16
|
}, this.handleDetailSlotChange = (e) => {
|
|
17
17
|
this.hasDetail = r(e.target);
|
|
18
|
+
}, this.handlePlaceholderSlotChange = (e) => {
|
|
19
|
+
this.hasPlaceholder = r(e.target);
|
|
18
20
|
};
|
|
19
21
|
}
|
|
20
22
|
static {
|
|
@@ -32,13 +34,14 @@ var u = class extends n {
|
|
|
32
34
|
super.disconnectedCallback(), this.resizeObserver?.disconnect();
|
|
33
35
|
}
|
|
34
36
|
updated(e) {
|
|
35
|
-
super.updated(e), e.has("masterSize") && this.style.setProperty("--_master-size", this.masterSize), e.has("hasDetail") && this.internals?.states?.[this.hasDetail ? "add" : "delete"]("detail"), e.has("overlayBreakpoint") && this.applyOverlay(this.getBoundingClientRect().width);
|
|
37
|
+
super.updated(e), e.has("masterSize") && this.style.setProperty("--_master-size", this.masterSize), e.has("detailSize") && (this.detailSize ? this.style.setProperty("--_detail-size", this.detailSize) : this.style.removeProperty("--_detail-size")), e.has("hasDetail") && this.internals?.states?.[this.hasDetail ? "add" : "delete"]("detail"), e.has("overlayBreakpoint") && this.applyOverlay(this.getBoundingClientRect().width);
|
|
36
38
|
}
|
|
37
39
|
render() {
|
|
40
|
+
let e = this.hasDetail ? "" : this.hasPlaceholder ? "placeholder" : "empty";
|
|
38
41
|
return o`
|
|
39
|
-
<div class="master ${this.hasDetail ? "covered" : ""}" part="master"><slot></slot></div>
|
|
40
|
-
<div class="divider ${
|
|
41
|
-
<div class="detail ${
|
|
42
|
+
<div class="master ${this.hasDetail ? "covered" : ""} ${e === "empty" ? "alone" : ""}" part="master"><slot></slot></div>
|
|
43
|
+
<div class="divider ${e}" part="divider"></div>
|
|
44
|
+
<div class="detail ${e}" part="detail">
|
|
42
45
|
<u-button class="detail-close" part="detail-close" appearance="plain"
|
|
43
46
|
aria-label=${i(this.locale || void 0).detailClose}
|
|
44
47
|
@click=${this.handleDetailClose}
|
|
@@ -46,6 +49,7 @@ var u = class extends n {
|
|
|
46
49
|
<u-icon lib="internal" name="x"></u-icon>
|
|
47
50
|
</u-button>
|
|
48
51
|
<slot name="detail" @slotchange=${this.handleDetailSlotChange}></slot>
|
|
52
|
+
<slot name="detail-placeholder" ?hidden=${this.hasDetail} @slotchange=${this.handlePlaceholderSlotChange}></slot>
|
|
49
53
|
</div>
|
|
50
54
|
`;
|
|
51
55
|
}
|
|
@@ -54,8 +58,12 @@ t([c({
|
|
|
54
58
|
type: String,
|
|
55
59
|
attribute: "master-size"
|
|
56
60
|
}), e("design:type", Object)], u.prototype, "masterSize", void 0), t([c({
|
|
61
|
+
type: String,
|
|
62
|
+
attribute: "detail-size",
|
|
63
|
+
reflect: !0
|
|
64
|
+
}), e("design:type", String)], u.prototype, "detailSize", void 0), t([c({
|
|
57
65
|
type: Number,
|
|
58
66
|
attribute: "overlay-breakpoint"
|
|
59
|
-
}), e("design:type", Object)], u.prototype, "overlayBreakpoint", void 0), t([c({ type: String }), e("design:type", Object)], u.prototype, "locale", void 0), t([l(), e("design:type", Object)], u.prototype, "hasDetail", void 0), u = t([s("u-master-detail-layout")], u);
|
|
67
|
+
}), e("design:type", Object)], u.prototype, "overlayBreakpoint", void 0), t([c({ type: String }), e("design:type", Object)], u.prototype, "locale", void 0), t([l(), e("design:type", Object)], u.prototype, "hasDetail", void 0), t([l(), e("design:type", Object)], u.prototype, "hasPlaceholder", void 0), u = t([s("u-master-detail-layout")], u);
|
|
60
68
|
//#endregion
|
|
61
69
|
export { u as MasterDetailLayout };
|
|
@@ -17,6 +17,18 @@ var t = e`
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
.master.alone {
|
|
21
|
+
flex: 1 1 auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:host([detail-size]) .master {
|
|
25
|
+
flex: 1 1 auto;
|
|
26
|
+
}
|
|
27
|
+
:host([detail-size]:not([overlay])) .detail {
|
|
28
|
+
flex: 0 0 var(--_detail-size);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
20
32
|
|
|
21
33
|
:host([overlay]) .master {
|
|
22
34
|
flex: 1 1 auto;
|
|
@@ -34,6 +46,10 @@ var t = e`
|
|
|
34
46
|
:host([overlay]) .divider {
|
|
35
47
|
display: none;
|
|
36
48
|
}
|
|
49
|
+
|
|
50
|
+
:host([overlay]) .detail.placeholder {
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
37
53
|
|
|
38
54
|
.detail {
|
|
39
55
|
flex: 1 1 auto;
|
|
@@ -102,6 +118,7 @@ var t = e`
|
|
|
102
118
|
overflow: visible;
|
|
103
119
|
}
|
|
104
120
|
.detail-close,
|
|
121
|
+
.detail.placeholder,
|
|
105
122
|
:host([overlay]) .detail-close,
|
|
106
123
|
:host([overlay]) .master.covered {
|
|
107
124
|
display: none;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { getLocaleStrings as i } from "../internals/locale.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { DEFAULT_NAV_ICON as r } from "../internals/nav-icon.js";
|
|
5
5
|
import { styles as i } from "./SidebarButton.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { DEFAULT_NAV_ICON as r } from "../internals/nav-icon.js";
|
|
5
5
|
import { SidebarLink as i } from "./SidebarLink.js";
|
|
@@ -2,9 +2,6 @@ import { css as e } from "lit";
|
|
|
2
2
|
//#region src/components/SidebarGroup.styles.ts
|
|
3
3
|
var t = e`
|
|
4
4
|
:host {
|
|
5
|
-
|
|
6
|
-
--group-accent: var(--app-sidebar-active-bg, var(--u-primary-color, #1976D2));
|
|
7
|
-
|
|
8
5
|
display: flex;
|
|
9
6
|
flex-direction: column;
|
|
10
7
|
color: var(--app-sidebar-fg, var(--u-txt-color, #212121));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { getLocaleStrings as r } from "../internals/locale.js";
|
|
5
5
|
import { DEFAULT_NAV_ICON as i } from "../internals/nav-icon.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarSection.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
2
2
|
import { StyledElement as t } from "../internals/StyledElement.js";
|
|
3
3
|
import "./TimelineItem.js";
|
|
4
4
|
import { styles as n } from "./Timeline.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { styles as i } from "./TimelineItem.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { getLocaleStrings as r } from "../internals/locale.js";
|
|
5
5
|
import { styles as i } from "./Wizard.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { property as n } from "lit/decorators.js";
|
|
4
4
|
import { UElement as r } from "@iyulab/components/dist/components/UElement.js";
|
|
5
5
|
//#region src/internals/StyledElement.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import e from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import t from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { slotHasContent as r } from "../internals/slotted.js";
|
|
5
5
|
import { getLocaleStrings as i } from "../internals/locale.js";
|
|
@@ -81,7 +81,7 @@ var M = class extends n {
|
|
|
81
81
|
constructor(...e) {
|
|
82
82
|
super(...e), this.state = "default", this.counts = {}, this.locale = "", this.hasOverlay = !1, this.openMenu = null, this.menuIds = /* @__PURE__ */ new WeakMap(), this.hasNotice = !1, this.routeLoading = !1, this.mainTabStop = !1, this.overlayTrigger = null, this.internals = typeof this.attachInternals == "function" ? this.attachInternals() : void 0, this.currentPath = null, this.unsizedWarnKey = `unsized:${++w}`, this.isMatchedLink = (e) => this.currentPath === null || !e ? !1 : (e = typeof e == "string" ? new URLPattern(e, window.location.origin) : e, e.test(this.currentPath, window.location.origin)), this.handleToggleButtonClick = () => {
|
|
83
83
|
let e = this.screenSize ?? a.screen ?? "large";
|
|
84
|
-
e === "large" ? this.state = this.state === "default" ? "slim" : "default" : e === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : e === "small" ? this.state = this.state === "mobile" ? "mobile-open" : "mobile" : console.warn("
|
|
84
|
+
e === "large" ? this.state = this.state === "default" ? "slim" : "default" : e === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : e === "small" ? this.state = this.state === "mobile" ? "mobile-open" : "mobile" : console.warn("[@iyulab/modern-app] unknown screen size:", e);
|
|
85
85
|
}, this.handleBackdropClick = () => {
|
|
86
86
|
this.state = "slim";
|
|
87
87
|
}, this.handleRouteBegin = (e) => {
|
|
@@ -155,7 +155,7 @@ var M = class extends n {
|
|
|
155
155
|
e.preventDefault();
|
|
156
156
|
}, this.handleScreenResize = (e) => {
|
|
157
157
|
let t = e.detail.size;
|
|
158
|
-
this.screenSize = t, t === "large" ? this.state = "default" : t === "medium" ? this.state = "slim" : t === "small" ? this.state = "mobile" : console.warn("
|
|
158
|
+
this.screenSize = t, t === "large" ? this.state = "default" : t === "medium" ? this.state = "slim" : t === "small" ? this.state = "mobile" : console.warn("[@iyulab/modern-app] unknown screen size:", t);
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
161
|
static {
|
package/dist/react.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ import { TimelineItemColor } from './components/TimelineItem.js';
|
|
|
8
8
|
import { InfoFieldFormat, InfoFieldSize, InfoFieldTrend, InfoFieldTone } from './components/InfoField.js';
|
|
9
9
|
export { ScreenObserver } from './internals/ScreenObserver.js';
|
|
10
10
|
export type { ScreenSize, ScreenObserverConfig, ScreenResizeEvent } from './internals/ScreenObserver.js';
|
|
11
|
+
/** `useUnsavedChanges` 가 돌려주는 것. */
|
|
12
|
+
export interface UnsavedChangesHandle {
|
|
13
|
+
/**
|
|
14
|
+
* 지금 값은 저장됐다(또는 버려도 된다) — 다음 렌더 전까지의 이동은 묻지 않는다. 저장 · 삭제 성공 뒤 같은 틱에 떠날 때 부른다:
|
|
15
|
+
* 이동 순간의 판정은 아직 저장 전 렌더의 `dirty` 를 본다. 다음 렌더부터는 다시 그 렌더의 값을 본다.
|
|
16
|
+
*/
|
|
17
|
+
markClean(): void;
|
|
18
|
+
}
|
|
11
19
|
/**
|
|
12
20
|
* React 화면의 «저장 안 한 변경» — 마운트된 동안 판정을 등록한다(`trackUnsavedChanges`). 셸(`app.load()`)이 떠나기 전에
|
|
13
21
|
* «변경을 버릴까요?» 를 묻고, 문서 이탈(새로 고침 · 탭 닫기)에는 브라우저 확인이 걸린다. 값은 매 렌더의 것을 본다.
|
|
@@ -15,11 +23,16 @@ export type { ScreenSize, ScreenObserverConfig, ScreenResizeEvent } from './inte
|
|
|
15
23
|
* ```tsx
|
|
16
24
|
* function OrderForm() {
|
|
17
25
|
* const [draft, setDraft] = useState(initial);
|
|
18
|
-
* useUnsavedChanges(draft !== initial);
|
|
26
|
+
* const unsaved = useUnsavedChanges(draft !== initial);
|
|
27
|
+
* async function save() {
|
|
28
|
+
* const row = await post(draft);
|
|
29
|
+
* unsaved.markClean(); // 저장했다 — 이어지는 이동은 묻지 않는다
|
|
30
|
+
* navigate(`/orders/${row.id}`);
|
|
31
|
+
* }
|
|
19
32
|
* }
|
|
20
33
|
* ```
|
|
21
34
|
*/
|
|
22
|
-
export declare function useUnsavedChanges(dirty: boolean):
|
|
35
|
+
export declare function useUnsavedChanges(dirty: boolean): UnsavedChangesHandle;
|
|
23
36
|
/**
|
|
24
37
|
* React wrapper for `<u-sidebar-layout>`. `config` is an `@property({ type: Object })` —
|
|
25
38
|
* a raw custom element handles that correctly under React 19 (property assignment) but not
|
package/dist/react.js
CHANGED
|
@@ -7,7 +7,13 @@ import { createComponent as a } from "@lit/react";
|
|
|
7
7
|
//#region src/react.ts
|
|
8
8
|
function o(e) {
|
|
9
9
|
let n = i.useRef(e);
|
|
10
|
-
n.current = e
|
|
10
|
+
n.current = e;
|
|
11
|
+
let r = i.useRef(!1);
|
|
12
|
+
return i.useEffect(() => {
|
|
13
|
+
r.current = !1;
|
|
14
|
+
}), i.useEffect(() => t(() => !r.current && n.current), []), i.useMemo(() => ({ markClean: () => {
|
|
15
|
+
r.current = !0;
|
|
16
|
+
} }), []);
|
|
11
17
|
}
|
|
12
18
|
var s = a({
|
|
13
19
|
react: i,
|
package/dist/unsaved.d.ts
CHANGED
|
@@ -32,7 +32,8 @@ export declare function confirmDiscardChanges(options?: DiscardChangesOptions):
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function unsavedChangesGuard(_ctx?: RouteLeaveContext): boolean | Promise<boolean>;
|
|
34
34
|
/**
|
|
35
|
-
* Lit 요소의 «저장 안 한 변경» — 연결된 동안 판정을 등록하고, 떨어지면 해제한다.
|
|
35
|
+
* Lit 요소의 «저장 안 한 변경» — 연결된 동안 판정을 등록하고, 떨어지면 해제한다. 저장 · 삭제 뒤 같은 틱에 떠날 때는
|
|
36
|
+
* `markClean()` — 다음 갱신 전까지의 이동은 묻지 않는다(판정이 폼 상태를 아직 내리지 않았어도).
|
|
36
37
|
* @example
|
|
37
38
|
* ```ts
|
|
38
39
|
* class OrderPage extends LitElement {
|
|
@@ -43,7 +44,12 @@ export declare function unsavedChangesGuard(_ctx?: RouteLeaveContext): boolean |
|
|
|
43
44
|
export declare class UnsavedChangesController implements ReactiveController {
|
|
44
45
|
private readonly isDirty;
|
|
45
46
|
private release?;
|
|
47
|
+
private clean;
|
|
48
|
+
private readonly check;
|
|
46
49
|
constructor(host: ReactiveControllerHost, isDirty: () => boolean);
|
|
50
|
+
/** 지금 상태는 저장됐다 — 호스트의 다음 갱신 전까지의 이동은 묻지 않는다. */
|
|
51
|
+
markClean(): void;
|
|
47
52
|
hostConnected(): void;
|
|
53
|
+
hostUpdated(): void;
|
|
48
54
|
hostDisconnected(): void;
|
|
49
55
|
}
|
package/dist/unsaved.js
CHANGED
|
@@ -12,7 +12,7 @@ function i() {
|
|
|
12
12
|
for (let e of t) try {
|
|
13
13
|
if (e()) return !0;
|
|
14
14
|
} catch (e) {
|
|
15
|
-
return console.error("
|
|
15
|
+
return console.error("[@iyulab/modern-app] the unsaved-changes check threw — treating the screen as unsaved:", e), !0;
|
|
16
16
|
}
|
|
17
17
|
return !1;
|
|
18
18
|
}
|
|
@@ -30,10 +30,16 @@ function o(e) {
|
|
|
30
30
|
}
|
|
31
31
|
var s = class {
|
|
32
32
|
constructor(e, t) {
|
|
33
|
-
this.isDirty = t, e.addController(this);
|
|
33
|
+
this.isDirty = t, this.clean = !1, this.check = () => !this.clean && this.isDirty(), e.addController(this);
|
|
34
|
+
}
|
|
35
|
+
markClean() {
|
|
36
|
+
this.clean = !0;
|
|
34
37
|
}
|
|
35
38
|
hostConnected() {
|
|
36
|
-
this.release ??= r(this.
|
|
39
|
+
this.release ??= r(this.check);
|
|
40
|
+
}
|
|
41
|
+
hostUpdated() {
|
|
42
|
+
this.clean = !1;
|
|
37
43
|
}
|
|
38
44
|
hostDisconnected() {
|
|
39
45
|
this.release?.(), this.release = void 0;
|
package/package.json
CHANGED
|
@@ -245,7 +245,8 @@ Back/Forward included) and the browser asks before the document is left (`before
|
|
|
245
245
|
import { UnsavedChangesController, confirmDiscardChanges } from '@iyulab/modern-app';
|
|
246
246
|
// Lit
|
|
247
247
|
readonly unsaved = new UnsavedChangesController(this, () => this.form.dirty);
|
|
248
|
-
// React: useUnsavedChanges(dirty) from '@iyulab/modern-app/react'
|
|
248
|
+
// React: const unsaved = useUnsavedChanges(dirty) from '@iyulab/modern-app/react'
|
|
249
|
+
// Saved (or deleted) and leaving in the same tick: unsaved.markClean() before navigate() — until the next render
|
|
249
250
|
// Plain: const release = trackUnsavedChanges(() => dirty);
|
|
250
251
|
// A modal drawer's close is not a navigation — ask from its cancellable `hide` event:
|
|
251
252
|
drawer.addEventListener('hide', async (e) => {
|
|
@@ -20,6 +20,24 @@ Swap the `detail` slot's content when the selection changes:
|
|
|
20
20
|
</u-master-detail-layout>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
While nothing is in the `detail` slot the master takes the whole width — a fixed master with an
|
|
24
|
+
empty two-thirds beside it reads as a broken page. To keep the detail place visible with a «pick a
|
|
25
|
+
record» message instead, put it in `slot="detail-placeholder"`; it shows while `detail` is empty
|
|
26
|
+
and steps aside once it is filled (not in overlay mode, where it would cover the master).
|
|
27
|
+
|
|
28
|
+
## A wide list with a side panel
|
|
29
|
+
|
|
30
|
+
`masterSize` fixes the master — the mail / settings shape (narrow list, wide detail). For the
|
|
31
|
+
opposite — a wide table with a record summary beside it — set `detail-size`: the detail takes that
|
|
32
|
+
width and the master fills the rest. With the detail empty the table takes everything.
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<u-master-detail-layout detail-size="24rem">
|
|
36
|
+
<u-rich-table …></u-rich-table>
|
|
37
|
+
<aside slot="detail">…selected record…</aside>
|
|
38
|
+
</u-master-detail-layout>
|
|
39
|
+
```
|
|
40
|
+
|
|
23
41
|
On a narrow container (self width, not viewport — see below) the detail pane switches to a
|
|
24
42
|
full overlay above the master pane, with a small close button that fires `detail-close`.
|
|
25
43
|
Clearing the detail slot yourself (in response to the event) is what actually dismisses it —
|
|
@@ -72,13 +90,15 @@ own margin; that is inherent to printing two columns.
|
|
|
72
90
|
| Name | Description |
|
|
73
91
|
|------|-------------|
|
|
74
92
|
| *(default)* | Master area |
|
|
75
|
-
| `detail` | Detail area — present ⇒ shown, empty ⇒ hidden |
|
|
93
|
+
| `detail` | Detail area — present ⇒ shown, empty ⇒ hidden (the master then takes the whole width) |
|
|
94
|
+
| `detail-placeholder` | Shown in the detail place while `detail` is empty — side by side only |
|
|
76
95
|
|
|
77
96
|
## Properties
|
|
78
97
|
|
|
79
98
|
| Property | Type | Default | Reflect | Description |
|
|
80
99
|
|----------|------|---------|---------|-------------|
|
|
81
100
|
| `masterSize` | `string` | `'22rem'` | | Fixed width of the master pane, a CSS length (`master-size`) |
|
|
101
|
+
| `detailSize` | `string | undefined` | `undefined` | ✓ | Fixed width of the detail pane (`detail-size`) — the master then fills the rest and `masterSize` is not used |
|
|
82
102
|
| `overlayBreakpoint` | `number` | `760` | | Below this self width in px, detail becomes a full overlay (`overlay-breakpoint`) |
|
|
83
103
|
| `locale` | `string` | `''` | | Language tag for the close button's accessible label |
|
|
84
104
|
|