@iyulab/modern-app 0.2.0 → 0.2.2

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 CHANGED
@@ -1,7 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## [Unreleased]
4
- -
3
+ ## 0.2.2 (2025-11-17)
4
+ - added `fallback` config in `AppConfig` type
5
+ - improved route-progress handling in layout component
6
+
7
+ ## 0.2.1 (2025-11-13)
8
+ - Fixed few component and sidebar styles
9
+ - removed `theme` setter property in `app` instance
10
+ - changed `progress` property to function in `app` instance
11
+ - changed name `locales` option to `localization` option in `AppConfig` type
5
12
 
6
13
  ## 0.2.0 (2025-11-12)
7
14
  - Initial library version release
package/dist/app.d.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  import { Router } from '@iyulab/router';
2
- import { ThemeType } from '@iyulab/components/dist/utilities/theme.js';
3
2
  import { AppConfig } from './types/AppConfigs.js';
4
- import { DialogOptions, NotificationOptions } from './types/AppOptions.js';
3
+ import { NotificationOptions } from './types/AppOptions.js';
5
4
  /**
6
5
  * 애플리케이션 전역 상태 및 설정 관리 클래스
7
6
  */
8
7
  declare class App {
9
8
  private static _instance;
10
9
  private _config?;
10
+ private _layout?;
11
11
  private _router?;
12
- private _root?;
13
12
  private constructor();
14
13
  /** 싱글톤 인스턴스 반환 */
15
14
  static get instance(): App;
@@ -17,28 +16,26 @@ declare class App {
17
16
  get config(): AppConfig | undefined;
18
17
  /** 라우터 인스턴스 반환 */
19
18
  get router(): Router | undefined;
20
- /** 스타일 테마 반환 */
21
- get theme(): ThemeType;
22
- /** 스타일 테마 설정 */
23
- set theme(value: ThemeType);
24
- /** 현재 진행바 설정 */
25
- set progress(value: number);
19
+ /** 스타일 테마 관리 유틸리티 객체 반환 */
20
+ get theme(): import('@iyulab/components/dist/utilities/theme.js').Theme;
21
+ /** 다국어 로컬라이저(i18next) 반환 */
22
+ get localizer(): import('i18next').i18n;
26
23
  /** 앱 로드 및 초기화 */
27
24
  load(config: AppConfig): Promise<void>;
28
25
  /** 앱 언로드 */
29
26
  unload(): void;
30
27
  /** 페이지 이동 */
31
28
  navigate(path: string): void;
29
+ /** 공지 메시지 */
30
+ notice(message: string, options?: NotificationOptions): Promise<void>;
31
+ /** 정보 메시지 */
32
+ info(message: string, options?: NotificationOptions): Promise<void>;
33
+ /** 경고 메시지 */
34
+ warning(message: string, options?: NotificationOptions): Promise<void>;
32
35
  /** 성공 메시지 */
33
36
  success(message: string, options?: NotificationOptions): Promise<void>;
34
37
  /** 에러 메시지 */
35
38
  error(message: string, options?: NotificationOptions): Promise<void>;
36
- /** 경고 메시지 */
37
- warning(message: string, options?: NotificationOptions): Promise<void>;
38
- /** 정보 메시지 */
39
- info(message: string, options?: NotificationOptions): Promise<void>;
40
- /** 확인 대화상자 */
41
- confirm(message: string, _options?: DialogOptions): Promise<boolean>;
42
39
  /** 레이아웃 생성 */
43
40
  private createLayout;
44
41
  /** 알림 표시 */
package/dist/app.js CHANGED
@@ -1,22 +1,22 @@
1
- import { runInAction as a } from "mobx";
2
- import u from "i18next";
3
- import { Router as c } from "@iyulab/router";
1
+ import { runInAction as u } from "mobx";
2
+ import n from "i18next";
3
+ import { Router as l } from "@iyulab/router";
4
+ import { theme as s } from "@iyulab/components/dist/utilities/theme.js";
4
5
  import { notifier as d } from "@iyulab/components/dist/utilities/notifier.js";
5
- import { theme as r } from "@iyulab/components/dist/utilities/theme.js";
6
- import { screen as n, progress as h } from "./internals/observers.js";
7
- class i {
6
+ import { screen as r } from "./internals/observables.js";
7
+ class o {
8
8
  // private 생성자로 외부에서 인스턴스 생성 방지
9
9
  constructor() {
10
10
  this.handleWindowResize = (t) => {
11
- const e = window.innerWidth, [o, s] = this._config?.layout.breakpoints || [768, 1024];
12
- a(() => {
13
- e < o ? n.set("small") : e < s ? n.set("medium") : n.set("large");
11
+ const e = window.innerWidth, [i, a] = this._config?.layout.breakpoints || [768, 1024];
12
+ u(() => {
13
+ e < i ? r.set("small") : e < a ? r.set("medium") : r.set("large");
14
14
  });
15
15
  };
16
16
  }
17
17
  /** 싱글톤 인스턴스 반환 */
18
18
  static get instance() {
19
- return i._instance || (i._instance = new i()), i._instance;
19
+ return o._instance || (o._instance = new o()), o._instance;
20
20
  }
21
21
  /** 현재 앱 설정 반환 */
22
22
  get config() {
@@ -26,86 +26,78 @@ class i {
26
26
  get router() {
27
27
  return this._router;
28
28
  }
29
- /** 스타일 테마 반환 */
29
+ /** 스타일 테마 관리 유틸리티 객체 반환 */
30
30
  get theme() {
31
- const t = r.get();
32
- if (!t)
33
- throw new Error("No theme is set, cannot get theme.");
34
- return t;
35
- }
36
- /** 스타일 테마 설정 */
37
- set theme(t) {
38
- r.set(t);
39
- }
40
- /** 현재 진행바 설정 */
41
- set progress(t) {
42
- a(() => {
43
- h.set(t);
44
- });
31
+ return s;
32
+ }
33
+ /** 다국어 로컬라이저(i18next) 반환 */
34
+ get localizer() {
35
+ return n;
45
36
  }
46
37
  /** 앱 로드 및 초기화 */
47
38
  async load(t) {
48
- if (this.unload(), this._config = t, r.init(t.theme), t.locales) {
49
- for (const e of t.locales.plugins || [])
50
- u.use(e);
51
- await u.init(t.locales);
39
+ if (this.unload(), this._config = t, await s.init(t.theme), t.localization) {
40
+ for (const e of t.localization.plugins || [])
41
+ n.use(e);
42
+ await n.init(t.localization);
52
43
  }
53
- window.addEventListener("resize", this.handleWindowResize), this.handleWindowResize(new Event("resize")), this._root = await this.createLayout(t.layout), this._router = new c({
54
- root: this._root,
44
+ window.addEventListener("resize", this.handleWindowResize), this.handleWindowResize(new Event("resize")), this._layout = await this.createLayout(t.layout), this._router = new l({
45
+ root: this._layout,
55
46
  basepath: t.basepath,
56
- routes: t.routes
57
- }), console.log("✅ App loaded successfully");
47
+ routes: t.routes,
48
+ fallback: t.fallback
49
+ });
58
50
  }
59
51
  /** 앱 언로드 */
60
52
  unload() {
61
- window.removeEventListener("resize", this.handleWindowResize), this._root && (this._root.remove(), this._root = void 0), this._router && (this._router.destroy(), this._router = void 0), this._config && (this._config = void 0);
53
+ window.removeEventListener("resize", this.handleWindowResize), this._layout && (this._layout.remove(), this._layout = void 0), this._router && (this._router.destroy(), this._router = void 0), this._config && (this._config = void 0);
62
54
  }
63
55
  /** 페이지 이동 */
64
56
  navigate(t) {
65
57
  this._router?.go(t);
66
58
  }
67
- /** 성공 메시지 */
68
- async success(t, e) {
69
- await this.notify("success", t, e);
59
+ /** 공지 메시지 */
60
+ async notice(t, e) {
61
+ await this.notify("notice", t, e);
70
62
  }
71
- /** 에러 메시지 */
72
- async error(t, e) {
73
- await this.notify("error", t, e);
63
+ /** 정보 메시지 */
64
+ async info(t, e) {
65
+ await this.notify("info", t, e);
74
66
  }
75
67
  /** 경고 메시지 */
76
68
  async warning(t, e) {
77
69
  await this.notify("warning", t, e);
78
70
  }
79
- /** 정보 메시지 */
80
- async info(t, e) {
81
- await this.notify("info", t, e);
71
+ /** 성공 메시지 */
72
+ async success(t, e) {
73
+ await this.notify("success", t, e);
82
74
  }
83
- /** 확인 대화상자 */
84
- async confirm(t, e) {
85
- return window.confirm(t);
75
+ /** 에러 메시지 */
76
+ async error(t, e) {
77
+ await this.notify("error", t, e);
86
78
  }
87
79
  /** 레이아웃 생성 */
88
80
  async createLayout(t) {
89
81
  let e = document.body.querySelector("[data-layout]");
90
82
  if (e && document.body.removeChild(e), t.type === "sidebar") {
91
- const { SidebarLayout: o } = await import("./layouts/SidebarLayout.js"), s = new o();
92
- s.config = t, e = s;
83
+ const { SidebarLayout: i } = await import("./layouts/SidebarLayout.js"), a = new i();
84
+ a.config = t, e = a;
93
85
  } else
94
86
  throw new Error(`Unsupported layout type: ${t.type}`);
95
87
  return e.setAttribute("data-layout", "true"), document.body.appendChild(e), "updateComplete" in e && await e.updateComplete, e;
96
88
  }
97
89
  /** 알림 표시 */
98
- async notify(t, e, o) {
90
+ async notify(t, e, i) {
99
91
  await d.toast({
100
92
  type: t,
101
93
  content: e,
102
- label: o?.title,
103
- duration: o?.duration || 3e3,
104
- position: o?.position || "top-right"
94
+ label: i?.title,
95
+ duration: i?.duration || 3e3,
96
+ position: i?.position || "top-right"
105
97
  });
106
98
  }
107
99
  }
108
- const _ = i.instance;
100
+ const p = o.instance;
109
101
  export {
110
- _ as app
102
+ p as app
111
103
  };
@@ -36,7 +36,5 @@ export declare class SidebarLink extends ExtendedBaseElement<ElementParts> {
36
36
  render(): import('lit-html').TemplateResult<1>;
37
37
  /** 라우트 변경 이벤트 핸들러 */
38
38
  private handleRouteBegin;
39
- /** 라우트 변경 이벤트 핸들러 */
40
- private handleRouteDone;
41
39
  }
42
40
  export {};
@@ -1,37 +1,35 @@
1
- import { html as l } from "lit";
2
- import { property as t } from "lit/decorators.js";
3
- import { Icon as c } from "@iyulab/components/dist/components/Icon/Icon.js";
4
- import { ExtendedBaseElement as d } from "../internals/ExtendedBaseElement.js";
5
- import { styles as h } from "./SidebarLink.styles.js";
6
- var u = Object.defineProperty, n = (r, i, a, f) => {
7
- for (var e = void 0, s = r.length - 1, p; s >= 0; s--)
8
- (p = r[s]) && (e = p(i, a, e) || e);
9
- return e && u(i, a, e), e;
1
+ import { html as c } from "lit";
2
+ import { property as e } from "lit/decorators.js";
3
+ import { Icon as l } from "@iyulab/components/dist/components/Icon/Icon.js";
4
+ import { ExtendedBaseElement as h } from "../internals/ExtendedBaseElement.js";
5
+ import { styles as d } from "./SidebarLink.styles.js";
6
+ var m = Object.defineProperty, n = (o, r, a, u) => {
7
+ for (var t = void 0, s = o.length - 1, p; s >= 0; s--)
8
+ (p = o[s]) && (t = p(r, a, t) || t);
9
+ return t && m(r, a, t), t;
10
10
  };
11
- class o extends d {
11
+ class i extends h {
12
12
  constructor() {
13
- super(...arguments), this.selected = !1, this.compact = !1, this.handleRouteBegin = (i) => {
14
- this.selected = !1;
15
- }, this.handleRouteDone = (i) => {
16
- this.pattern ||= this.href, this.pattern ? (this.pattern = typeof this.pattern == "string" ? new URLPattern(this.pattern, window.location.origin) : this.pattern, this.selected = this.pattern.test(i.routeInfo.path, window.location.origin)) : this.selected = !1;
13
+ super(...arguments), this.selected = !1, this.compact = !1, this.handleRouteBegin = (r) => {
14
+ this.pattern ||= this.href, this.pattern ? (this.pattern = typeof this.pattern == "string" ? new URLPattern(this.pattern, window.location.origin) : this.pattern, this.selected = this.pattern.test(r.context.path, window.location.origin)) : this.selected = !1;
17
15
  };
18
16
  }
19
17
  static {
20
- this.styles = [super.styles, h];
18
+ this.styles = [super.styles, d];
21
19
  }
22
20
  static {
23
21
  this.dependencies = {
24
- "u-icon": c
22
+ "u-icon": l
25
23
  };
26
24
  }
27
25
  connectedCallback() {
28
- super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone);
26
+ super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin);
29
27
  }
30
28
  disconnectedCallback() {
31
- window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), super.disconnectedCallback();
29
+ window.removeEventListener("route-begin", this.handleRouteBegin), super.disconnectedCallback();
32
30
  }
33
31
  render() {
34
- return l`
32
+ return c`
35
33
  <u-link part="base"
36
34
  .href=${this.href || "#"}
37
35
  ?compact=${this.compact}>
@@ -49,23 +47,23 @@ class o extends d {
49
47
  }
50
48
  }
51
49
  n([
52
- t({ type: Boolean, reflect: !0 })
53
- ], o.prototype, "selected");
50
+ e({ type: Boolean, reflect: !0 })
51
+ ], i.prototype, "selected");
54
52
  n([
55
- t({ type: Boolean })
56
- ], o.prototype, "compact");
53
+ e({ type: Boolean })
54
+ ], i.prototype, "compact");
57
55
  n([
58
- t({ type: String })
59
- ], o.prototype, "icon");
56
+ e({ type: String })
57
+ ], i.prototype, "icon");
60
58
  n([
61
- t({ type: String })
62
- ], o.prototype, "label");
59
+ e({ type: String })
60
+ ], i.prototype, "label");
63
61
  n([
64
- t({ type: String })
65
- ], o.prototype, "href");
62
+ e({ type: String })
63
+ ], i.prototype, "href");
66
64
  n([
67
- t({ type: String })
68
- ], o.prototype, "pattern");
65
+ e({ type: String })
66
+ ], i.prototype, "pattern");
69
67
  export {
70
- o as SidebarLink
68
+ i as SidebarLink
71
69
  };
@@ -1,19 +1,19 @@
1
- import { nothing as m, html as n } from "lit";
1
+ import { nothing as m, html as s } from "lit";
2
2
  import { property as e } from "lit/decorators.js";
3
3
  import { Icon as l } from "@iyulab/components/dist/components/icon/Icon.js";
4
- import { ExtendedBaseElement as y } from "../internals/ExtendedBaseElement.js";
5
- import { styles as h } from "./SidebarLogo.styles.js";
6
- var d = Object.defineProperty, i = (r, s, a, g) => {
4
+ import { ExtendedBaseElement as h } from "../internals/ExtendedBaseElement.js";
5
+ import { styles as y } from "./SidebarLogo.styles.js";
6
+ var d = Object.defineProperty, i = (r, n, a, g) => {
7
7
  for (var t = void 0, p = r.length - 1, c; p >= 0; p--)
8
- (c = r[p]) && (t = c(s, a, t) || t);
9
- return t && d(s, a, t), t;
8
+ (c = r[p]) && (t = c(n, a, t) || t);
9
+ return t && d(n, a, t), t;
10
10
  };
11
- class o extends y {
11
+ class o extends h {
12
12
  constructor() {
13
13
  super(...arguments), this.compact = !1, this.type = "icon";
14
14
  }
15
15
  static {
16
- this.styles = [super.styles, h];
16
+ this.styles = [super.styles, y];
17
17
  }
18
18
  static {
19
19
  this.dependencies = {
@@ -21,13 +21,14 @@ class o extends y {
21
21
  };
22
22
  }
23
23
  render() {
24
- return n`
24
+ return s`
25
25
  <div class="container" part="container">
26
- ${this.type === "image" && this.image ? n`
26
+ ${this.type === "image" && this.image ? s`
27
27
  <img part="image"
28
+ ?compact=${this.compact}
28
29
  src=${this.image}
29
30
  alt="App Logo"
30
- />` : this.type === "icon" && this.icon ? n`
31
+ />` : this.type === "icon" && this.icon ? s`
31
32
  <u-icon part="icon"
32
33
  .remote=${!0}
33
34
  .name=${this.icon}
@@ -26,11 +26,15 @@ const t = e`
26
26
  }
27
27
 
28
28
  img {
29
- width: 100%;
29
+ width: auto;
30
30
  height: 1em;
31
31
  object-fit: contain;
32
32
  flex-shrink: 0;
33
33
  }
34
+ img[compact] {
35
+ width: 1em;
36
+ height: auto;
37
+ }
34
38
 
35
39
  u-icon {
36
40
  font-size: 1em;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { app } from './app';
1
2
  export type * from './types/AppConfigs.js';
2
3
  export type * from './types/AppOptions.js';
3
4
  export type * from './types/AppTypes.js';
4
5
  export { app } from './app';
6
+ export default app;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { app as r } from "./app.js";
1
+ import { app as a } from "./app.js";
2
2
  export {
3
- r as app
3
+ a as app,
4
+ a as default
4
5
  };
@@ -0,0 +1,7 @@
1
+ import { IObservableValue } from 'mobx';
2
+ /** 화면 크기 타입 */
3
+ export type ScreenSize = 'small' | 'medium' | 'large';
4
+ /**
5
+ * 현재 화면 크기 상태
6
+ */
7
+ export declare const screen: IObservableValue<ScreenSize>;
@@ -0,0 +1,5 @@
1
+ import { observable as e } from "mobx";
2
+ const r = e.box("large");
3
+ export {
4
+ r as screen
5
+ };
@@ -1,19 +1,19 @@
1
- import { nothing, PropertyValues, TemplateResult } from 'lit';
1
+ import { nothing, PropertyValues } from 'lit';
2
2
  import { BaseElement } from '@iyulab/components/dist/components/BaseElement.js';
3
+ import { IconButton } from '@iyulab/components/dist/components/icon-button/IconButton.js';
4
+ import { ProgressBar } from '@iyulab/components/dist/components/progress-bar/ProgressBar.js';
3
5
  import { ExtendedBaseElement } from '../internals/ExtendedBaseElement';
4
- import { ProgressBar } from '../components/ProgressBar';
5
- import { UnsafeContent } from '../components/UnsafeContent';
6
6
  import { SidebarLogo } from '../components/SidebarLogo';
7
7
  import { SidebarSection } from '../components/SidebarSection';
8
8
  import { SidebarGroup } from '../components/SidebarGroup';
9
9
  import { SidebarLink } from '../components/SidebarLink';
10
10
  import { SidebarButton } from '../components/SidebarButton';
11
11
  import { SidebarLayoutConfig, SidebarState, SidebarParts } from './SidebarLayout.types';
12
- /** 엘리먼트 타입 매핑 */
12
+ /** 엘리먼트 타입 매핑 (for deveveloper experience) */
13
13
  declare global {
14
14
  interface HTMLElementTagNameMap {
15
+ 'u-icon-button': IconButton;
15
16
  'u-progress-bar': ProgressBar;
16
- 'u-unsafe-content': UnsafeContent;
17
17
  'u-sidebar-logo': SidebarLogo;
18
18
  'u-sidebar-section': SidebarSection;
19
19
  'u-sidebar-group': SidebarGroup;
@@ -37,7 +37,7 @@ export declare class SidebarLayout extends ExtendedBaseElement<SidebarParts> {
37
37
  static dependencies: Record<string, typeof BaseElement>;
38
38
  /** 반응형 상태 관리를 위한 MobX 반응 해제 함수들 */
39
39
  private disposers;
40
- progressEl?: ProgressBar;
40
+ progressBarEl: ProgressBar;
41
41
  /** 사이드바 상태 */
42
42
  state: SidebarState;
43
43
  /** 사이드바 레이아웃 설정 */
@@ -45,7 +45,7 @@ export declare class SidebarLayout extends ExtendedBaseElement<SidebarParts> {
45
45
  connectedCallback(): void;
46
46
  disconnectedCallback(): void;
47
47
  protected updated(changedProperties: PropertyValues): void;
48
- render(): TemplateResult<1> | typeof nothing;
48
+ render(): import('lit-html').TemplateResult<1> | typeof nothing;
49
49
  /** 사이드바 아이템 렌더링 */
50
50
  private renderItem;
51
51
  /** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
@@ -56,5 +56,11 @@ export declare class SidebarLayout extends ExtendedBaseElement<SidebarParts> {
56
56
  * 모달 상태에서 사이드바 닫기
57
57
  * 백드롭 클릭 or 라우트 변경 시
58
58
  */
59
- private handleCloseModal;
59
+ private toggleStateIfModalState;
60
+ /** 라우트 변경 시작 핸들러 */
61
+ private handleRouteBegin;
62
+ /** 라우트 변경 진행 핸들러 */
63
+ private handleRouteProgress;
64
+ /** 라우트 변경 완료 핸들러 */
65
+ private handleRouteDone;
60
66
  }
@@ -1,44 +1,47 @@
1
1
  import { nothing as u, html as i } from "lit";
2
- import { query as h, state as f, property as m, customElement as g } from "lit/decorators.js";
3
- import { repeat as l } from "lit/directives/repeat.js";
4
- import { autorun as b } from "mobx";
5
- import { IconButton as $ } from "@iyulab/components/dist/components/icon-button/IconButton.js";
6
- import { screen as p, progress as y } from "../internals/observers.js";
7
- import { ExtendedBaseElement as v } from "../internals/ExtendedBaseElement.js";
8
- import { ProgressBar as _ } from "../components/ProgressBar.js";
9
- import { UnsafeContent as S } from "../components/UnsafeContent.js";
10
- import { SidebarLogo as k } from "../components/SidebarLogo.js";
11
- import { SidebarSection as C } from "../components/SidebarSection.js";
12
- import { SidebarGroup as w } from "../components/SidebarGroup.js";
2
+ import { query as h, state as g, property as b, customElement as f } from "lit/decorators.js";
3
+ import { unsafeHTML as m } from "lit/directives/unsafe-html.js";
4
+ import { repeat as n } from "lit/directives/repeat.js";
5
+ import { autorun as $ } from "mobx";
6
+ import { IconButton as v } from "@iyulab/components/dist/components/icon-button/IconButton.js";
7
+ import { ProgressBar as y } from "@iyulab/components/dist/components/progress-bar/ProgressBar.js";
8
+ import { screen as p } from "../internals/observables.js";
9
+ import { ExtendedBaseElement as S } from "../internals/ExtendedBaseElement.js";
10
+ import { SidebarLogo as _ } from "../components/SidebarLogo.js";
11
+ import { SidebarSection as w } from "../components/SidebarSection.js";
12
+ import { SidebarGroup as k } from "../components/SidebarGroup.js";
13
13
  import { SidebarLink as E } from "../components/SidebarLink.js";
14
- import { SidebarButton as O } from "../components/SidebarButton.js";
15
- import { styles as I } from "./SidebarLayout.styles.js";
16
- var M = Object.defineProperty, P = Object.getOwnPropertyDescriptor, z = Object.getPrototypeOf, B = Reflect.get, n = (e, t, s, a) => {
17
- for (var r = a > 1 ? void 0 : a ? P(t, s) : t, d = e.length - 1, c; d >= 0; d--)
14
+ import { SidebarButton as B } from "../components/SidebarButton.js";
15
+ import { styles as L } from "./SidebarLayout.styles.js";
16
+ var R = Object.defineProperty, I = Object.getOwnPropertyDescriptor, P = Object.getPrototypeOf, C = Reflect.get, l = (e, t, s, a) => {
17
+ for (var r = a > 1 ? void 0 : a ? I(t, s) : t, d = e.length - 1, c; d >= 0; d--)
18
18
  (c = e[d]) && (r = (a ? c(t, s, r) : c(r)) || r);
19
- return a && r && M(t, s, r), r;
20
- }, L = (e, t, s) => B(z(e), s, t);
21
- let o = class extends v {
19
+ return a && r && R(t, s, r), r;
20
+ }, O = (e, t, s) => C(P(e), s, t);
21
+ let o = class extends S {
22
22
  constructor() {
23
23
  super(...arguments), this.disposers = [], this.state = "docked", this.toggleState = () => {
24
24
  const e = p.get();
25
25
  e === "large" ? this.state = this.state === "docked" ? "slim" : "docked" : e === "medium" ? this.state = this.state === "slim" ? "modal" : "slim" : e === "small" ? this.state = this.state === "closed" ? "modal" : "closed" : console.warn("Unknown screen size:", e);
26
- }, this.handleCloseModal = () => {
26
+ }, this.toggleStateIfModalState = () => {
27
27
  const e = p.get();
28
28
  this.state === "modal" && (e === "medium" ? this.state = "slim" : e === "small" && (this.state = "closed"));
29
+ }, this.handleRouteBegin = (e) => {
30
+ this.progressBarEl.value = 0, this.toggleStateIfModalState();
31
+ }, this.handleRouteProgress = (e) => {
32
+ this.progressBarEl.value = e.progress;
33
+ }, this.handleRouteDone = (e) => {
34
+ this.progressBarEl.value = 100;
29
35
  };
30
36
  }
31
37
  connectedCallback() {
32
- super.connectedCallback(), this.disposers.push(b(() => {
38
+ super.connectedCallback(), this.disposers.push($(() => {
33
39
  const e = p.get();
34
40
  this.updateState(e);
35
- })), this.disposers.push(b(() => {
36
- const e = y.get();
37
- this.progressEl && (this.progressEl.value = e);
38
- })), window.addEventListener("route-begin", this.handleCloseModal);
41
+ })), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress);
39
42
  }
40
43
  disconnectedCallback() {
41
- this.disposers.forEach((e) => e()), window.removeEventListener("route-begin", this.handleCloseModal), super.disconnectedCallback();
44
+ this.disposers.forEach((e) => e()), window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), super.disconnectedCallback();
42
45
  }
43
46
  updated(e) {
44
47
  super.updated(e), e.has("config") && (this.styles = this.config?.styles);
@@ -51,12 +54,12 @@ let o = class extends v {
51
54
  <div class="sidebar-header" part="sidebar-header">
52
55
  <u-sidebar-logo
53
56
  .compact="${this.state === "slim"}"
54
- .type=${this.config.logo.type}
55
- .image="${this.config.logo.image}"
56
- .icon="${this.config.logo.icon}"
57
- .label="${this.config.logo.label}"
58
- .styles="${this.config.logo.styles}"
59
- @click="${this.config.logo.onClick}"
57
+ .type=${this.config.logo?.type || "icon"}
58
+ .image="${this.config.logo?.image}"
59
+ .icon="${this.config.logo?.icon}"
60
+ .label="${this.config.logo?.label}"
61
+ .styles="${this.config.logo?.styles}"
62
+ @click="${this.config.logo?.onClick}"
60
63
  ></u-sidebar-logo>
61
64
  <u-icon-button class="sidebar-toggler" part="sidebar-toggler"
62
65
  name=${this.state === "closed" ? "chevron-right" : "layout-sidebar"}
@@ -66,7 +69,7 @@ let o = class extends v {
66
69
 
67
70
  <!-- Sidebar Navigation Menu -->
68
71
  <nav class="sidebar-menu" part="sidebar-menu">
69
- ${l(
72
+ ${n(
70
73
  this.config.menu ?? [],
71
74
  (e, t) => t,
72
75
  (e, t) => this.renderItem(e)
@@ -74,8 +77,9 @@ let o = class extends v {
74
77
  </nav>
75
78
 
76
79
  <!-- Sidebar Footer -->
77
- <div class="sidebar-footer" part="sidebar-footer">
78
- ${l(
80
+ <div class="sidebar-footer" part="sidebar-footer"
81
+ ?hidden="${!this.config.footer || this.config.footer.length === 0}">
82
+ ${n(
79
83
  this.config.footer ?? [],
80
84
  (e, t) => t,
81
85
  (e, t) => this.renderItem(e)
@@ -93,26 +97,20 @@ let o = class extends v {
93
97
  <!-- Backdrop for modal state -->
94
98
  <div class="backdrop"
95
99
  ?hidden="${this.state !== "modal"}"
96
- @click="${this.handleCloseModal}"
100
+ @click="${this.toggleStateIfModalState}"
97
101
  ></div>
98
102
  ` : u;
99
103
  }
100
104
  /** 사이드바 아이템 렌더링 */
101
105
  renderItem(e) {
102
- return e ? e.type === "group" ? i`
103
- <u-sidebar-group
106
+ return e ? e.type === "content" ? this.state === "slim" ? u : m(e.content) : e.type === "button" ? i`
107
+ <u-sidebar-button
104
108
  ?compact=${this.state === "slim"}
105
- ?collapsed="${e.collapsed ?? !1}"
106
109
  .icon="${e.icon}"
107
110
  .label="${e.label}"
108
- .items="${e.items}"
109
- .styles="${e.styles}">
110
- ${l(
111
- e.items,
112
- (t, s) => s,
113
- (t, s) => this.renderItem(t)
114
- )}
115
- </u-sidebar-group>
111
+ .styles="${e.styles}"
112
+ @click="${e.onClick}"
113
+ ></u-sidebar-button>
116
114
  ` : e.type === "section" ? i`
117
115
  <u-sidebar-section
118
116
  ?compact=${this.state === "slim"}
@@ -120,25 +118,26 @@ let o = class extends v {
120
118
  .subTitle="${e.subTitle}"
121
119
  .items="${e.items}"
122
120
  .styles="${e.styles}">
123
- ${l(
121
+ ${n(
124
122
  e.items,
125
123
  (t, s) => s,
126
124
  (t, s) => this.renderItem(t)
127
125
  )}
128
126
  </u-sidebar-section>
129
- ` : e.type === "button" ? i`
130
- <u-sidebar-button
127
+ ` : e.type === "group" ? i`
128
+ <u-sidebar-group
131
129
  ?compact=${this.state === "slim"}
130
+ ?collapsed="${e.collapsed ?? !1}"
132
131
  .icon="${e.icon}"
133
132
  .label="${e.label}"
134
- .styles="${e.styles}"
135
- @click="${e.onClick}"
136
- ></u-sidebar-button>
137
- ` : e.type === "content" ? i`
138
- <u-sidebar-content
139
- ?compact=${this.state === "slim"}
140
- .content="${e.content}"
141
- ></u-sidebar-content>
133
+ .items="${e.items}"
134
+ .styles="${e.styles}">
135
+ ${n(
136
+ e.items,
137
+ (t, s) => s,
138
+ (t, s) => this.renderItem(t)
139
+ )}
140
+ </u-sidebar-group>
142
141
  ` : i`
143
142
  <u-sidebar-link
144
143
  ?compact=${this.state === "slim"}
@@ -155,28 +154,27 @@ let o = class extends v {
155
154
  e === "large" ? this.state = "docked" : e === "medium" ? this.state = "slim" : e === "small" ? this.state = "closed" : console.warn("Unknown screen size:", e);
156
155
  }
157
156
  };
158
- o.styles = [L(o, o, "styles"), I];
157
+ o.styles = [O(o, o, "styles"), L];
159
158
  o.dependencies = {
160
- "u-icon-button": $,
161
- "u-progress-bar": _,
162
- "u-unsafe-content": S,
163
- "u-sidebar-logo": k,
164
- "u-sidebar-section": C,
165
- "u-sidebar-group": w,
159
+ "u-icon-button": v,
160
+ "u-progress-bar": y,
161
+ "u-sidebar-logo": _,
162
+ "u-sidebar-section": w,
163
+ "u-sidebar-group": k,
166
164
  "u-sidebar-link": E,
167
- "u-sidebar-button": O
165
+ "u-sidebar-button": B
168
166
  };
169
- n([
167
+ l([
170
168
  h("u-progress-bar")
171
- ], o.prototype, "progressEl", 2);
172
- n([
173
- f()
169
+ ], o.prototype, "progressBarEl", 2);
170
+ l([
171
+ g()
174
172
  ], o.prototype, "state", 2);
175
- n([
176
- m({ type: Object })
173
+ l([
174
+ b({ type: Object })
177
175
  ], o.prototype, "config", 2);
178
- o = n([
179
- g("app-sidebar-layout")
176
+ o = l([
177
+ f("app-sidebar-layout")
180
178
  ], o);
181
179
  export {
182
180
  o as SidebarLayout
@@ -100,6 +100,25 @@ const r = o`
100
100
  border-top: 1px solid var(--u-border-color-weak);
101
101
  }
102
102
 
103
+ /* Main Content */
104
+ .main {
105
+ position: relative;
106
+ flex: 1;
107
+ display: block;
108
+ overflow: hidden;
109
+ background: var(--u-bg-color);
110
+ }
111
+
112
+ .main u-progress-bar {
113
+ position: absolute;
114
+ z-index: 100;
115
+ top: 0;
116
+ left: 0;
117
+ height: 4px;
118
+ border-radius: 0;
119
+ background-color: transparent;
120
+ }
121
+
103
122
  /* Backdrop for modal mode */
104
123
  .backdrop {
105
124
  content: '';
@@ -111,15 +130,6 @@ const r = o`
111
130
  bottom: 0;
112
131
  background: var(--u-overlay-bg-color);
113
132
  }
114
-
115
- /* Main Content */
116
- .main {
117
- position: relative;
118
- flex: 1;
119
- display: block;
120
- overflow: hidden;
121
- background: var(--u-bg-color);
122
- }
123
133
  `;
124
134
  export {
125
135
  r as styles
@@ -4,18 +4,22 @@ import { SidebarLinkConfig } from '../components/SidebarLink';
4
4
  import { SidebarSectionConfig } from '../components/SidebarSection';
5
5
  import { SidebarGroupConfig } from '../components/SidebarGroup';
6
6
  import { SidebarButtonConfig } from '../components/SidebarButton';
7
- import { UnsafeContentConfig } from '../components/UnsafeContent';
8
7
  /** 사이드바 레이아웃 컴포넌트의 요소(part) 타입 */
9
8
  export type SidebarParts = 'host' | 'sidebar' | 'sidebar-toggler' | 'sidebar-header' | 'sidebar-menu' | 'sidebar-footer' | 'main' | 'progress';
10
9
  /** 사이드바 상태 타입 */
11
10
  export type SidebarState = 'docked' | 'modal' | 'slim' | 'closed';
11
+ /** 사이드바 컨텐츠 커스텀 HTML 타입 */
12
+ export interface SidebarContentConfig {
13
+ type: 'content';
14
+ content: string;
15
+ }
12
16
  /** union: section | group | link | button */
13
- export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | UnsafeContentConfig);
17
+ export type SidebarItem = (SidebarLinkConfig | SidebarSectionConfig | SidebarGroupConfig | SidebarButtonConfig | SidebarContentConfig);
14
18
  /** 사이드바 전체 설정 (루트) */
15
19
  export interface SidebarLayoutConfig {
16
20
  type: 'sidebar';
17
21
  /** 최상단 앱 로고 */
18
- logo: SidebarLogoConfig;
22
+ logo?: SidebarLogoConfig;
19
23
  /** 상단/메인 메뉴 항목들 */
20
24
  menu?: SidebarItem[];
21
25
  /** 하단(footer)에 고정해서 렌더할 항목들 */
@@ -1,14 +1,14 @@
1
1
  import { InitOptions, Module, Newable, NewableModule } from 'i18next';
2
- import { RouteConfig } from '@iyulab/router';
2
+ import { RouteConfig, FallbackRouteConfig } from '@iyulab/router';
3
3
  import { ThemeInitOptions } from '@iyulab/components/dist/utilities/theme.js';
4
4
  import { SidebarLayoutConfig } from '../layouts/SidebarLayout.types';
5
5
  /**
6
6
  * i18next의 초기화 옵션과 플러그인 배열을 포함한 다국어 설정
7
7
  */
8
- export type LocaleInitOptions = InitOptions & {
8
+ export type LocalizationInitOptions = InitOptions & {
9
9
  /**
10
10
  * i18next 플러그인 배열
11
- * @description i18next.use() 메서드에 전달될 플러그인들의 배열입니다.
11
+ * @description i18next.use() 메서드에 전달되는 플러그인들의 배열입니다.
12
12
  */
13
13
  plugins?: (Module | NewableModule<Module> | Newable<Module>)[];
14
14
  };
@@ -35,7 +35,11 @@ export interface AppConfig {
35
35
  /**
36
36
  * 클라이언트 사이드 라우팅을 위한 설정 배열
37
37
  */
38
- routes: RouteConfig[];
38
+ routes?: RouteConfig[];
39
+ /**
40
+ * 라우팅 실패 시 대체 컨텐츠 설정
41
+ */
42
+ fallback?: FallbackRouteConfig;
39
43
  /**
40
44
  * 애플리케이션의 스타일 테마 설정
41
45
  */
@@ -45,7 +49,7 @@ export interface AppConfig {
45
49
  * @description i18next의 InitOptions와 플러그인 배열을 포함합니다. i18next를 사용하지 않는 경우 이 설정은 생략할 수 있습니다.
46
50
  * @see 설정에 대한 자세한 내용은 {@link https://www.i18next.com/overview/configuration-options} 참조하십시오.
47
51
  */
48
- locales?: LocaleInitOptions;
52
+ localization?: LocalizationInitOptions;
49
53
  /**
50
54
  * 애플리케이션을 구성하기 위한 레이아웃 설정
51
55
  * @description 현재는 사이드바 레이아웃만 지원합니다.
@@ -1,5 +1,3 @@
1
- /** 화면 크기 타입 */
2
- export type ScreenSize = 'small' | 'medium' | 'large';
3
1
  /** 스타일 값 타입 */
4
2
  export type StyleValue = Record<string, string> | Partial<CSSStyleDeclaration>;
5
3
  /** 스타일 맵 타입 */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@iyulab/modern-app",
3
3
  "description": "web-framework by iyulab based on react, lit-element",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -29,7 +29,7 @@
29
29
  "import": "./dist/index.js"
30
30
  },
31
31
  "./dist/*": {
32
- "types": "./dist/*",
32
+ "types": "./dist/*.d.ts",
33
33
  "import": "./dist/*"
34
34
  }
35
35
  },
@@ -38,8 +38,8 @@
38
38
  "build": "vite build"
39
39
  },
40
40
  "dependencies": {
41
- "@iyulab/components": "^0.1.0",
42
- "@iyulab/router": "^0.5.0",
41
+ "@iyulab/components": "^0.1.3",
42
+ "@iyulab/router": "^0.5.2",
43
43
  "lit": "^3.3.1",
44
44
  "mobx": "^6.15.0",
45
45
  "i18next": "^25.6.2"
@@ -1,22 +0,0 @@
1
- import { BaseElement } from '@iyulab/components/dist/components/BaseElement.js';
2
- /**
3
- * ProgressBar 컴포넌트는 진행 상태를 시각적으로 표시합니다.
4
- * 로딩 상태나 작업 진행률을 표시하는데 사용됩니다.
5
- */
6
- export declare class ProgressBar extends BaseElement {
7
- static styles: import('lit').CSSResultGroup[];
8
- static dependencies: Record<string, typeof BaseElement>;
9
- /** 불확정 상태 (로딩 애니메이션 표시) */
10
- indeterminate: boolean;
11
- /** 최소값 (기본값: 0) */
12
- minValue: number;
13
- /** 최대값 (기본값: 100) */
14
- maxValue: number;
15
- /** 현재값 */
16
- value: number;
17
- connectedCallback(): void;
18
- protected updated(changedProperties: Map<string, unknown>): void;
19
- render(): import('lit-html').TemplateResult<1>;
20
- /** 진행 상태 업데이트 */
21
- private updateProgress;
22
- }
@@ -1,54 +0,0 @@
1
- import { html as h } from "lit";
2
- import { property as i } from "lit/decorators.js";
3
- import { BaseElement as n } from "@iyulab/components/dist/components/BaseElement.js";
4
- import { styles as p } from "./ProgressBar.styles.js";
5
- var v = Object.defineProperty, a = (u, t, s, o) => {
6
- for (var e = void 0, l = u.length - 1, m; l >= 0; l--)
7
- (m = u[l]) && (e = m(t, s, e) || e);
8
- return e && v(t, s, e), e;
9
- };
10
- class r extends n {
11
- constructor() {
12
- super(...arguments), this.indeterminate = !1, this.minValue = 0, this.maxValue = 100, this.value = 0;
13
- }
14
- static {
15
- this.styles = [super.styles, p];
16
- }
17
- static {
18
- this.dependencies = {};
19
- }
20
- connectedCallback() {
21
- super.connectedCallback(), this.setAttribute("role", "progressbar");
22
- }
23
- updated(t) {
24
- super.updated(t), t.has("value") && (this.updateProgress(t.get("value"), this.value), this.setAttribute("aria-valuenow", this.value.toString())), t.has("minValue") && (this.updateProgress(this.value, this.value), this.setAttribute("aria-valuemax", this.maxValue.toString())), t.has("maxValue") && (this.updateProgress(this.value, this.value), this.setAttribute("aria-valuemin", this.minValue.toString())), t.has("indeterminate") && (this.updateProgress(this.value, this.value), this.indeterminate ? (this.removeAttribute("aria-valuenow"), this.setAttribute("aria-busy", "true")) : this.removeAttribute("aria-busy"));
25
- }
26
- render() {
27
- return h`<slot></slot>`;
28
- }
29
- /** 진행 상태 업데이트 */
30
- updateProgress(t, s) {
31
- if (this.indeterminate) return;
32
- const e = (Math.min(Math.max(s, this.minValue), this.maxValue) - this.minValue) / (this.maxValue - this.minValue);
33
- t < s ? (this.style.transform = `scaleX(${e})`, s >= this.maxValue && setTimeout(() => {
34
- this.style.opacity = "0";
35
- }, 300)) : (this.style.opacity = "0", this.style.transform = "scaleX(0)", setTimeout(() => {
36
- this.style.opacity = "1", this.style.transform = `scaleX(${e})`;
37
- }, 300));
38
- }
39
- }
40
- a([
41
- i({ type: Boolean, reflect: !0 })
42
- ], r.prototype, "indeterminate");
43
- a([
44
- i({ type: Number })
45
- ], r.prototype, "minValue");
46
- a([
47
- i({ type: Number })
48
- ], r.prototype, "maxValue");
49
- a([
50
- i({ type: Number })
51
- ], r.prototype, "value");
52
- export {
53
- r as ProgressBar
54
- };
@@ -1 +0,0 @@
1
- export declare const styles: import('lit').CSSResult;
@@ -1,31 +0,0 @@
1
- import { css as t } from "lit";
2
- const a = t`
3
- :host {
4
- display: block;
5
- position: absolute;
6
- z-index: 9999;
7
- top: 0;
8
- left: 0;
9
- right: 0;
10
- height: 4px;
11
- background-color: var(--u-blue-600);
12
- transform-origin: left;
13
- transition: all 0.3s ease-out;
14
- box-shadow: 0 2px 4px var(--u-shadow-weak);
15
- }
16
- :host([indeterminate]) {
17
- animation: indeterminate 1.5s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
18
- }
19
-
20
- @keyframes indeterminate {
21
- 0% {
22
- transform: translateX(-100%);
23
- }
24
- 100% {
25
- transform: translateX(100%);
26
- }
27
- }
28
- `;
29
- export {
30
- a as styles
31
- };
@@ -1,16 +0,0 @@
1
- import { BaseElement } from '@iyulab/components/dist/components/BaseElement.js';
2
- /** Raw HTML content */
3
- export interface UnsafeContentConfig {
4
- type: 'content';
5
- content: string;
6
- }
7
- /**
8
- * 일반 HTML 콘텐츠를 표시하는 사이드바 컴포넌트
9
- */
10
- export declare class UnsafeContent extends BaseElement {
11
- static styles: import('lit').CSSResultGroup[];
12
- static dependencies: Record<string, typeof BaseElement>;
13
- /** HTML 콘텐츠 */
14
- content?: string;
15
- render(): import('lit-html/directive.js').DirectiveResult<typeof import('lit-html/directives/unsafe-html.js').UnsafeHTMLDirective>;
16
- }
@@ -1,26 +0,0 @@
1
- import { property as i } from "lit/decorators.js";
2
- import { unsafeHTML as p } from "lit/directives/unsafe-html.js";
3
- import { BaseElement as f } from "@iyulab/components/dist/components/BaseElement.js";
4
- import { styles as a } from "./UnsafeContent.styles.js";
5
- var m = Object.defineProperty, l = (t, s, n, c) => {
6
- for (var e = void 0, r = t.length - 1, o; r >= 0; r--)
7
- (o = t[r]) && (e = o(s, n, e) || e);
8
- return e && m(s, n, e), e;
9
- };
10
- class d extends f {
11
- static {
12
- this.styles = [super.styles, a];
13
- }
14
- static {
15
- this.dependencies = {};
16
- }
17
- render() {
18
- return p(this.content);
19
- }
20
- }
21
- l([
22
- i({ type: String })
23
- ], d.prototype, "content");
24
- export {
25
- d as UnsafeContent
26
- };
@@ -1 +0,0 @@
1
- export declare const styles: import('lit').CSSResult;
@@ -1,9 +0,0 @@
1
- import { css as s } from "lit";
2
- const o = s`
3
- :host {
4
- display: contents;
5
- }
6
- `;
7
- export {
8
- o as styles
9
- };
@@ -1,10 +0,0 @@
1
- import { IObservableValue } from 'mobx';
2
- import { ScreenSize } from '../types/AppTypes';
3
- /**
4
- * 현재 화면 크기 상태
5
- */
6
- export declare const screen: IObservableValue<ScreenSize>;
7
- /**
8
- * 로딩 진행 상태 (0 - 100)
9
- */
10
- export declare const progress: IObservableValue<number>;
@@ -1,6 +0,0 @@
1
- import { observable as o } from "mobx";
2
- const e = o.box("large"), s = o.box(0);
3
- export {
4
- s as progress,
5
- e as screen
6
- };