@iyulab/modern-app 0.18.2 → 0.18.4

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,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.18.4] - 2026-08-23
4
+
5
+ ### Fixed
6
+
7
+ - **`App.load()` now initializes the theme before checking the `auth` gate.** When `auth.me()`
8
+ resolves to `null`, `load()` calls `auth.renderLogin()` and returns before the shell is built —
9
+ but `Theme.init()` used to run only after the auth check passed, so any `renderLogin` screen
10
+ that relies on `--u-*` design tokens (background, spacing, accent colors) rendered with no
11
+ stylesheet in the document at all. `Theme.init()` and `iconBasepath` setup are pure display
12
+ configuration independent of auth state, so they now run unconditionally before the gate —
13
+ every render path, including the pre-auth login screen, has tokens available.
14
+
15
+ ## [0.18.3] - 2026-08-22
16
+
17
+ ### Fixed
18
+
19
+ - **`SidebarButtonConfig.id`'s docs no longer claim external light-DOM `u-popover` anchoring
20
+ works.** It doesn't: the rendered `<u-sidebar-button>` lives inside `<u-sidebar-layout>`'s own
21
+ shadow root, and `for="#id"` lookups never cross a shadow boundary — a popover placed outside
22
+ the layout can never find the anchor, no matter how the `id` is passed. Documents the recipe
23
+ that does work instead: assemble the trigger and its `u-popover` together inside a single
24
+ `type: 'html'` item, with `placement` chosen from the sidebar's own state (a sideways flyout
25
+ has no room once the sidebar widens on mobile; a vertical one always does). Not a code change —
26
+ `id` still passes through exactly as before.
27
+
3
28
  ## [0.18.2] - 2026-08-19
4
29
 
5
30
  ### Fixed
package/dist/App.js CHANGED
@@ -28,7 +28,7 @@ var o = class o {
28
28
  return t;
29
29
  }
30
30
  async load(a) {
31
- if (this.unload(), this._config = a, a.auth) {
31
+ if (this.unload(), this._config = a, await i.init(a.theme), a.iconBasepath && r(a.iconBasepath), a.auth) {
32
32
  let e = await a.auth.me();
33
33
  if (e == null) {
34
34
  let e = a.root || document.body, t = a.auth.renderLogin({
@@ -42,7 +42,7 @@ var o = class o {
42
42
  }
43
43
  this._user = e, await a.auth.onAuthenticated?.(e);
44
44
  }
45
- if (await i.init(a.theme), a.iconBasepath && r(a.iconBasepath), a.i18n) {
45
+ if (a.i18n) {
46
46
  for (let e of a.i18n.plugins || []) t.use(e);
47
47
  await t.init(a.i18n);
48
48
  }
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.18.2",
4
+ "version": "0.18.4",
5
5
  "keywords": [
6
6
  "iyulab",
7
7
  "web-framework",
@@ -144,6 +144,7 @@ Action button — triggers a callback instead of navigating.
144
144
  ```typescript
145
145
  interface SidebarButtonConfig {
146
146
  type: 'button';
147
+ id?: string;
147
148
  icon?: string;
148
149
  lib?: string;
149
150
  label: string | DirectiveResult;
@@ -158,6 +159,12 @@ Example:
158
159
  { type: 'button', icon: 'logout', label: 'Sign Out', onClick: () => auth.signOut() }
159
160
  ```
160
161
 
162
+ `id` is passed straight through to the rendered `<u-sidebar-button>` host. **It does not enable
163
+ anchoring a `u-popover` you place outside the layout** — the button lives inside
164
+ `<u-sidebar-layout>`'s own shadow root, and `querySelector`/`for="#id"` never crosses a shadow
165
+ boundary. If you need a popover anchored to a sidebar item, use `type: 'html'` and assemble both
166
+ inside the same template — see [popup-style submenus](#popup-style-submenus-u-popover) below.
167
+
161
168
  ---
162
169
 
163
170
  ### `SidebarHtmlConfig` — `type: 'html'`
@@ -190,6 +197,38 @@ Example:
190
197
 
191
198
  ---
192
199
 
200
+ ### Popup-style submenus (`u-popover`)
201
+
202
+ For a submenu that flies out from a sidebar item (rather than expanding in place like
203
+ `SidebarGroupConfig`), assemble a `u-popover` and its trigger together inside a single
204
+ `type: 'html'` item — both then live in the sidebar layout's own shadow root, which is required
205
+ for `for="#id"` anchoring to resolve (see the `id` note above).
206
+
207
+ ```typescript
208
+ {
209
+ type: 'html',
210
+ render: (state) => html`
211
+ <u-sidebar-button id="more-trigger" icon="three-dots" label="More"></u-sidebar-button>
212
+ <u-popover for="#more-trigger" placement=${state.startsWith('mobile') ? 'bottom-start' : 'right-start'}>
213
+ <u-menu>
214
+ <u-menu-item @click=${doA}>Action A</u-menu-item>
215
+ <u-menu-item @click=${doB}>Action B</u-menu-item>
216
+ </u-menu>
217
+ </u-popover>
218
+ `,
219
+ }
220
+ ```
221
+
222
+ ⚠**Pick `placement` based on `state`, not a fixed value.** On `mobile`/`mobile-open`, the sidebar
223
+ itself widens to occupy nearly the full screen — a sideways placement (`right-start`, the natural
224
+ choice for a desktop flyout) then has no room on either side, and `flip()` correctly declines to
225
+ flip when the opposite side has none either. The popover renders off-screen and is invisible. A
226
+ vertical placement (`bottom-start`) has room regardless of sidebar width and works at every state.
227
+ Confirmed empirically in `tests/browser/sidebar-popover-submenu.browser.test.ts` — this is not a
228
+ `strategy="absolute"` vs `"fixed"` distinction, switching strategy does not change the outcome.
229
+
230
+ ---
231
+
193
232
  ## Sidebar parts
194
233
 
195
234
  Parts available for `styles` overrides on the root layout: