@iyulab/modern-app 0.3.3 → 0.3.5
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 +92 -78
- package/README.md +73 -123
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.124.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.124.0}/helpers/decorateMetadata.js +1 -1
- package/dist/components/SidebarButton.js +2 -2
- package/dist/components/SidebarGroup.js +2 -2
- package/dist/components/SidebarLink.js +2 -2
- package/dist/components/SidebarSection.js +2 -2
- package/dist/internals/StyledElement.js +2 -2
- package/dist/layouts/SidebarLayout.d.ts +2 -0
- package/dist/layouts/SidebarLayout.js +12 -6
- package/dist/layouts/SidebarLayout.styles.js +14 -3
- package/package.json +7 -6
- package/skills/modern-app/SKILL.md +219 -0
- package/skills/modern-app/references/api.md +179 -0
- package/skills/modern-app/references/layout.md +204 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,99 +1,113 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.3.
|
|
3
|
+
## [0.3.5] - 2026-04-07
|
|
4
4
|
|
|
5
|
-
###
|
|
6
|
-
-
|
|
7
|
-
- `SidebarButton`, `SidebarGroup`, `SidebarLink`, `SidebarSection` 4개 컴포넌트
|
|
8
|
-
- `SidebarLayout` 템플릿에서 `u-sidebar-*`로 참조하지만 등록은 `sidebar-*`로 되어 있어 렌더링 안 되던 버그 수정
|
|
5
|
+
### Changed
|
|
6
|
+
- Updated dependencies: `@iyulab/components` ^1.0.1 → ^1.0.3, `@iyulab/router` ^0.7.4 → ^0.9.1
|
|
9
7
|
|
|
10
|
-
## 0.3.
|
|
8
|
+
## [0.3.4] - 2026-04-01
|
|
11
9
|
|
|
12
|
-
###
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- `IconRegistry` import path: `utilities/IconRegistry.js` → `utilities/icons.js`
|
|
17
|
-
- Component import style: `.component.js` → side-effect import (`UIcon.js`, `UButton.js`, etc.)
|
|
10
|
+
### Added
|
|
11
|
+
- Progress bar fade-in/out with CSS transition tied to `route-begin` and `route-done` events
|
|
12
|
+
- Progress bar error state (red) on `route-error` with auto-dismiss
|
|
13
|
+
- `docs/` topic guides and `skills/modern-app/` agent skill package
|
|
18
14
|
|
|
19
|
-
###
|
|
20
|
-
-
|
|
21
|
-
- **Simplified Toast API**: Removed `private notify()` method from `App` class, each notification method now calls `Toast.notice()`, etc. directly
|
|
15
|
+
### Changed
|
|
16
|
+
- Rewrote `README.md` with accurate API references and links to docs and skills
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
- Changed `updated()` → `willUpdate()` in `SidebarLayout` to resolve update-in-update anti-pattern
|
|
25
|
-
- Switched `SidebarLayout` icon library from inline `sidebar-app` to `bootstrap`, removed inline SVG registration code
|
|
18
|
+
## [0.3.3] - 2026-04-01
|
|
26
19
|
|
|
27
|
-
###
|
|
28
|
-
-
|
|
29
|
-
- `i18next` ^25.8.13 → ^25.10.10
|
|
30
|
-
- `vite` ^7.3.1 → ^8.0.3
|
|
31
|
-
- `@types/node` ^25.3.2 → ^25.5.0
|
|
20
|
+
### Fixed
|
|
21
|
+
- Fixed missing `u-` prefix in `customElement` registration for `SidebarButton`, `SidebarGroup`, `SidebarLink`, and `SidebarSection` — components were referenced as `u-sidebar-*` in templates but registered as `sidebar-*`, causing them not to render
|
|
32
22
|
|
|
33
|
-
## 0.3.
|
|
23
|
+
## [0.3.2] - 2026-04-01
|
|
34
24
|
|
|
35
|
-
###
|
|
36
|
-
- **
|
|
25
|
+
### Changed
|
|
26
|
+
- **Breaking:** Upgraded `@iyulab/components` from `^0.4.0` to `^1.0.0`:
|
|
27
|
+
- `Notifier` → `Toast` API migration
|
|
28
|
+
- `AlertType` → `AlertStatus`, `ScreenPosition` → `ToastPosition` type renames
|
|
29
|
+
- `IconRegistry` import path: `utilities/IconRegistry.js` → `utilities/icons.js`
|
|
30
|
+
- Component import style: `.component.js` → side-effect import (e.g. `UIcon.js`, `UButton.js`)
|
|
31
|
+
- Removed `static dependencies` pattern — switched to `@customElement` decorator with side-effect imports
|
|
32
|
+
- Simplified Toast API: each notification method now calls `Toast.notice()` etc. directly; removed private `notify()` from `App` class
|
|
33
|
+
- Updated dependencies: `i18next` ^25.8.13 → ^25.10.10, `vite` ^7.3.1 → ^8.0.3, `@types/node` ^25.3.2 → ^25.5.0
|
|
37
34
|
|
|
38
|
-
###
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
- **Fixed import paths**: Corrected casing for `Theme.js`, `Notifier.js`, etc.
|
|
35
|
+
### Fixed
|
|
36
|
+
- Changed `updated()` → `willUpdate()` in `SidebarLayout` to resolve update-in-update anti-pattern
|
|
37
|
+
- Switched `SidebarLayout` icon library from inline `sidebar-app` to `bootstrap`; removed inline SVG registration
|
|
42
38
|
|
|
43
|
-
|
|
39
|
+
## [0.3.1] - 2026-03-05
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- **Breaking:** Replaced `<u-outlet>` inside `SidebarLayout` with `<slot>`; moved `<u-outlet>` to Light DOM in `App` to allow external CSS during React routing
|
|
43
|
+
- Migrated base class of all components from `BaseElement` to `UElement`
|
|
44
|
+
- `SidebarLinkConfig.type` changed from optional to required
|
|
45
|
+
- Corrected import path casing for `Theme.js`, `Notifier.js`, etc.
|
|
46
|
+
- Updated dependencies: `@iyulab/components` ^0.2.0 → ^0.4.0, `@iyulab/router` ^0.6.2 → ^0.7.2, `i18next` ^25.8.0 → ^25.8.13, `@types/node` ^25.0.9 → ^25.3.2
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
44
49
|
- Removed duplicate `color` property in `SidebarButton.styles.ts`
|
|
45
50
|
- Cleaned up unused callback parameters in `repeat` directive
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- `
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
###
|
|
56
|
-
- **
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- Removed `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
###
|
|
70
|
-
- Improved responsive behavior with screen size observer
|
|
71
|
-
- Added route context tracking and active link highlighting
|
|
72
|
-
- Improved color scheme and typography
|
|
73
|
-
- Enhanced transition animations
|
|
74
|
-
- Cleaner layout spacing and gaps
|
|
75
|
-
- Improved event handling for route changes and screen resizing
|
|
76
|
-
- Enhanced pattern matching for active link detection
|
|
77
|
-
|
|
78
|
-
## 0.2.5 (2026-01-06)
|
|
52
|
+
## [0.3.0] - 2026-01-21
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
- Mobile layout support with new states (`mobile`, `mobile-open`) and dedicated mobile header
|
|
56
|
+
- Route context tracking and active link highlighting
|
|
57
|
+
- `ScreenObserver` class replacing MobX observables for responsive behavior
|
|
58
|
+
- `title` and `iconBasepath` properties in app configuration
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
- **Breaking:** `localization` config option renamed to `i18n`
|
|
62
|
+
- **Breaking:** `layout.menu` renamed to `layout.main`
|
|
63
|
+
- **Breaking:** `logo` type changed to string (icon name)
|
|
64
|
+
- Type definitions moved from `AppTypes.ts` to their respective modules
|
|
65
|
+
- Improved color scheme, typography, transition animations, layout spacing, and event handling
|
|
66
|
+
|
|
67
|
+
### Removed
|
|
68
|
+
- Removed `SidebarLogo` component — logo now rendered via `u-icon`
|
|
69
|
+
- Removed `observables.ts` (replaced by `ScreenObserver`)
|
|
70
|
+
- Removed `AppTypes.ts`
|
|
71
|
+
|
|
72
|
+
## [0.2.5] - 2026-01-06
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
79
75
|
- Updated `@iyulab/components` to v0.1.11
|
|
80
76
|
|
|
81
|
-
## 0.2.4
|
|
82
|
-
|
|
77
|
+
## [0.2.4] - 2025-12-19
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
- Fixed case-sensitive import paths
|
|
81
|
+
|
|
82
|
+
## [0.2.3] - 2025-12-19
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
- `root` option in `AppConfig` to set the application root element
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
- Updated `@iyulab/components` to v0.1.10
|
|
89
|
+
|
|
90
|
+
## [0.2.2] - 2025-11-17
|
|
91
|
+
|
|
92
|
+
### Added
|
|
93
|
+
- `fallback` config option in `AppConfig`
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
- Improved `route-progress` handling in layout component
|
|
97
|
+
|
|
98
|
+
## [0.2.1] - 2025-11-13
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
- `progress` property changed from a value to a function on the `app` instance
|
|
102
|
+
- `locales` option renamed to `localization` in `AppConfig`
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
- update `@iyulab/components` to v0.1.10
|
|
104
|
+
### Fixed
|
|
105
|
+
- Fixed component and sidebar style issues
|
|
87
106
|
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
- improved route-progress handling in layout component
|
|
107
|
+
### Removed
|
|
108
|
+
- Removed `theme` setter property from `app` instance
|
|
91
109
|
|
|
92
|
-
## 0.2.
|
|
93
|
-
- Fixed few component and sidebar styles
|
|
94
|
-
- removed `theme` setter property in `app` instance
|
|
95
|
-
- changed `progress` property to function in `app` instance
|
|
96
|
-
- changed name `locales` option to `localization` option in `AppConfig` type
|
|
110
|
+
## [0.2.0] - 2025-11-12
|
|
97
111
|
|
|
98
|
-
|
|
99
|
-
- Initial
|
|
112
|
+
### Added
|
|
113
|
+
- Initial release
|
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @iyulab/modern-app
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
For complete examples and documentation, visit our demo site: [https://modern-app.iyulab.com](https://modern-app.iyulab.com)
|
|
3
|
+
A client-side SPA framework built on [Lit Element](https://lit.dev/) by iyulab. It bundles routing, a responsive sidebar layout, theme management, toast notifications, and i18n into a single `app` singleton.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -10,167 +8,119 @@ For complete examples and documentation, visit our demo site: [https://modern-ap
|
|
|
10
8
|
npm install @iyulab/modern-app
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
`@iyulab/modern-app` is a **client-side SPA framework** built on Lit Element. It is designed for:
|
|
16
|
-
|
|
17
|
-
✅ **Suitable for:**
|
|
18
|
-
- Single Page Applications (SPA)
|
|
19
|
-
- Admin dashboards and internal tools
|
|
20
|
-
- Progressive Web Apps (PWA)
|
|
21
|
-
- Projects where SEO is not a primary concern
|
|
22
|
-
|
|
23
|
-
❌ **Not suitable for:**
|
|
24
|
-
- Server-Side Rendering (SSR) frameworks (Next.js, Nuxt, SvelteKit, etc.)
|
|
25
|
-
- Static Site Generation (SSG)
|
|
26
|
-
- SEO-critical public-facing pages
|
|
27
|
-
- Projects requiring initial HTML content for search engines
|
|
11
|
+
## When to use it
|
|
28
12
|
|
|
29
|
-
|
|
13
|
+
| ✅ Good fit | ❌ Not a good fit |
|
|
14
|
+
|-------------|-----------------|
|
|
15
|
+
| Single Page Applications (SPA) | SSR frameworks (Next.js, Nuxt, SvelteKit) |
|
|
16
|
+
| Admin dashboards and internal tools | Static Site Generation (SSG) |
|
|
17
|
+
| Progressive Web Apps (PWA) | SEO-critical public-facing pages |
|
|
30
18
|
|
|
31
19
|
## Quick Start
|
|
32
20
|
|
|
33
21
|
```typescript
|
|
34
22
|
import { app } from '@iyulab/modern-app';
|
|
23
|
+
import { html } from 'lit';
|
|
35
24
|
|
|
36
25
|
await app.load({
|
|
37
26
|
basepath: '/',
|
|
38
27
|
layout: {
|
|
39
28
|
type: 'sidebar',
|
|
40
|
-
|
|
29
|
+
logo: '/assets/logo.svg',
|
|
30
|
+
title: 'My App',
|
|
31
|
+
main: [
|
|
32
|
+
{ type: 'link', icon: 'home', label: 'Home', href: '/' },
|
|
33
|
+
{ type: 'link', icon: 'users', label: 'Users', href: '/users' },
|
|
34
|
+
],
|
|
41
35
|
},
|
|
42
36
|
routes: [
|
|
43
|
-
{ index: true,
|
|
44
|
-
{ path: '
|
|
37
|
+
{ index: true, render: () => html`<home-page></home-page>` },
|
|
38
|
+
{ path: 'users', render: () => html`<users-page></users-page>` },
|
|
39
|
+
{ path: 'users/:id', render: (ctx) => html`<user-detail .userId=${ctx.params.id}></user-detail>` },
|
|
45
40
|
],
|
|
41
|
+
fallback: {
|
|
42
|
+
render: (ctx) => html`<error-page .error=${ctx.error}></error-page>`,
|
|
43
|
+
},
|
|
44
|
+
theme: { default: 'system' },
|
|
46
45
|
});
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
##
|
|
48
|
+
## Skills Usage
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
AI agent skills for this package are located in `skills/modern-app/`. Install them with `npx skills`:
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
app
|
|
56
|
-
|
|
57
|
-
// Access router instance
|
|
58
|
-
app.router?.go('/path');
|
|
59
|
-
app.router?.basepath; // Get base path
|
|
60
|
-
app.router?.routes; // Get registered routes
|
|
61
|
-
app.router?.context; // Get current route context
|
|
52
|
+
**From GitHub:**
|
|
53
|
+
```bash
|
|
54
|
+
npx skills add iyulab/node-modern-app
|
|
62
55
|
```
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Get current theme
|
|
68
|
-
app.theme.get(); // Returns: 'system' | 'light' | 'dark' | undefined
|
|
69
|
-
|
|
70
|
-
// Set theme
|
|
71
|
-
app.theme.set('dark'); // 'system' | 'light' | 'dark'
|
|
72
|
-
|
|
73
|
-
// Check initialization status
|
|
74
|
-
app.theme.isInitialized;
|
|
57
|
+
**From local `node_modules`:**
|
|
58
|
+
```bash
|
|
59
|
+
npx skills add ./node_modules/@iyulab/modern-app
|
|
75
60
|
```
|
|
76
61
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
// Display notifications (returns Promise<void>)
|
|
81
|
-
await app.notice('Notice message');
|
|
82
|
-
await app.info('Info message');
|
|
83
|
-
await app.success('Success message');
|
|
84
|
-
await app.warning('Warning message');
|
|
85
|
-
await app.error('Error message');
|
|
86
|
-
|
|
87
|
-
// With options
|
|
88
|
-
await app.success('Saved!', {
|
|
89
|
-
title: 'Success',
|
|
90
|
-
duration: 5000, // milliseconds (default: 3000)
|
|
91
|
-
position: 'top-right' // 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
|
|
92
|
-
});
|
|
93
|
-
```
|
|
62
|
+
## Core API
|
|
94
63
|
|
|
95
|
-
###
|
|
64
|
+
### Navigation
|
|
96
65
|
|
|
97
66
|
```typescript
|
|
98
|
-
//
|
|
99
|
-
app.
|
|
100
|
-
|
|
101
|
-
// Usage with lit-i18n
|
|
102
|
-
import { translate } from 'lit-i18n';
|
|
103
|
-
html`<p>${translate('namespace::key')}</p>`;
|
|
67
|
+
app.navigate('/users/42'); // push a route
|
|
68
|
+
app.router?.go('/users/42'); // via router instance
|
|
69
|
+
app.router?.context; // current RouteContext
|
|
104
70
|
```
|
|
105
71
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
### AppConfig
|
|
72
|
+
### Theme
|
|
109
73
|
|
|
110
74
|
```typescript
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
routes: RouteConfig[]; // Route definitions
|
|
115
|
-
fallback?: FallbackConfig; // Error fallback route
|
|
116
|
-
theme?: ThemeInitOptions; // Theme configuration
|
|
117
|
-
localization?: i18next.InitOptions; // i18next options
|
|
118
|
-
layout: LayoutConfig; // Layout configuration
|
|
119
|
-
}
|
|
75
|
+
app.theme.get(); // 'system' | 'light' | 'dark' | undefined
|
|
76
|
+
app.theme.set('dark');
|
|
77
|
+
app.theme.isInitialized; // boolean
|
|
120
78
|
```
|
|
121
79
|
|
|
122
|
-
###
|
|
80
|
+
### Notifications
|
|
123
81
|
|
|
124
82
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
prefix?: string;
|
|
131
|
-
};
|
|
132
|
-
useBuiltIn?: boolean; // Use built-in styles (default: true)
|
|
133
|
-
}
|
|
83
|
+
await app.success('Saved!', { title: 'Done', duration: 4000, position: 'top-right' });
|
|
84
|
+
await app.error('Something went wrong');
|
|
85
|
+
await app.info('Info message');
|
|
86
|
+
await app.warning('Double-check this');
|
|
87
|
+
await app.notice('Neutral notice');
|
|
134
88
|
```
|
|
135
89
|
|
|
136
|
-
###
|
|
90
|
+
### Localization (i18next)
|
|
137
91
|
|
|
138
92
|
```typescript
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
};
|
|
149
|
-
menu?: MenuItem[]; // Navigation menu items
|
|
150
|
-
footer?: FooterItem[]; // Footer buttons/items
|
|
151
|
-
}
|
|
152
|
-
```
|
|
93
|
+
// Pass i18next plugins and InitOptions
|
|
94
|
+
await app.load({
|
|
95
|
+
// ...
|
|
96
|
+
i18n: {
|
|
97
|
+
plugins: [i18nextHttpBackend],
|
|
98
|
+
lng: 'en',
|
|
99
|
+
backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
|
|
100
|
+
},
|
|
101
|
+
});
|
|
153
102
|
|
|
154
|
-
|
|
103
|
+
// Access i18next
|
|
104
|
+
app.i18n.t('common::greeting');
|
|
105
|
+
app.i18n.changeLanguage('ko');
|
|
155
106
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
path?: string; // Route path (supports :param patterns)
|
|
160
|
-
title?: string; // Document title
|
|
161
|
-
force?: boolean; // Force re-render
|
|
162
|
-
render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
interface RouteContext {
|
|
166
|
-
href: string; // Full URL
|
|
167
|
-
pathname: string; // Path portion
|
|
168
|
-
basepath: string; // Base path
|
|
169
|
-
params: Record<string, string>; // URL parameters
|
|
170
|
-
progress: (value: number) => void; // Progress callback (0-100)
|
|
171
|
-
}
|
|
107
|
+
// Reactive translations in Lit templates
|
|
108
|
+
import { translate } from 'lit-i18n';
|
|
109
|
+
html`<p>${translate('common::greeting')}</p>`;
|
|
172
110
|
```
|
|
173
111
|
|
|
112
|
+
## Documentation
|
|
113
|
+
|
|
114
|
+
| Guide | Description |
|
|
115
|
+
|-------|-------------|
|
|
116
|
+
| [getting-started.md](./docs/getting-started.md) | Bootstrap, architecture, entry point setup |
|
|
117
|
+
| [routing.md](./docs/routing.md) | Route config, URL params, async routes, progress |
|
|
118
|
+
| [layout.md](./docs/layout.md) | Sidebar layout, all menu item types, responsive behaviour |
|
|
119
|
+
| [theme.md](./docs/theme.md) | Theme init, runtime switching, CSS tokens |
|
|
120
|
+
| [notifications.md](./docs/notifications.md) | Toast methods and options |
|
|
121
|
+
| [i18n.md](./docs/i18n.md) | i18next setup, plugins, lit-i18n usage |
|
|
122
|
+
| [configuration.md](./docs/configuration.md) | Full TypeScript interface reference |
|
|
123
|
+
|
|
174
124
|
## License
|
|
175
125
|
|
|
176
126
|
MIT
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region \0@oxc-project+runtime@0.
|
|
1
|
+
//#region \0@oxc-project+runtime@0.124.0/helpers/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,5 +1,5 @@
|
|
|
1
|
-
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarButton.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { SidebarLink as r } from "./SidebarLink.js";
|
|
5
5
|
import { styles as i } from "./SidebarGroup.styles.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
|
|
3
3
|
import { StyledElement as n } from "../internals/StyledElement.js";
|
|
4
4
|
import { styles as r } from "./SidebarLink.styles.js";
|
|
5
5
|
import { html as i } from "lit";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/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,5 +1,5 @@
|
|
|
1
|
-
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
-
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import { __decorateMetadata as e } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
2
|
+
import { __decorate as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/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
|
|
@@ -49,6 +49,8 @@ export declare class SidebarLayout extends StyledElement<SidebarParts> {
|
|
|
49
49
|
private handleRouteProgress;
|
|
50
50
|
/** 라우트 변경 완료 핸들러 */
|
|
51
51
|
private handleRouteDone;
|
|
52
|
+
/** 라우트 에러 핸들러 */
|
|
53
|
+
private handleRouteError;
|
|
52
54
|
/** 화면 크기 변경에 따른 사이드바 상태 업데이트 */
|
|
53
55
|
private handleScreenResize;
|
|
54
56
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { app as e } from "../App.js";
|
|
2
|
-
import { __decorateMetadata as t } from "../_virtual/_@oxc-project_runtime@0.
|
|
3
|
-
import { __decorate as n } from "../_virtual/_@oxc-project_runtime@0.
|
|
2
|
+
import { __decorateMetadata as t } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorateMetadata.js";
|
|
3
|
+
import { __decorate as n } from "../_virtual/_@oxc-project_runtime@0.124.0/helpers/decorate.js";
|
|
4
4
|
import { StyledElement as r } from "../internals/StyledElement.js";
|
|
5
5
|
import "../components/SidebarSection.js";
|
|
6
6
|
import "../components/SidebarLink.js";
|
|
@@ -25,11 +25,17 @@ var m, h = class extends r {
|
|
|
25
25
|
}, this.handleBackdropClick = () => {
|
|
26
26
|
this.state = "slim";
|
|
27
27
|
}, this.handleRouteBegin = (e) => {
|
|
28
|
-
this.progressBarEl.value = 0, this.state === "modal" && (this.state = "slim"), this.state === "mobile-open" && (this.state = "mobile"), this.context = e.context;
|
|
28
|
+
this.progressBarEl.setAttribute("visible", ""), this.progressBarEl.value = 0, this.state === "modal" && (this.state = "slim"), this.state === "mobile-open" && (this.state = "mobile"), this.context = e.context;
|
|
29
29
|
}, this.handleRouteProgress = (e) => {
|
|
30
30
|
this.progressBarEl.value = e.progress;
|
|
31
31
|
}, this.handleRouteDone = (e) => {
|
|
32
|
-
this.progressBarEl.value = 100
|
|
32
|
+
this.progressBarEl.value = 100, setTimeout(() => {
|
|
33
|
+
this.progressBarEl.removeAttribute("visible");
|
|
34
|
+
}, 300);
|
|
35
|
+
}, this.handleRouteError = (e) => {
|
|
36
|
+
this.progressBarEl.setAttribute("error", ""), this.progressBarEl.value = 100, setTimeout(() => {
|
|
37
|
+
this.progressBarEl.removeAttribute("visible"), this.progressBarEl.removeAttribute("error");
|
|
38
|
+
}, 300);
|
|
33
39
|
}, this.handleScreenResize = (e) => {
|
|
34
40
|
let t = e.detail.size;
|
|
35
41
|
t === "large" ? this.state = "default" : t === "medium" ? this.state = "slim" : t === "small" ? this.state = "mobile" : console.warn("Unknown screen size:", t);
|
|
@@ -39,10 +45,10 @@ var m, h = class extends r {
|
|
|
39
45
|
this.styles = [super.styles, i];
|
|
40
46
|
}
|
|
41
47
|
connectedCallback() {
|
|
42
|
-
super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("screen-resize", this.handleScreenResize);
|
|
48
|
+
super.connectedCallback(), window.addEventListener("route-begin", this.handleRouteBegin), window.addEventListener("route-done", this.handleRouteDone), window.addEventListener("route-progress", this.handleRouteProgress), window.addEventListener("route-error", this.handleRouteError), window.addEventListener("screen-resize", this.handleScreenResize);
|
|
43
49
|
}
|
|
44
50
|
disconnectedCallback() {
|
|
45
|
-
window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("screen-resize", this.handleScreenResize), super.disconnectedCallback();
|
|
51
|
+
window.removeEventListener("route-begin", this.handleRouteBegin), window.removeEventListener("route-done", this.handleRouteDone), window.removeEventListener("route-progress", this.handleRouteProgress), window.removeEventListener("route-error", this.handleRouteError), window.removeEventListener("screen-resize", this.handleScreenResize), super.disconnectedCallback();
|
|
46
52
|
}
|
|
47
53
|
willUpdate(e) {
|
|
48
54
|
super.willUpdate(e), e.has("config") && (this.styles = this.config?.styles);
|
|
@@ -151,14 +151,25 @@ var t = e`
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
.main u-progress-bar {
|
|
154
|
+
--progress-bar-height: 4px;
|
|
155
|
+
--progres-bar-track-color: transparent;
|
|
156
|
+
|
|
154
157
|
position: absolute;
|
|
155
158
|
z-index: 100;
|
|
156
159
|
top: 0;
|
|
157
160
|
left: 0;
|
|
158
161
|
right: 0;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
opacity: 0;
|
|
163
|
+
transform: translateY(-4px);
|
|
164
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
165
|
+
pointer-events: none;
|
|
166
|
+
}
|
|
167
|
+
.main u-progress-bar[visible] {
|
|
168
|
+
opacity: 1;
|
|
169
|
+
transform: translateY(0);
|
|
170
|
+
}
|
|
171
|
+
.main u-progress-bar[error] {
|
|
172
|
+
--progress-bar-color: var(--u-red-500);
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
/* Backdrop for modal mode */
|
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 lit-element",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.5",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iyulab",
|
|
7
7
|
"web-framework",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
+
"skills",
|
|
18
19
|
"package.json",
|
|
19
20
|
"README.md",
|
|
20
21
|
"CHANGELOG.md",
|
|
@@ -37,15 +38,15 @@
|
|
|
37
38
|
"build": "vite build"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@iyulab/components": "^1.0.
|
|
41
|
-
"@iyulab/router": "^0.
|
|
42
|
-
"i18next": "^
|
|
41
|
+
"@iyulab/components": "^1.0.3",
|
|
42
|
+
"@iyulab/router": "^0.9.1",
|
|
43
|
+
"i18next": "^26.0.6",
|
|
43
44
|
"lit": "^3.3.2"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^25.
|
|
47
|
+
"@types/node": "^25.6.0",
|
|
47
48
|
"typescript": "^5.9.3",
|
|
48
|
-
"vite": "^8.0.
|
|
49
|
+
"vite": "^8.0.8",
|
|
49
50
|
"vite-plugin-dts": "^4.5.4"
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: modern-app
|
|
3
|
+
description: Client-side SPA framework built on Lit Element. Bootstraps an application with sidebar layout, client-side routing, theme management, toast notifications, and i18n (i18next). Use when working with the @iyulab/modern-app package — setting up a new app, configuring routes, adding navigation menu items, managing theme, showing toasts, or wiring up localization.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: iyulab
|
|
7
|
+
version: "0.3.4"
|
|
8
|
+
compatibility: Designed for Lit Element / TypeScript projects. Requires @iyulab/modern-app.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# @iyulab/modern-app
|
|
12
|
+
|
|
13
|
+
A client-side SPA framework built on Lit Element. Provides a single `app` singleton that wires together routing, layout, theme, notifications, and i18n.
|
|
14
|
+
|
|
15
|
+
> **Suitable for:** SPAs, admin dashboards, internal tools, PWAs.
|
|
16
|
+
> **Not suitable for:** SSR (Next.js, Nuxt, SvelteKit), SSG, SEO-critical pages.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @iyulab/modern-app
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Bootstrap
|
|
27
|
+
|
|
28
|
+
Call `app.load()` once at the entry point. All options are in [`AppConfig`](./references/api.md#appconfig).
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { app } from '@iyulab/modern-app';
|
|
32
|
+
import { html } from 'lit';
|
|
33
|
+
|
|
34
|
+
await app.load({
|
|
35
|
+
basepath: '/',
|
|
36
|
+
layout: {
|
|
37
|
+
type: 'sidebar',
|
|
38
|
+
logo: '/assets/logo.svg',
|
|
39
|
+
title: 'My App',
|
|
40
|
+
main: [
|
|
41
|
+
{ type: 'link', icon: 'home', label: 'Home', href: '/' },
|
|
42
|
+
{ type: 'link', icon: 'users', label: 'Users', href: '/users' },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
routes: [
|
|
46
|
+
{ index: true, render: () => html`<home-page></home-page>` },
|
|
47
|
+
{ path: 'users', render: () => html`<users-page></users-page>` },
|
|
48
|
+
{ path: 'users/:id', render: (ctx) => html`<user-detail .userId=${ctx.params.id}></user-detail>` },
|
|
49
|
+
],
|
|
50
|
+
fallback: {
|
|
51
|
+
render: (ctx) => html`<error-page .error=${ctx.error}></error-page>`,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To tear down the app:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
app.unload();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Navigation
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
app.navigate('/users/42'); // push route
|
|
68
|
+
app.router?.go('/users/42'); // same via router instance
|
|
69
|
+
app.router?.basepath; // base path string
|
|
70
|
+
app.router?.context; // current RouteContext
|
|
71
|
+
app.router?.routes; // registered routes
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Theme
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
app.theme.get(); // 'system' | 'light' | 'dark' | undefined
|
|
80
|
+
app.theme.set('dark'); // 'system' | 'light' | 'dark'
|
|
81
|
+
app.theme.isInitialized; // boolean
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Theme `store` options persist the preference across sessions:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
theme: {
|
|
88
|
+
default: 'system',
|
|
89
|
+
store: { type: 'localStorage', prefix: 'myapp' },
|
|
90
|
+
useBuiltIn: true,
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Notifications (Toast)
|
|
97
|
+
|
|
98
|
+
All methods return `Promise<void>`.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
await app.notice('A general notice');
|
|
102
|
+
await app.info('Loaded successfully');
|
|
103
|
+
await app.success('Saved!', { title: 'Done', duration: 4000, position: 'top-right' });
|
|
104
|
+
await app.warning('Check your input');
|
|
105
|
+
await app.error('Something went wrong');
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`position` values: `'top-right'` | `'top-left'` | `'bottom-right'` | `'bottom-left'`
|
|
109
|
+
`duration` default: `3000` ms
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Localization (i18next)
|
|
114
|
+
|
|
115
|
+
Pass standard i18next `InitOptions` plus an optional `plugins` array.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import i18nextHttpBackend from 'i18next-http-backend';
|
|
119
|
+
|
|
120
|
+
await app.load({
|
|
121
|
+
// ...
|
|
122
|
+
i18n: {
|
|
123
|
+
plugins: [i18nextHttpBackend],
|
|
124
|
+
lng: 'en',
|
|
125
|
+
fallbackLng: 'en',
|
|
126
|
+
backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' },
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Access i18next instance
|
|
131
|
+
app.i18n.t('namespace::key');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Use in Lit templates with `lit-i18n`:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { translate } from 'lit-i18n';
|
|
138
|
+
html`<p>${translate('namespace::greeting')}</p>`;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Sidebar Layout
|
|
144
|
+
|
|
145
|
+
Full configuration reference: [references/layout.md](./references/layout.md)
|
|
146
|
+
|
|
147
|
+
### Menu item types
|
|
148
|
+
|
|
149
|
+
| type | Description |
|
|
150
|
+
|------|-------------|
|
|
151
|
+
| `'link'` | Single navigation link with optional icon |
|
|
152
|
+
| `'group'` | Collapsible group of links |
|
|
153
|
+
| `'section'` | Labelled section grouping links and groups |
|
|
154
|
+
| `'button'` | Action button (non-navigation) |
|
|
155
|
+
| `'html'` | Custom Lit template rendered inline |
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
layout: {
|
|
159
|
+
type: 'sidebar',
|
|
160
|
+
logo: '/logo.svg',
|
|
161
|
+
title: 'App Name',
|
|
162
|
+
main: [
|
|
163
|
+
{
|
|
164
|
+
type: 'section',
|
|
165
|
+
title: 'Management',
|
|
166
|
+
items: [
|
|
167
|
+
{ type: 'link', icon: 'users', label: 'Users', href: '/users' },
|
|
168
|
+
{
|
|
169
|
+
type: 'group', icon: 'settings', label: 'Settings',
|
|
170
|
+
items: [
|
|
171
|
+
{ type: 'link', label: 'Profile', href: '/settings/profile' },
|
|
172
|
+
{ type: 'link', label: 'Security', href: '/settings/security' },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
footer: [
|
|
179
|
+
{ type: 'button', icon: 'logout', label: 'Logout', onClick: () => signOut() },
|
|
180
|
+
],
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Responsive breakpoints
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
layout: {
|
|
188
|
+
type: 'sidebar',
|
|
189
|
+
breakpoints: [768, 1024], // [tablet-min-px, desktop-min-px]
|
|
190
|
+
// ...
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Sidebar states: `'default'` | `'slim'` | `'modal'` | `'mobile'` | `'mobile-open'`
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Routes with progress
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
{
|
|
202
|
+
path: 'dashboard',
|
|
203
|
+
title: 'Dashboard',
|
|
204
|
+
render: async (ctx) => {
|
|
205
|
+
ctx.progress(30);
|
|
206
|
+
const data = await fetchData();
|
|
207
|
+
ctx.progress(100);
|
|
208
|
+
return html`<dashboard-page .data=${data}></dashboard-page>`;
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`RouteContext` fields: `href`, `pathname`, `basepath`, `params`, `progress`
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Full `AppConfig` reference
|
|
218
|
+
|
|
219
|
+
See [references/api.md](./references/api.md) for all TypeScript interfaces.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# API Reference — @iyulab/modern-app
|
|
2
|
+
|
|
3
|
+
## `AppConfig`
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
interface AppConfig {
|
|
7
|
+
/** Root element to render into. Default: document.body */
|
|
8
|
+
root?: Element;
|
|
9
|
+
|
|
10
|
+
/** Base path for all routes. Default: '/' */
|
|
11
|
+
basepath?: string;
|
|
12
|
+
|
|
13
|
+
/** Base URL for icon assets. Default: '/assets/icons/' */
|
|
14
|
+
iconBasepath?: string;
|
|
15
|
+
|
|
16
|
+
/** Route definitions. */
|
|
17
|
+
routes?: RouteConfig[];
|
|
18
|
+
|
|
19
|
+
/** Fallback rendered on 404 or unhandled errors. */
|
|
20
|
+
fallback?: FallbackRouteConfig;
|
|
21
|
+
|
|
22
|
+
/** Layout configuration. Currently only 'sidebar' is supported. */
|
|
23
|
+
layout: LayoutConfig;
|
|
24
|
+
|
|
25
|
+
/** Theme initialization options. */
|
|
26
|
+
theme?: ThemeInitOptions;
|
|
27
|
+
|
|
28
|
+
/** i18next options plus optional plugins array. Omit to skip i18n. */
|
|
29
|
+
i18n?: I18nInitOptions;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## `LayoutConfig`
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
type LayoutConfig = SidebarLayoutConfig & {
|
|
39
|
+
/** Responsive breakpoints [tablet-min, desktop-min] in px. Default: [768, 1024] */
|
|
40
|
+
breakpoints?: [number, number];
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## `ThemeInitOptions`
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
interface ThemeInitOptions {
|
|
50
|
+
/** Initial theme. Default: 'system' */
|
|
51
|
+
default?: 'system' | 'light' | 'dark';
|
|
52
|
+
|
|
53
|
+
/** Log theme decisions to console. */
|
|
54
|
+
debug?: boolean;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Persist the user's preference.
|
|
58
|
+
* Set to `false` to disable persistence.
|
|
59
|
+
* Default: localStorage with no prefix.
|
|
60
|
+
*/
|
|
61
|
+
store?: false | {
|
|
62
|
+
type: 'cookie' | 'localStorage' | 'sessionStorage';
|
|
63
|
+
prefix?: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** Apply built-in CSS custom properties. Default: true */
|
|
67
|
+
useBuiltIn?: boolean;
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## `RouteConfig`
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
interface RouteConfig {
|
|
77
|
+
/** Matches the root path (equivalent to `path: ''`). */
|
|
78
|
+
index?: boolean;
|
|
79
|
+
|
|
80
|
+
/** Path string. Supports `:param` segments. */
|
|
81
|
+
path?: string;
|
|
82
|
+
|
|
83
|
+
/** Sets `document.title` when the route activates. */
|
|
84
|
+
title?: string;
|
|
85
|
+
|
|
86
|
+
/** Force a re-render even if the path did not change. */
|
|
87
|
+
force?: boolean;
|
|
88
|
+
|
|
89
|
+
/** Render function. May be async. */
|
|
90
|
+
render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## `RouteContext`
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
interface RouteContext {
|
|
100
|
+
/** Full URL string. */
|
|
101
|
+
href: string;
|
|
102
|
+
|
|
103
|
+
/** Pathname portion of the URL. */
|
|
104
|
+
pathname: string;
|
|
105
|
+
|
|
106
|
+
/** Configured basepath. */
|
|
107
|
+
basepath: string;
|
|
108
|
+
|
|
109
|
+
/** Named URL parameters extracted from the path pattern. */
|
|
110
|
+
params: Record<string, string>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Report loading progress (0–100).
|
|
114
|
+
* Drives the progress bar shown in the layout header.
|
|
115
|
+
*/
|
|
116
|
+
progress: (value: number) => void;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## `FallbackRouteConfig`
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
interface FallbackRouteConfig {
|
|
126
|
+
render: (context: RouteContext) => RenderResult | Promise<RenderResult>;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The fallback `RouteContext` will include an `error` property when triggered by
|
|
131
|
+
a routing error.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## `I18nInitOptions`
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
type I18nInitOptions = i18next.InitOptions & {
|
|
139
|
+
/** i18next plugins to register via i18next.use() before init. */
|
|
140
|
+
plugins?: (Module | NewableModule<Module> | Newable<Module>)[];
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## `NotificationOptions`
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
interface NotificationOptions {
|
|
150
|
+
title?: string;
|
|
151
|
+
duration?: number; // milliseconds, default 3000
|
|
152
|
+
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## `app` singleton methods
|
|
159
|
+
|
|
160
|
+
| Method | Signature | Description |
|
|
161
|
+
|--------|-----------|-------------|
|
|
162
|
+
| `load` | `(config: AppConfig) => Promise<void>` | Initialize and mount the application |
|
|
163
|
+
| `unload` | `() => void` | Tear down layout, router, and observers |
|
|
164
|
+
| `navigate` | `(path: string) => void` | Push a new route |
|
|
165
|
+
| `notice` | `(msg, opts?) => Promise<void>` | Show a neutral toast |
|
|
166
|
+
| `info` | `(msg, opts?) => Promise<void>` | Show an info toast |
|
|
167
|
+
| `success` | `(msg, opts?) => Promise<void>` | Show a success toast |
|
|
168
|
+
| `warning` | `(msg, opts?) => Promise<void>` | Show a warning toast |
|
|
169
|
+
| `error` | `(msg, opts?) => Promise<void>` | Show an error toast |
|
|
170
|
+
|
|
171
|
+
### `app` singleton properties
|
|
172
|
+
|
|
173
|
+
| Property | Type | Description |
|
|
174
|
+
|----------|------|-------------|
|
|
175
|
+
| `config` | `AppConfig \| undefined` | Current config passed to `load()` |
|
|
176
|
+
| `router` | `Router \| undefined` | Underlying `@iyulab/router` instance |
|
|
177
|
+
| `screen` | `ScreenSize \| undefined` | Current responsive screen size |
|
|
178
|
+
| `theme` | `Theme` (static) | Theme utility (`get`, `set`, `isInitialized`) |
|
|
179
|
+
| `i18n` | `i18next` | Raw i18next instance |
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Sidebar Layout Reference — @iyulab/modern-app
|
|
2
|
+
|
|
3
|
+
## `SidebarLayoutConfig`
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
interface SidebarLayoutConfig {
|
|
7
|
+
type: 'sidebar';
|
|
8
|
+
|
|
9
|
+
/** URL or path to the logo image. */
|
|
10
|
+
logo?: string;
|
|
11
|
+
|
|
12
|
+
/** Application title displayed beside the logo. */
|
|
13
|
+
title?: string;
|
|
14
|
+
|
|
15
|
+
/** Main (top) navigation items. */
|
|
16
|
+
main?: SidebarItem[];
|
|
17
|
+
|
|
18
|
+
/** Footer (bottom-pinned) items. */
|
|
19
|
+
footer?: SidebarItem[];
|
|
20
|
+
|
|
21
|
+
/** Per-part style overrides (CSS custom properties / inline styles). */
|
|
22
|
+
styles?: StyleMap<SidebarParts>;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## `SidebarItem` union
|
|
29
|
+
|
|
30
|
+
`SidebarItem` is the union of all six item types below.
|
|
31
|
+
|
|
32
|
+
### `SidebarLinkConfig` — `type: 'link'`
|
|
33
|
+
|
|
34
|
+
A single navigation link. Highlights automatically when the current URL matches `href` (or `pattern`).
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
interface SidebarLinkConfig {
|
|
38
|
+
type: 'link';
|
|
39
|
+
label: string | DirectiveResult;
|
|
40
|
+
href: string;
|
|
41
|
+
icon?: string;
|
|
42
|
+
/** Override the URL matching pattern. Accepts a string or URLPattern. */
|
|
43
|
+
pattern?: string | URLPattern;
|
|
44
|
+
styles?: StyleMap<'host' | 'base' | 'icon' | 'label'>;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
{ type: 'link', icon: 'dashboard', label: 'Dashboard', href: '/' }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### `SidebarGroupConfig` — `type: 'group'`
|
|
57
|
+
|
|
58
|
+
Collapsible group that contains links.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
interface SidebarGroupConfig {
|
|
62
|
+
type: 'group';
|
|
63
|
+
icon: string;
|
|
64
|
+
label: string | DirectiveResult;
|
|
65
|
+
items: SidebarLinkConfig[];
|
|
66
|
+
/** Start collapsed. Default: true */
|
|
67
|
+
collapsed?: boolean;
|
|
68
|
+
styles?: StyleMap<'host' | 'header' | 'icon' | 'label' | 'caret' | 'items'>;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Example:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
{
|
|
76
|
+
type: 'group',
|
|
77
|
+
icon: 'settings',
|
|
78
|
+
label: 'Settings',
|
|
79
|
+
collapsed: false,
|
|
80
|
+
items: [
|
|
81
|
+
{ type: 'link', label: 'Profile', href: '/settings/profile' },
|
|
82
|
+
{ type: 'link', label: 'Security', href: '/settings/security' },
|
|
83
|
+
],
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### `SidebarSectionConfig` — `type: 'section'`
|
|
90
|
+
|
|
91
|
+
Labelled section that groups links and groups.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface SidebarSectionConfig {
|
|
95
|
+
type: 'section';
|
|
96
|
+
title: string | DirectiveResult;
|
|
97
|
+
subTitle?: string | DirectiveResult;
|
|
98
|
+
items: (SidebarGroupConfig | SidebarLinkConfig)[];
|
|
99
|
+
styles?: StyleMap<'host' | 'header' | 'title' | 'subtitle' | 'items'>;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
{
|
|
107
|
+
type: 'section',
|
|
108
|
+
title: 'Administration',
|
|
109
|
+
items: [
|
|
110
|
+
{ type: 'link', icon: 'users', label: 'Users', href: '/admin/users' },
|
|
111
|
+
{ type: 'link', icon: 'database', label: 'Database', href: '/admin/db' },
|
|
112
|
+
],
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### `SidebarButtonConfig` — `type: 'button'`
|
|
119
|
+
|
|
120
|
+
Action button — triggers a callback instead of navigating.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface SidebarButtonConfig {
|
|
124
|
+
type: 'button';
|
|
125
|
+
icon?: string;
|
|
126
|
+
label: string | DirectiveResult;
|
|
127
|
+
onClick: () => void;
|
|
128
|
+
styles?: StyleMap<string>;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
{ type: 'button', icon: 'logout', label: 'Sign Out', onClick: () => auth.signOut() }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
### `SidebarHtmlConfig` — `type: 'html'`
|
|
141
|
+
|
|
142
|
+
Renders a custom Lit template or raw HTML element. The `render` function
|
|
143
|
+
receives the current sidebar state so you can adapt the content.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface SidebarHtmlConfig {
|
|
147
|
+
type: 'html';
|
|
148
|
+
render: (state: SidebarState) => TemplateResult<1> | HTMLElement | string;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`SidebarState` values: `'default'` | `'slim'` | `'modal'` | `'mobile'` | `'mobile-open'`
|
|
153
|
+
|
|
154
|
+
Example:
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
{
|
|
158
|
+
type: 'html',
|
|
159
|
+
render: (state) => html`
|
|
160
|
+
<div class="user-card" ?hidden=${state === 'slim'}>
|
|
161
|
+
<img src="/avatar.png" />
|
|
162
|
+
<span>John Doe</span>
|
|
163
|
+
</div>
|
|
164
|
+
`,
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Sidebar parts
|
|
171
|
+
|
|
172
|
+
Parts available for `styles` overrides on the root layout:
|
|
173
|
+
|
|
174
|
+
| Part | Element |
|
|
175
|
+
|------|---------|
|
|
176
|
+
| `host` | Outer layout shell |
|
|
177
|
+
| `mobile-header` | Top bar shown on mobile |
|
|
178
|
+
| `sidebar` | Sidebar panel |
|
|
179
|
+
| `sidebar-header` | Logo + title area |
|
|
180
|
+
| `sidebar-main` | Scrollable main nav area |
|
|
181
|
+
| `sidebar-footer` | Pinned footer area |
|
|
182
|
+
| `main` | Main content area |
|
|
183
|
+
| `progress` | Top progress bar |
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Responsive behaviour
|
|
188
|
+
|
|
189
|
+
| Screen width | Sidebar state |
|
|
190
|
+
|--------------|--------------|
|
|
191
|
+
| < breakpoints[0] | `mobile` / `mobile-open` |
|
|
192
|
+
| breakpoints[0] – breakpoints[1] | `slim` (icons only) |
|
|
193
|
+
| > breakpoints[1] | `default` (full labels) |
|
|
194
|
+
|
|
195
|
+
Default breakpoints: `[768, 1024]` px.
|
|
196
|
+
Override per app:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
layout: {
|
|
200
|
+
type: 'sidebar',
|
|
201
|
+
breakpoints: [640, 1280],
|
|
202
|
+
// ...
|
|
203
|
+
}
|
|
204
|
+
```
|