refactor(uix): move useId counter from soma to $active-uix/id

The soma `id` module conflated two concerns: `createId` (wraps Svelte
`$props.id()` for SSR/ARIA element ids — ~200 soma importers, intrinsic to
the headless layer) and `useId` (a generic monotonic counter — 6 importers).
Only the counter is a cross-layer utility.

Move `useId` to a new `$active-uix/id` subpath (mirroring the existing
`$active-uix/prefs` that soma already imports), so any UIX layer — soma,
eidos, demos, apps — can mint client-only ids from a cheap, collision-free
counter without coupling to soma. `createId` stays in soma (its domain).

  + src/uix/active-uix/id/index.ts   (useId)
  ~ soma/id now exports createId only
  ~ 6 useId importers repointed to $active-uix/id
    (floating, toaster, date-field, time-field, accordion-item, internal/arrow)

Output format unchanged (`soma-{component}-{n}`) -> zero behavior change.
`npm run check` 0 errors; toast/accordion/popover verified in browser.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
active-uix
dev 4 months ago
parent f8056f438b
commit 9f007a84f7

@ -0,0 +1,24 @@
/**
* Process-unique client-side id counter.
*
* `useId()` returns the next value of a monotonic module-level counter —
* cheaper than `Date.now()` and, unlike a timestamp, collision-free. Use it
* for client-only ephemeral ids (list keys, generated handles).
*
* NOT for SSR / ARIA element ids derived from the component tree — those use
* soma's `createId`, which wraps Svelte's `$props.id()` for hydration-stable
* output. A global counter would desync between server and client renders.
*
* Lives in active-uix (not soma) so any UIX layer — soma, eidos, demos,
* apps — can mint ids without coupling to soma. Imported by subpath
* (`$active-uix/id`), mirroring `$active-uix/prefs`, so it never pulls the
* runtime barrel. The `soma-` prefix is the shared UIX id convention (same
* shape `createId` emits), not a marker of where this lives.
*/
let counter = 0
export function useId(component = ''): string {
const id = ++counter
return component ? `soma-${component}-${id}` : `soma-${id}`
}

@ -838,7 +838,7 @@ src/uix/soma/
├── keyboard/ ← KEYS, directional
├── dom/ ← DOM utilities, focus
├── css/ ← styleToString, cssToStyleObj
├── id/ ← createId, useId
├── id/ ← createId (useId → $active-uix/id)
├── types/ ← shared types + service interfaces
├── layers/ ← behavior layers (classes only)
│ ├── presence.svelte.ts

@ -4,7 +4,8 @@
writableActive
} from '$libs/reactive';
import { mergeProps } from '../../../props';
import { createId, useId } from '../../../id';
import { createId } from '../../../id';
import { useId } from '$active-uix/id';
import { AccordionItemProvider } from '../accordion-provider.svelte';
import type { AccordionItemProps } from '../types';

@ -21,7 +21,7 @@ import type {
} from '../../types';
import { KEYS, getDirectionalKeys } from '../../keyboard';
import { isNumberString } from '../../types/guards';
import { useId } from '../../id';
import { useId } from '$active-uix/id';
import { Soma } from '../../core/soma.svelte';
import {
DateFormatter,

@ -1,6 +1,6 @@
<script lang="ts">
import { mergeProps } from '../../props';
import { useId } from '../../id';
import { useId } from '$active-uix/id';
import type { WithChild, PrimitiveSpanAttributes } from '../../types';
type ArrowProps = WithChild<{

@ -12,7 +12,7 @@ import {
import type { SomaFocusEvent, SomaKeyboardEvent, OnChangeFn } from '../../types';
import { KEYS, getDirectionalKeys } from '../../keyboard';
import { isNumberString } from '../../types/guards';
import { useId } from '../../id';
import { useId } from '$active-uix/id';
import { Soma } from '../../core/soma.svelte';
import {
DateFormatter,

@ -1,4 +1,4 @@
import { useId } from '../../id';
import { useId } from '$active-uix/id';
import type { Intent } from '$uix/intent';
// ── Types ────────────────────────────────────────────────────────────────────

@ -1,5 +1,3 @@
let counter = 0;
/**
* Create an ID from a Svelte $props.id() uid, with optional component context.
*
@ -7,13 +5,11 @@ let counter = 0;
* createId(uid) // "soma-c12"
* createId(uid, 'dialog-trigger') // "soma-dialog-trigger-c12"
* ```
*
* This is soma's ARIA/DOM id helper: the `uid` comes from the component's
* `$props.id()`, so the output is hydration-stable (deterministic per render
* tree). For a generic client-only counter, use `useId` from `$active-uix/id`.
*/
export function createId(uid: string, component?: string): string {
return component ? `soma-${component}-${uid}` : `soma-${uid}`;
}
/** Generate a unique ID with component context. */
export function useId(component = ''): string {
const id = ++counter;
return component ? `soma-${component}-${id}` : `soma-${id}`;
}

@ -1 +1 @@
export { createId, useId } from './create-id';
export { createId } from './create-id';

@ -15,7 +15,7 @@ import { cssToStyleObj, styleToString } from '../../css';
import type { ActiveDom } from '$adom';
import { state, readableActive, type Active, type ActiveProps } from '$libs/reactive';
import { isNotNull } from '../../types';
import { useId } from '../../id';
import { useId } from '$active-uix/id';
import { context, type SomaContext } from '../../provider';
import type { Arrayable, Direction, StyleProperties } from '../../types';
import type { WithRefOpts } from '../../provider';

Loading…
Cancel
Save

Powered by TurnKey Linux.