@solidrt/components 0.0.61 → 0.0.62

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/AGENTS.md CHANGED
@@ -99,7 +99,7 @@ One bullet per module, generated from the first paragraph of its docs/ file.
99
99
  - `Select` - A single-choice picker whose presentation forks on the interaction policy: `desktop`/`hybrid` opens an anchored dropdown under the trigger (flipping above when there is no room), `touch` opens a bottom sheet over a scrim. Same contract either way: `options` is an `Option[]` (`{ value, label }`), controlled via `value`/`onChange` or uncontrolled via `defaultValue`; pressing outside closes without a change. `placeholder` shows in the trigger while nothing is selected. Both presentations fade in and out, and the trigger's chevron flips while open. The option list is not scrollable yet, so keep it short. The chevron is the `theme.icons.chevronDown` slot when a theme sets one.
100
100
  - `SegmentedControl` - A single-choice row of equal-width segments joined flush: only the control's outermost corners are rounded, hairline dividers separate the segments, and the active segment is one `primary` indicator that springs between segments on a selection change - the `indicator` transition entry retimes it. Hovered segments tint with the theme `overlayHover` under non-touch interaction policies. `options` is an `Option[]`; controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Override the inactive fill via `style.backgroundColor` and the outer radius via `style.borderRadius`.
101
101
  - `ContextMenu` - Secondary actions on the wrapped content. The opening gesture follows the physical pointer: right-click for a mouse, long-press (500 ms, cancelled by finger travel; core's long-press recognizer) for touch and pen. The long-press wins the finger at its timer: a pressable inside retracts and does not fire on the lift, and a scroll that started first keeps the finger. The presentation forks on the interaction policy: `touch` gets a bottom sheet over a scrim, `desktop`/`hybrid` an anchored menu at the pointer that flips up near the bottom edge. Both presentations fade in and out. `items` is a `ContextMenuItem[]` (`{ label, onSelect?, disabled? }`); pressing outside closes without selecting.
102
- - `NavShell` - An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`.
102
+ - `NavShell` - An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`. With `@solidrt/router` the shell is the tabs route's component, `value`/`onChange` wired to `useTabs()` (`active()`/`select(path)`) and `<Outlet />` as the content; the router then keeps a stack per tab and the tabs mounted.
103
103
  - `SplitView` - A list-detail container driven by `policy.layout`: `twoPane` shows the `list` pane (width `listWidth`, default `theme.size.splitViewList`) beside the `detail` pane, `singlePane` shows one at a time per `showDetail`. Keep pane state (selection, scroll) in the app, not in the panes: crossing a breakpoint re-arranges and can remount them. It draws no chrome and adds no padding; a back affordance in the single-pane detail is the app's to render (fork on `policy.layout`).
104
104
  - `QrCode` - Renders a QR code for `data` out of primitives: same-color modules in a row collapse into one box, drawn on a light quiet-zone panel; the grid recomputes only when `data` or `level` changes. It paints black on white by default (not the theme) so it stays scannable through a theme switch; override `color`/`background` only if the contrast still holds. `moduleSize` (default 6) is pixels per module, `margin` (default 16) the quiet zone (keep it non-zero), `level` the error correction (`L`/`M`/`Q`/`H`, default `M`: higher tolerates more damage but caps data length sooner), `radius` the panel's corner radius.
105
105
  - `Icon` - A thin themed wrapper over the core `parseSvg` primitive. `src` is a whole SVG document as a string; the component parses it once (memoized), maps the draws to `<d-path>` in a square `designSize`-fitted box (`size`, default 24) and, for monochrome icons that stroke/fill with `currentColor`, recolors it via `color` (default the theme text color). It carries no icon set and no name registry, so any `currentColor` SVG works (Lucide, Feather, Heroicons, ...) and only the icons you import are bundled. Multi-color documents keep their own fills. For a non-square box, use `parseSvg` directly.
package/README.md CHANGED
@@ -738,7 +738,7 @@ API: `ContextMenu`, `ContextMenuProps`, `ContextMenuItem` - typed and commented
738
738
 
739
739
  ### NavShell
740
740
 
741
- An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`.
741
+ An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`. With `@solidrt/router` the shell is the tabs route's component, `value`/`onChange` wired to `useTabs()` (`active()`/`select(path)`) and `<Outlet />` as the content; the router then keeps a stack per tab and the tabs mounted.
742
742
 
743
743
  ```jsx
744
744
  import { NavShell, Icon } from "@solidrt/components"
package/docs/nav-shell.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # NavShell
2
2
 
3
- An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`.
3
+ An app shell that arranges primary navigation around the content per `policy.navigation`: bottom tabs under it (`bottomTabs`), a narrow rail (`rail`), or a wide sidebar (`sidebar`) beside it. The content is a single stable node; switching arrangement only flips the shell's flex direction and remounts the stateless nav strip, so page state survives a resize across a breakpoint. `items` is a `NavItem[]` (`{ value, label, icon? }`; the icon renders above the label in tabs/rail, beside it in the sidebar); controlled via `value`/`onChange`, or uncontrolled via `defaultValue`. Safe areas are the caller's concern: wrap the shell in `SafeArea`. With `@solidrt/router` the shell is the tabs route's component, `value`/`onChange` wired to `useTabs()` (`active()`/`select(path)`) and `<Outlet />` as the content; the router then keeps a stack per tab and the tabs mounted.
4
4
 
5
5
  ```jsx
6
6
  import { NavShell, Icon } from "@solidrt/components"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/components",
3
- "version": "0.0.61",
3
+ "version": "0.0.62",
4
4
  "license": "MIT",
5
5
  "funding": "https://github.com/sponsors/wellawaretech",
6
6
  "author": "Antoine van Wel",
@@ -21,6 +21,6 @@
21
21
  },
22
22
  "peerDependencies": {
23
23
  "@solidjs/signals": "2.0.0-rc.9",
24
- "@solidrt/core": "0.0.61"
24
+ "@solidrt/core": "0.0.62"
25
25
  }
26
26
  }
@@ -92,6 +92,13 @@ export interface EditorFieldProps extends TransitionProps {
92
92
  // navigator.clipboard (copy and cut need a range; single-line paste flattens
93
93
  // line breaks). A tap outside the field blurs it (core's outside-tap blur in
94
94
  // window.ts), as does Escape.
95
+ // Font options compared field by field: the same keys with the same values.
96
+ function sameFont(a: MeasureTextOptions, b: MeasureTextOptions): boolean {
97
+ let ka = Object.keys(a) as (keyof MeasureTextOptions)[]
98
+ let kb = Object.keys(b) as (keyof MeasureTextOptions)[]
99
+ return ka.length === kb.length && ka.every((k) => a[k] === b[k])
100
+ }
101
+
95
102
  export function EditorField(props: EditorFieldProps) {
96
103
  let [caretOn, setCaretOn] = createSignal(true)
97
104
 
@@ -403,13 +410,19 @@ export function EditorField(props: EditorFieldProps) {
403
410
  let layoutFont = () => layout().text as EditorLayoutProps
404
411
  let fontSize = () => (layoutFont().fontSize ?? theme.text.body.size) * policy.textScale
405
412
  let lineHeight = () => layoutFont().lineHeight ?? theme.text.body.lineHeight
406
- let font = (): MeasureTextOptions => ({
407
- fontFamily: layoutFont().fontFamily ?? theme.text.fontFamily,
408
- fontSize: fontSize(),
409
- lineHeight: lineHeight(),
410
- fontStyle: layoutFont().fontStyle,
411
- fontWeight: typeWeight(layoutFont().fontWeight ?? theme.text.body.weight, fontSize()),
412
- })
413
+ // A memo, equal by field: the editor layout reads the font on every
414
+ // caret, text or size change, and rebuilding it (theme, policy and the
415
+ // weight compensation) per read cost more than the geometry it fed.
416
+ let font = createMemo(
417
+ (): MeasureTextOptions => ({
418
+ fontFamily: layoutFont().fontFamily ?? theme.text.fontFamily,
419
+ fontSize: fontSize(),
420
+ lineHeight: lineHeight(),
421
+ fontStyle: layoutFont().fontStyle,
422
+ fontWeight: typeWeight(layoutFont().fontWeight ?? theme.text.body.weight, fontSize()),
423
+ }),
424
+ { equals: sameFont },
425
+ )
413
426
  let rowHeight = () => Math.round(fontSize() * lineHeight())
414
427
  let editor = createTextEditorLayout(
415
428
  () => viewport,