lecodes-cli 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,16 @@
1
1
  import { PagerElement, type UIPager } from "./UIPager"
2
- import { ScreenElement, type UIScreen } from "./UIScreen"
2
+ import { ScreenElement, UIScreen } from "./UIScreen"
3
3
  import { UIButton } from "./UIButton"
4
4
  import { UIRow, UIColumn, type UIRow as UIRowType } from "./UIContainer"
5
5
  import { UIText } from "./UIText"
6
6
  import { UIImage, type ImageSource } from "./UIImage"
7
+ import type { UIChildArg } from "./UINode"
7
8
 
8
9
  // UITabs — the standard bottom-tab shell, prewired: a UIScreen composing [ UIPager (flexGrow: 1),
9
10
  // tab bar ]. Pure SDK composition over the pager — zero new ABI, works wherever UIPager does.
10
- // The build-mode twin of the design scaffold's `defineTabs` (lecodes-design/tabbar-surface.md):
11
- // same keyed config, same `tab-<id>` button names, same theme variables with the same fallbacks,
12
- // so a designed mockup and the built app render identically.
11
+ // `defineTabs` (below) is its design-stage twin (lecodes-design/tabbar-surface.md): the same keyed
12
+ // config without screens, mounted per-file with a literal tab id. They share the constants and bar
13
+ // geometry in this file, so a designed mockup and the built app render identically.
13
14
 
14
15
  /** Active/inactive/bar colors ride the theme — one `theme({ primaryColor, mutedColor, tabbarBg })`
15
16
  * call restyles the bar app-wide; the fallbacks keep an unthemed app looking right. */
@@ -173,3 +174,63 @@ export class TabsElement extends ScreenElement {
173
174
  export function UITabs(tabs: Record<string, UITabDef>): UITabs {
174
175
  return new TabsElement(tabs) as unknown as UITabs
175
176
  }
177
+
178
+ // ---- defineTabs — the design-stage twin ---------------------------------------------------------
179
+
180
+ /** A `defineTabs` tab: `label` + optional `icon` (an `assetIcon("lucide:…")` call — never a
181
+ * hand-written source object). `UITabDef` is this plus the tab's root `screen`. */
182
+ export type TabDef = Omit<UITabDef, "screen">
183
+
184
+ /** What `defineTabs` returns: the bar (mount it yourself when the screen owns its whole layout)
185
+ * and the standard screen shell around it. `active` must be a declared tab id, as a literal. */
186
+ export type TabsHandle<T extends Record<string, TabDef>> = {
187
+ /** Tab ids in declaration order. */
188
+ ids: (keyof T & string)[]
189
+ /** The bar alone — one button per tab, the `active` one highlighted. */
190
+ bar(active: keyof T & string): UIRowType
191
+ /** A screen sitting ON the bar: `[ padded content column, bar ]`. */
192
+ screen(active: keyof T & string, ...children: UIChildArg[]): UIScreen
193
+ }
194
+
195
+ /**
196
+ * Declare a tab bar WITHOUT screens — the design-stage twin of {@link UITabs} (same keys, same
197
+ * `tab-<id>` button names, same theme variables), for mockups where every screen is its own file
198
+ * and mounts the bar with a literal tab id:
199
+ *
200
+ * ```ts
201
+ * export const mainTabs = defineTabs({
202
+ * home: { label: "Home", icon: assetIcon("lucide:house") },
203
+ * profile: { label: "Profile", icon: assetIcon("lucide:user") },
204
+ * })
205
+ * // in a screen file:
206
+ * export default () => mainTabs.screen("home", [ ...content ])
207
+ * ```
208
+ *
209
+ * Restyle through the theme — `theme({ primaryColor, mutedColor, tabbarBg, screenBg })` recolors
210
+ * every bar at once. The built app passes the same keys (plus each tab's root screen) to `UITabs`,
211
+ * which renders the identical bar interactively.
212
+ */
213
+ export function defineTabs<T extends Record<string, TabDef>>(tabs: T): TabsHandle<T> {
214
+ const ids = Object.keys(tabs) as (keyof T & string)[]
215
+
216
+ const bar = (active: keyof T & string) =>
217
+ UIRow(ids.map((id) => {
218
+ const def = tabs[id]
219
+ const color = id === active ? ACTIVE : INACTIVE
220
+ return UIButton(
221
+ def.icon ? UIImage(def.icon).style({ width: 22, height: 22, tintColor: color }) : null,
222
+ UIText(def.label).style({ fontSize: 10, color }),
223
+ ).style({ name: `tab-${id}`, flexDirection: "column", gap: 3, flex: 1, pt: 8, pb: 6 })
224
+ })).style({
225
+ name: "tabbar", bgColor: BAR_BG, pb: "comfort-bottom",
226
+ borderTopWidth: 1, borderTopColor: BAR_BORDER,
227
+ })
228
+
229
+ const screen = (active: keyof T & string, ...children: UIChildArg[]) =>
230
+ UIScreen(
231
+ UIColumn(...children).style({ flexGrow: 1, p: 16, pt: "safe-top" }),
232
+ bar(active),
233
+ ).style({ bgColor: SCREEN_BG })
234
+
235
+ return { ids, bar, screen }
236
+ }