@symbiote-native/solid 3.2.0 → 3.3.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.
Files changed (61) hide show
  1. package/build/components/flat-list/index.d.ts +3 -2
  2. package/build/components/flat-list/index.jsx +8 -10
  3. package/build/components/input-accessory-view-props.d.ts +4 -4
  4. package/build/components/keyboard-avoiding-view.d.ts +2 -2
  5. package/build/components/keyboard-avoiding-view.jsx +34 -114
  6. package/build/components/layout-conformance-props.d.ts +6 -0
  7. package/build/components/layout-conformance-props.js +1 -0
  8. package/build/components/modal.d.ts +4 -4
  9. package/build/components/modal.jsx +11 -11
  10. package/build/components/refresh-control-props.d.ts +7 -7
  11. package/build/components/scroll-view-props.d.ts +8 -5
  12. package/build/components/text-input-props.d.ts +2 -1
  13. package/build/components/text-props.d.ts +3 -3
  14. package/build/components/touchable-props.d.ts +6 -6
  15. package/build/components/virtualized-list/index.android.d.ts +2 -2
  16. package/build/components/virtualized-list/index.ios.d.ts +2 -2
  17. package/build/components/virtualized-list/item-content.d.ts +6 -0
  18. package/build/components/virtualized-list/item-content.jsx +9 -0
  19. package/build/components/virtualized-list/list-body.d.ts +15 -0
  20. package/build/components/virtualized-list/list-body.jsx +20 -0
  21. package/build/components/virtualized-list/list-parts.d.ts +14 -0
  22. package/build/components/virtualized-list/list-parts.js +27 -0
  23. package/build/components/virtualized-list/list-rows.d.ts +18 -0
  24. package/build/components/virtualized-list/list-rows.jsx +91 -0
  25. package/build/components/virtualized-list/list-window.d.ts +11 -0
  26. package/build/components/virtualized-list/list-window.js +29 -0
  27. package/build/components/virtualized-list/nested-scope.d.ts +7 -0
  28. package/build/components/virtualized-list/nested-scope.js +21 -0
  29. package/build/components/virtualized-list/scroll-handlers.d.ts +12 -0
  30. package/build/components/virtualized-list/scroll-handlers.js +31 -0
  31. package/build/components/virtualized-list/scroll-tree.d.ts +18 -0
  32. package/build/components/virtualized-list/scroll-tree.jsx +143 -0
  33. package/build/components/virtualized-list/shared.d.ts +3 -74
  34. package/build/components/virtualized-list/shared.jsx +53 -752
  35. package/build/components/virtualized-list/sticky-binding.d.ts +13 -0
  36. package/build/components/virtualized-list/sticky-binding.js +111 -0
  37. package/build/components/virtualized-list/sticky-header.d.ts +1 -0
  38. package/build/components/virtualized-list/sticky-header.jsx +11 -159
  39. package/build/components/virtualized-list/sticky-state.d.ts +14 -0
  40. package/build/components/virtualized-list/sticky-state.js +54 -0
  41. package/build/components/virtualized-list/use-list-driver.d.ts +22 -0
  42. package/build/components/virtualized-list/use-list-driver.js +128 -0
  43. package/build/components/virtualized-list/use-separators.d.ts +7 -0
  44. package/build/components/virtualized-list/use-separators.js +22 -0
  45. package/build/components/virtualized-list/virtualized-list-props.d.ts +86 -0
  46. package/build/components/virtualized-list/virtualized-list-props.js +53 -0
  47. package/build/components/virtualized-section-list/index.d.ts +22 -9
  48. package/build/components/virtualized-section-list/index.jsx +87 -115
  49. package/build/components/virtualized-section-list/section-item-cell.d.ts +18 -0
  50. package/build/components/virtualized-section-list/section-item-cell.jsx +37 -0
  51. package/build/components.d.ts +5 -1
  52. package/build/components.js +1 -0
  53. package/build/index.d.ts +9 -4
  54. package/build/index.js +20 -37
  55. package/build/modules/animated/index.d.ts +54 -41
  56. package/build/modules/animated/index.js +2 -26
  57. package/build/modules/app-registry/index.d.ts +3 -3
  58. package/build/modules/app-registry/index.js +2 -2
  59. package/build/modules/status-bar.js +14 -27
  60. package/build/primitives/create-color-scheme.d.ts +1 -1
  61. package/package.json +6 -6
@@ -0,0 +1,53 @@
1
+ // The public prop contract of the Solid VirtualizedList
2
+ // Consumed by the lifecycle itself, everything LEFT OVER is the accessibility surface that rides
3
+ // down onto the scroll host
4
+ export const HANDLED_PROPS = [
5
+ 'data',
6
+ 'getItem',
7
+ 'getItemCount',
8
+ 'renderItem',
9
+ 'ListItemComponent',
10
+ 'getItemLayout',
11
+ 'initialNumToRender',
12
+ 'windowSize',
13
+ 'disableVirtualization',
14
+ 'ListHeaderComponent',
15
+ 'ListFooterComponent',
16
+ 'ListEmptyComponent',
17
+ 'ItemSeparatorComponent',
18
+ 'CellRendererComponent',
19
+ 'ref',
20
+ 'onEndReached',
21
+ 'onEndReachedThreshold',
22
+ 'onStartReached',
23
+ 'onStartReachedThreshold',
24
+ 'keyExtractor',
25
+ 'onViewableItemsChanged',
26
+ 'viewabilityConfig',
27
+ 'viewabilityConfigCallbackPairs',
28
+ 'onScroll',
29
+ 'onScrollBeginDrag',
30
+ 'onScrollEndDrag',
31
+ 'onMomentumScrollBegin',
32
+ 'onMomentumScrollEnd',
33
+ 'scrollEventThrottle',
34
+ 'keyboardShouldPersistTaps',
35
+ 'keyboardDismissMode',
36
+ 'horizontal',
37
+ 'inverted',
38
+ 'style',
39
+ 'contentContainerStyle',
40
+ 'ListHeaderComponentStyle',
41
+ 'ListFooterComponentStyle',
42
+ 'class',
43
+ 'onScrollToIndexFailed',
44
+ 'onRefresh',
45
+ 'refreshing',
46
+ 'progressViewOffset',
47
+ 'stickyHeaderIndices',
48
+ 'maintainVisibleContentPosition',
49
+ 'initialScrollIndex',
50
+ 'maxToRenderPerBatch',
51
+ 'updateCellsBatchingPeriod',
52
+ 'extraData',
53
+ ];
@@ -1,30 +1,37 @@
1
1
  import { type Accessor, type Ref } from 'solid-js';
2
2
  import type { JSX } from '../../jsx-runtime';
3
- import { type ISection, type IAccessibilityProps, type IAriaProps, type ISeparatorProps, type ISeparators, type IVirtualizedSectionListHandle } from '@symbiote-native/components';
3
+ import { type ISection as ICoreSection, type IAccessibilityProps, type IAriaProps, type IInnerViewRef, type ISeparatorProps, type ISeparators, type IVirtualizedSectionListHandle } from '@symbiote-native/components';
4
4
  import { type IClassNameValue, type IStyleProp, type ISymbioteEvent, type IViewStyle } from '@symbiote-native/engine';
5
- export type { ISection, IVirtualizedSectionListHandle };
6
- export interface ISectionHeaderInfo<ItemT> {
5
+ import { type ICellRendererComponent } from '../virtualized-list';
6
+ export type { IVirtualizedSectionListHandle };
7
+ export type ISection<ItemT> = ICoreSection<ItemT> & {
8
+ renderItem?: (info: Accessor<ISectionCellInfo<ItemT>>) => JSX.Element;
9
+ ItemSeparatorComponent?: (props: ISeparatorProps<ItemT>) => JSX.Element;
10
+ };
11
+ export type ISectionHeaderInfo<ItemT> = {
7
12
  section: ISection<ItemT>;
8
- }
9
- export interface ISectionCellInfo<ItemT> {
13
+ };
14
+ export type ISectionCellInfo<ItemT> = {
10
15
  item: ItemT;
11
16
  index: number;
12
17
  section: ISection<ItemT>;
13
18
  separators: ISeparators;
14
- }
15
- export interface IVirtualizedSectionListProps<ItemT> extends IAccessibilityProps, IAriaProps {
19
+ };
20
+ export type IVirtualizedSectionListProps<ItemT> = IAccessibilityProps & IAriaProps & {
16
21
  sections: ReadonlyArray<ISection<ItemT>>;
17
22
  renderItem: (info: Accessor<ISectionCellInfo<ItemT>>) => JSX.Element;
18
23
  renderSectionHeader?: (info: Accessor<ISectionHeaderInfo<ItemT>>) => JSX.Element;
19
24
  renderSectionFooter?: (info: Accessor<ISectionHeaderInfo<ItemT>>) => JSX.Element;
20
25
  stickySectionHeadersEnabled?: boolean;
21
- SectionSeparatorComponent?: () => JSX.Element;
26
+ SectionSeparatorComponent?: (props: ISeparatorProps<ItemT>) => JSX.Element;
22
27
  keyExtractor?: (item: ItemT, index: number) => string;
23
28
  ItemSeparatorComponent?: (props: ISeparatorProps<ItemT>) => JSX.Element;
24
29
  ref?: Ref<IVirtualizedSectionListHandle>;
25
30
  ListHeaderComponent?: JSX.Element;
26
31
  ListFooterComponent?: JSX.Element;
27
32
  ListEmptyComponent?: JSX.Element;
33
+ ListHeaderComponentStyle?: IStyleProp<IViewStyle>;
34
+ ListFooterComponentStyle?: IStyleProp<IViewStyle>;
28
35
  extraData?: unknown;
29
36
  onEndReached?: (info: {
30
37
  distanceFromEnd: number;
@@ -42,7 +49,10 @@ export interface IVirtualizedSectionListProps<ItemT> extends IAccessibilityProps
42
49
  maxToRenderPerBatch?: number;
43
50
  updateCellsBatchingPeriod?: number;
44
51
  windowSize?: number;
52
+ disableVirtualization?: boolean;
53
+ CellRendererComponent?: ICellRendererComponent<unknown>;
45
54
  inverted?: boolean;
55
+ horizontal?: boolean;
46
56
  maintainVisibleContentPosition?: {
47
57
  minIndexForVisible: number;
48
58
  autoscrollToTopThreshold?: number;
@@ -52,13 +62,16 @@ export interface IVirtualizedSectionListProps<ItemT> extends IAccessibilityProps
52
62
  onScrollEndDrag?: (event: ISymbioteEvent) => void;
53
63
  onMomentumScrollBegin?: (event: ISymbioteEvent) => void;
54
64
  onMomentumScrollEnd?: (event: ISymbioteEvent) => void;
65
+ onContentSizeChange?: (width: number, height: number) => void;
55
66
  scrollEventThrottle?: number;
56
67
  keyboardShouldPersistTaps?: boolean | 'always' | 'never' | 'handled';
57
68
  keyboardDismissMode?: 'none' | 'on-drag' | 'interactive';
58
69
  removeClippedSubviews?: boolean;
59
70
  nestedScrollEnabled?: boolean;
71
+ stickyHeaderHiddenOnScroll?: boolean;
72
+ innerViewRef?: IInnerViewRef;
60
73
  style?: IStyleProp<IViewStyle>;
61
74
  contentContainerStyle?: IStyleProp<IViewStyle> | string;
62
75
  class?: IClassNameValue;
63
- }
76
+ };
64
77
  export declare function VirtualizedSectionList<ItemT>(props: IVirtualizedSectionListProps<ItemT>): JSX.Element;
@@ -1,12 +1,10 @@
1
- // VirtualizedSectionList: sections flattened into one virtualized stream. Each section contributes
2
- // a header row, its item rows, then a footer row; the flattened tagged sequence feeds
3
- // VirtualizedList as a single list, so headers, items and footers are windowed by the same
4
- // machinery. The flattening, entry keying, separator-item unwrap and scrollToLocation mapping are
5
- // shared verbatim from @symbiote-native/components; this file wires only Solid's lifecycle.
1
+ // Sections flattened into one virtualized stream: header, items and footer per section
2
+ // Flattening, keying and `scrollToLocation` are shared, this file wires Solid's lifecycle
6
3
  import { createMemo, splitProps } from 'solid-js';
7
- import { flattenSections, resolveStickySectionHeaders, scrollLocationToFlatIndex, sectionEntryKey, unwrapEntryItem, } from '@symbiote-native/components';
4
+ import { cellGapsFor, createSeparatorBoard, flattenSections, renderSectionEntry, resolveScrollLocation, resolveStickySectionHeaders, routeScrollHandle, SECTION_ENTRY_KIND, SEPARATOR_GAP_KIND, sectionEntryKey, } from '@symbiote-native/components';
8
5
  import { Platform, dlog, } from '@symbiote-native/engine';
9
6
  import { VirtualizedList, } from '../virtualized-list';
7
+ import { SectionItemCell } from './section-item-cell.jsx';
10
8
  // Consumed by this layer; everything LEFT OVER is VirtualizedList's own surface (the scroll host,
11
9
  // the list chrome, the windowing knobs, the accessibility props) and rides down untouched, exactly
12
10
  // as React's VirtualizedSectionList spreads its `...rest` onto the inner list.
@@ -21,125 +19,99 @@ const HANDLED_PROPS = [
21
19
  'stickySectionHeadersEnabled',
22
20
  'ref',
23
21
  ];
24
- export function VirtualizedSectionList(props) {
25
- const [, listRest] = splitProps(props, HANDLED_PROPS);
26
- const flattened = createMemo(() => flattenSections(props.sections, props.SectionSeparatorComponent !== undefined));
27
- const entries = () => flattened().entries;
28
- // RN sticks section headers by default only on iOS; Android does not unless asked. The
29
- // headerIndices are the flat positions of every section header, which the inner list wraps.
30
- const stickyHeaderIndices = () => resolveStickySectionHeaders(props.stickySectionHeadersEnabled, flattened().headerIndices, Platform.OS);
31
- // The entry under a row keeps its KIND for as long as the row lives — sectionEntryKey namespaces
32
- // header/footer/separator keys away from item keys, so <For> never hands a row an entry of a
33
- // different kind. `previous` covers only the tick where new sections have landed but the row has
34
- // not been disposed yet, during which the row's index can point at a neighbouring entry.
35
- function sectionInfo(info, initial) {
36
- return createMemo((previous) => {
37
- const entry = info().item;
38
- return entry.kind === 'section-separator'
39
- ? previous
40
- : { section: entry.section };
41
- }, { section: initial });
42
- }
43
- function cellInfo(info, initial) {
44
- return createMemo((previous) => {
45
- const entry = info().item;
46
- if (entry.kind !== 'item')
47
- return previous;
48
- return {
49
- item: entry.item,
50
- index: entry.itemIndex,
51
- section: entry.section,
52
- separators: info().separators,
53
- };
54
- }, initial);
55
- }
56
- // The inner list, held by identity in a plain variable: everything this handle adds over
57
- // VirtualizedList's own is the (section, item) -> flat index resolution, and the rest routes
58
- // straight through — the shared IScrollRoutingHandle tail both list handles extend.
59
- let inner;
60
- const handle = {
22
+ // Every entry kind carries its section, so a row whose index briefly points at a neighbour
23
+ // still reads a valid one
24
+ function sectionInfoOf(info) {
25
+ return createMemo(() => ({ section: info().item.section }));
26
+ }
27
+ function cellInfoOf(info, initial) {
28
+ return createMemo((previous) => {
29
+ const entry = info().item;
30
+ if (entry.kind !== SECTION_ENTRY_KIND.item)
31
+ return previous;
32
+ return {
33
+ item: entry.item,
34
+ index: entry.itemIndex,
35
+ section: entry.section,
36
+ separators: initial.separators,
37
+ };
38
+ }, initial);
39
+ }
40
+ // The separator for a gap: the section one, or the item one where a section's own beats the list's
41
+ function separatorRenderFor(gap, props) {
42
+ if (gap.props === undefined)
43
+ return undefined;
44
+ const gapProps = gap.props;
45
+ const Separator = gap.kind === SEPARATOR_GAP_KIND.section
46
+ ? props.SectionSeparatorComponent
47
+ : (gapProps.section.ItemSeparatorComponent ??
48
+ props.ItemSeparatorComponent);
49
+ if (Separator === undefined)
50
+ return undefined;
51
+ return ({ isHighlighted, override }) => (<Separator highlighted={isHighlighted} {...gapProps} {...override}/>);
52
+ }
53
+ // An item entry becomes a cell that paints its own separators around the row
54
+ function itemCellFor(rendering, info, entry) {
55
+ const { props, entries, keyOf } = rendering;
56
+ const gaps = createMemo(() => cellGapsFor(entries(), info().index));
57
+ const renderItem = entry.section.renderItem ?? props.renderItem;
58
+ return (<SectionItemCell board={rendering.board} cellKey={createMemo(() => keyOf(info().item))} prevCellKey={createMemo(() => {
59
+ const previous = entries()[info().index - 1];
60
+ return previous === undefined ? undefined : keyOf(previous);
61
+ })} renderItem={separators => renderItem(cellInfoOf(info, {
62
+ item: entry.item,
63
+ index: entry.itemIndex,
64
+ section: entry.section,
65
+ separators,
66
+ }))} leadingSeparator={createMemo(() => separatorRenderFor(gaps().leading, props))} trailingSeparator={createMemo(() => separatorRenderFor(gaps().trailing, props))} isInverted={props.inverted === true}/>);
67
+ }
68
+ // Called once per cell, `buildCell` of `VirtualizedList` already untracks it
69
+ function entryRendererFor(rendering) {
70
+ const { props } = rendering;
71
+ return info => renderSectionEntry({
72
+ header: () => props.renderSectionHeader?.(sectionInfoOf(info)),
73
+ footer: () => props.renderSectionFooter?.(sectionInfoOf(info)),
74
+ item: entry => itemCellFor(rendering, info, entry),
75
+ }, info().item);
76
+ }
77
+ // This handle adds the (section, item) to flat index resolution over the list's own handle
78
+ // The rest routes straight through the shared `IScrollRoutingHandle` tail
79
+ function buildSectionHandle(getInner, locate) {
80
+ return {
61
81
  scrollToLocation: (params) => {
62
- const flatIndex = scrollLocationToFlatIndex(flattened().headerIndices, params.sectionIndex, params.itemIndex);
63
- if (flatIndex === undefined) {
82
+ const target = locate(params);
83
+ if (target === undefined) {
64
84
  dlog(`VirtualizedSectionList scrollToLocation: section ${params.sectionIndex} out of range`);
65
85
  return;
66
86
  }
67
87
  dlog(`VirtualizedSectionList scrollToLocation section=${params.sectionIndex} ` +
68
- `item=${params.itemIndex} -> flat ${flatIndex}`);
69
- inner?.scrollToIndex({
70
- index: flatIndex,
71
- viewOffset: params.viewOffset,
72
- viewPosition: params.viewPosition,
73
- animated: params.animated,
74
- });
75
- },
76
- flashScrollIndicators: () => {
77
- inner?.flashScrollIndicators();
78
- },
79
- getNativeScrollRef: () => inner?.getNativeScrollRef() ?? null,
80
- getScrollableNode: () => inner?.getScrollableNode() ?? null,
81
- getScrollResponder: () => inner?.getScrollResponder() ?? null,
82
- getScrollNode: () => inner?.getScrollNode() ?? null,
83
- recordInteraction: () => {
84
- inner?.recordInteraction();
88
+ `item=${params.itemIndex} -> flat ${target.index}`);
89
+ getInner()?.scrollToIndex(target);
85
90
  },
91
+ ...routeScrollHandle(getInner),
86
92
  };
93
+ }
94
+ export function VirtualizedSectionList(props) {
95
+ const [, listRest] = splitProps(props, HANDLED_PROPS);
96
+ const flattened = createMemo(() => flattenSections(props.sections));
97
+ const entries = () => flattened().entries;
98
+ // RN sticks section headers by default only on iOS; Android does not unless asked. The
99
+ // headerIndices are the flat positions of every section header, which the inner list wraps.
100
+ const stickyHeaderIndices = () => resolveStickySectionHeaders(props.stickySectionHeadersEnabled, flattened().headerIndices, Platform.OS);
101
+ // The inner list, held by identity in a plain variable
102
+ let inner;
103
+ const handle = buildSectionHandle(() => inner, params => resolveScrollLocation(flattened().headerIndices, stickyHeaderIndices(), params));
87
104
  if (typeof props.ref === 'function')
88
105
  props.ref(handle);
89
- // Section chrome keys off its section (and never reaches the user's extractor, which is typed on
90
- // ItemT); an item keys off the user's extractor with its index INSIDE its section.
91
- function entryKeyExtractor(entry, index) {
92
- return sectionEntryKey(entry, index, props.keyExtractor);
93
- }
94
- function entrySeparatorProps(entryProps) {
95
- return {
96
- ...entryProps,
97
- leadingItem: unwrapEntryItem(entryProps.leadingItem),
98
- trailingItem: unwrapEntryItem(entryProps.trailingItem),
99
- };
100
- }
101
- // The user's separator is typed on ItemT while the inner stream carries the entry wrapper, so
102
- // each side is unwrapped back to its item — undefined for a gap next to section chrome, which has
103
- // no item on that side.
104
- const entrySeparatorComponent = createMemo(() => {
105
- const ItemSeparator = props.ItemSeparatorComponent;
106
- if (ItemSeparator === undefined)
107
- return undefined;
108
- // ONE bag, not a spread plus two overriding attributes: Solid's mergeProps resolves a key by
109
- // scanning its sources back to front and taking the first NON-undefined value, so an override
110
- // that unwraps to `undefined` (every gap next to section chrome) falls back to the raw entry
111
- // instead of clearing it, and the user's `leadingItem` reads as the wrapper object. Building the
112
- // whole bag in one function keeps a single source — and, being a spread of a CALL, it compiles
113
- // to mergeProps(() => …) so the separator's props stay live.
114
- return (entryProps) => (<ItemSeparator {...entrySeparatorProps(entryProps)}/>);
106
+ // Section chrome keys off its section and never reaches the user's extractor, typed on ItemT
107
+ const entryKeyExtractor = (entry) => sectionEntryKey(entry, props.keyExtractor);
108
+ const renderEntry = entryRendererFor({
109
+ props,
110
+ entries,
111
+ board: createSeparatorBoard(),
112
+ keyOf: entryKeyExtractor,
115
113
  });
116
- // Called ONCE per cell and already untracked by VirtualizedList's buildCell.
117
- function renderEntry(info) {
118
- const entry = info().item;
119
- if (entry.kind === 'header') {
120
- const header = props.renderSectionHeader;
121
- return header === undefined
122
- ? undefined
123
- : header(sectionInfo(info, entry.section));
124
- }
125
- if (entry.kind === 'footer') {
126
- const footer = props.renderSectionFooter;
127
- return footer === undefined
128
- ? undefined
129
- : footer(sectionInfo(info, entry.section));
130
- }
131
- if (entry.kind === 'section-separator') {
132
- const SectionSeparator = props.SectionSeparatorComponent;
133
- return SectionSeparator === undefined ? undefined : <SectionSeparator />;
134
- }
135
- return props.renderItem(cellInfo(info, {
136
- item: entry.item,
137
- index: entry.itemIndex,
138
- section: entry.section,
139
- separators: info().separators,
140
- }));
141
- }
142
- return (<VirtualizedList {...listRest} data={entries()} getItem={(_source, index) => entries()[index]} getItemCount={() => entries().length} renderItem={renderEntry} stickyHeaderIndices={stickyHeaderIndices()} keyExtractor={entryKeyExtractor} ItemSeparatorComponent={entrySeparatorComponent()} ref={list => {
114
+ return (<VirtualizedList {...listRest} data={entries()} getItem={(_source, index) => entries()[index]} getItemCount={() => entries().length} renderItem={renderEntry} stickyHeaderIndices={stickyHeaderIndices()} keyExtractor={entryKeyExtractor} ref={list => {
143
115
  inner = list;
144
116
  }}/>);
145
117
  }
@@ -0,0 +1,18 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import type { JSX } from '../../jsx-runtime';
3
+ import { type ISeparatorBoard, type ISeparators } from '@symbiote-native/components';
4
+ export type ISeparatorState = {
5
+ isHighlighted: boolean;
6
+ override: Record<string, unknown> | undefined;
7
+ };
8
+ export type ISeparatorRender = (state: ISeparatorState) => JSX.Element;
9
+ export type ISectionItemCellProps = {
10
+ board: ISeparatorBoard<Record<string, unknown>>;
11
+ cellKey: Accessor<string>;
12
+ prevCellKey: Accessor<string | undefined>;
13
+ renderItem: (separators: ISeparators) => JSX.Element;
14
+ leadingSeparator: Accessor<ISeparatorRender | undefined>;
15
+ trailingSeparator: Accessor<ISeparatorRender | undefined>;
16
+ isInverted: boolean;
17
+ };
18
+ export declare function SectionItemCell(props: ISectionItemCellProps): JSX.Element;
@@ -0,0 +1,37 @@
1
+ // Item cell with its separators around it, as in RN's `ItemWithSeparator`
2
+ // Their state sits in a shared board, т.к. `highlight()` also lights the previous cell's one
3
+ import { createEffect, createSignal, onCleanup, untrack, } from 'solid-js';
4
+ import { createCellSeparators, } from '@symbiote-native/components';
5
+ export function SectionItemCell(props) {
6
+ const { board } = props;
7
+ const [state, setState] = createSignal(untrack(() => board.read(props.cellKey())));
8
+ createEffect(() => {
9
+ const cellKey = props.cellKey();
10
+ setState(board.read(cellKey));
11
+ const unsubscribe = board.subscribe(cellKey, () => setState(board.read(cellKey)));
12
+ onCleanup(() => {
13
+ unsubscribe();
14
+ board.release(cellKey);
15
+ });
16
+ });
17
+ const separators = createCellSeparators(board, () => ({
18
+ cellKey: props.cellKey(),
19
+ prevCellKey: props.prevCellKey(),
20
+ has: {
21
+ leading: props.leadingSeparator() !== undefined,
22
+ trailing: props.trailingSeparator() !== undefined,
23
+ },
24
+ }));
25
+ const content = untrack(() => props.renderItem(separators));
26
+ const leading = () => props.leadingSeparator()?.({
27
+ isHighlighted: state().leadingHighlighted,
28
+ override: state().leadingOverride,
29
+ });
30
+ const trailing = () => props.trailingSeparator()?.({
31
+ isHighlighted: state().trailingHighlighted,
32
+ override: state().trailingOverride,
33
+ });
34
+ return props.isInverted
35
+ ? [trailing, content, leading]
36
+ : [leading, content, trailing];
37
+ }
@@ -1,6 +1,8 @@
1
1
  export type { IViewProps } from './components/view-props';
2
2
  export type { ITextProps } from './components/text-props';
3
3
  export type { ISafeAreaViewProps } from './components/safe-area-view-props';
4
+ export type { ILayoutConformanceProps } from './components/layout-conformance-props';
5
+ export type { ILayoutConformanceMode } from '@symbiote-native/components';
4
6
  export type { IImageProps } from './components/image-props';
5
7
  export type { IPressableProps } from './components/pressable-props';
6
8
  export type { IActivityIndicatorProps } from './components/activity-indicator-props';
@@ -16,7 +18,9 @@ export type { IRefreshControlProps } from './components/refresh-control-props';
16
18
  export type { IScrollViewProps } from './components/scroll-view-props';
17
19
  export type { IScrollViewHandle } from '@symbiote-native/components';
18
20
  export { VirtualizedList } from './components/virtualized-list';
19
- export type { IVirtualizedListProps, IVirtualizedListHandle, IVirtualizedListCellInfo, IVirtualizedListRenderItem, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './components/virtualized-list';
21
+ export { useVirtualizedListScope } from './components/virtualized-list/nested-scope';
22
+ export type { IListScope } from '@symbiote-native/components';
23
+ export type { IVirtualizedListProps, IVirtualizedListHandle, ICellRendererComponent, ICellRendererProps, IVirtualizedListCellInfo, IVirtualizedListRenderItem, ICellLayout, ISeparators, ISeparatorProps, IViewToken, IViewableItemsChangedInfo, IViewabilityConfig, IViewabilityConfigCallbackPair, } from './components/virtualized-list';
20
24
  export { FlatList } from './components/flat-list';
21
25
  export type { IFlatListProps, IFlatListHandle } from './components/flat-list';
22
26
  export { VirtualizedSectionList } from './components/virtualized-section-list';
@@ -8,6 +8,7 @@ export { KeyboardAvoidingView } from './components/keyboard-avoiding-view.jsx';
8
8
  // for the same import as the component. `IVirtualizedListComponent` stays internal — it is the
9
9
  // platform factory's return type, not API.
10
10
  export { VirtualizedList } from './components/virtualized-list';
11
+ export { useVirtualizedListScope } from './components/virtualized-list/nested-scope.js';
11
12
  // FlatList carries no platform split of its own — the iOS/Android divergence is
12
13
  // VirtualizedList's refreshControlMode, inherited by importing that folder, so Metro picks the
13
14
  // right variant with nothing here to switch on. IFlatListProps is an Omit over
package/build/index.d.ts CHANGED
@@ -8,8 +8,8 @@ export { setImageSourceResolver } from '@symbiote-native/components';
8
8
  export { descriptorToSolid } from './descriptor-to-solid';
9
9
  export { defineDescriptorComponent } from './define-descriptor-component';
10
10
  export { defineOptionalDescriptorComponent } from './define-optional-descriptor-component';
11
- export { AppRegistry, setHostRegistrar } from './modules/app-registry';
12
- export type { IComponentProvider, IWrapperComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
11
+ export { AppRegistry, HeadlessJsTaskError, setHostRegistrar, } from './modules/app-registry';
12
+ export type { IComponentProvider, IWrapperComponentProvider, IAppConfig, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
13
13
  export { StatusBar } from './modules/status-bar';
14
14
  export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
15
15
  export { Image } from './modules/image';
@@ -19,6 +19,7 @@ export { createTunnel } from './create-tunnel';
19
19
  export type { ITunnel, ITunnelInProps } from './create-tunnel';
20
20
  export { createColorScheme } from './primitives/create-color-scheme';
21
21
  export { createWindowDimensions } from './primitives/create-window-dimensions';
22
+ export { createAnimatedColor, createAnimatedValue, createAnimatedValueXY, } from '@symbiote-native/engine';
22
23
  export { createPermissionHook } from './primitives/create-permission-hook';
23
24
  export { createResourceHook } from './primitives/create-resource-hook';
24
25
  export { defineNativeViewComponent } from './primitives/define-native-view-component';
@@ -27,13 +28,17 @@ export { useEvent, useEventListener } from './primitives/use-event';
27
28
  export { Animated, createAnimatedComponent } from './modules/animated';
28
29
  export type { IAnimatedComponentProps } from './modules/animated';
29
30
  export { Platform, StyleSheet, PixelRatio, PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
30
- export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
31
+ export { setNativeViewConfigSource, setColorProcessor, } from '@symbiote-native/engine';
31
32
  export { dlog, isDebug } from '@symbiote-native/engine';
32
33
  export type { IViewStyle, ITextStyle, IFlexAlign, IFlexJustify, ISymbioteEvent, ISymbioteNode, IRootTag, INativeViewConfig, INativeViewConfigSource, IColorValue, IOpaqueColorValue, IDynamicColorIOSTuple, IPixelRatioStatic, IPlatformStatic, IPlatformOSType, IPlatformConstantsIOS, IPlatformConstantsAndroid, IPlatformSelectSpec, } from '@symbiote-native/engine';
33
- export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
34
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
34
35
  export type { IAlertType, IAlertButtonStyle, IAlertButton, IAlertButtons, IAlertOptions, IShareContent, IShareOptions, IShareAction, IActionSheetIOSOptions, IShareActionSheetIOSOptions, IShareActionSheetError, IUrlEvent, II18nManagerConstants, IDisplayMetrics, IDisplayMetricsAndroid, IDimensionsPayload, IDimensionsSet, IDimensionsKey, IDimensionsChangeListener, IDimensionsStatic, IColorSchemeName, IColorSchemePreference, IAppStateStatus, IAppStateEvent, IKeyboardEventName, IKeyboardEvent, IKeyboardMetrics, IBackPressEventName, IBackPressHandler, IPermission, IPermissionStatus, IRationale, ILayoutAnimationType, ILayoutAnimationProperty, ILayoutAnimationConfig, ILayoutAnimationAnim, IInteractionEvent, ISimpleTask, IPromiseTask, ITask, IHandle, } from '@symbiote-native/engine';
35
36
  export { PanResponder } from '@symbiote-native/engine';
36
37
  export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
37
38
  export { AccessibilityInfo } from '@symbiote-native/engine';
38
39
  export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, } from '@symbiote-native/engine';
39
40
  export { For, Index, Show, ErrorBoundary, Suspense, SuspenseList, } from 'solid-js';
41
+ export { DeviceEventEmitter, Easing, EventEmitter, NativeAppEventEmitter, NativeEventEmitter, } from '@symbiote-native/engine';
42
+ export type { IEasing, IEasingFunction, IEmitterSubscription, IEventEmitter, IEventEmitterModule, IEventSubscription, INativeEventListener, } from '@symbiote-native/engine';
43
+ export { codegenNativeCommands, codegenNativeComponent, DevMenu, DevSettings, LogBox, NativeComponentRegistry, NativeModules, Networking, PushNotificationIOS, ReactNativeVersion, registerCallableModule, requireNativeComponent, Systrace, Touchable, TurboModuleRegistry, UIManager, UTFSequence, } from '@symbiote-native/engine';
44
+ export type { IDevSettings } from '@symbiote-native/engine';
package/build/index.js CHANGED
@@ -4,13 +4,9 @@
4
4
  // uses. All Fabric clone-on-write lives in the engine, shared cross-adapter. App code imports
5
5
  // @symbiote-native/solid, never react-native.
6
6
  //
7
- // Layer status (symbiote-new-adapter §7): L4 in progress. The primitives, the stateful touch
8
- // components, the Touchable family, Button / ImageBackground / InputAccessoryView, StatusBar and
9
- // the list family are done at full parity with the React reference, and the engine-owned runtime
10
- // modules are re-exported below. Content relocation is in too: `Portal` (same-surface, React's
11
- // createPortal twin) and `createTunnel` (cross-surface), both written over the universal renderer
12
- // — solid-js/web's DOM-bound Portal was never the route to either. This barrel grows a layer at a
13
- // time on purpose, so a break localizes to the layer that introduced it.
7
+ // Layer status (symbiote-new-adapter §7): L4 in progress, the barrel grows a layer at a time
8
+ // Primitives, touch components, lists and the engine runtime modules are at full parity with React
9
+ // `Portal` (same-surface) and `createTunnel` (cross-surface) sit on the universal renderer
14
10
  //
15
11
  // The two lifecycle primitives are spelled `createColorScheme` / `createWindowDimensions`, not
16
12
  // `use*`: in Solid `use*` means consuming something that already exists (useContext,
@@ -37,14 +33,10 @@ export { defineDescriptorComponent } from './define-descriptor-component.js';
37
33
  export { defineOptionalDescriptorComponent } from './define-optional-descriptor-component.js';
38
34
  // Safe in the barrel (unlike ./bootstrap, which imports react-native): the registry seam itself
39
35
  // only reaches the engine and solid-js.
40
- export { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
41
- // Animated: the three list/scroll components that are still components, over the engine's
42
- // framework-agnostic value graph. There is no `Animated.View` / `Animated.Text` / `Animated.Image`
43
- // — those primitives are tags, and `<view style={{ opacity: someAnimatedValue }}>` is the whole
44
- // API (.claude/rules/animated-values-resolve-in-the-engine.md). Solid's JSX takes the dotted form
45
- // of what remains directly — `<Animated.ScrollView/>` compiles to
46
- // createComponent(Animated.ScrollView, …) — so no local alias is needed, unlike Angular, whose AOT
47
- // compiler cannot trace a class through property access (.claude/rules/dotted-component-tags.md).
36
+ export { AppRegistry, HeadlessJsTaskError, setHostRegistrar, } from './modules/app-registry/index.js';
37
+ // Animated keeps only the list and scroll components, over the engine's value graph
38
+ // The primitives are tags: `<view style={{ opacity: someAnimatedValue }}>` is the whole API
39
+ // Solid's JSX takes `<Animated.ScrollView/>` directly, unlike Angular (dotted-component-tags.md)
48
40
  export { StatusBar } from './modules/status-bar.js';
49
41
  // What survives of `Image` once the primitive is the `<image>` tag: RN's statics namespace, no
50
42
  // view. Grouped with the runtime modules rather than the components for exactly that reason.
@@ -64,6 +56,7 @@ export { createTunnel } from './create-tunnel/index.jsx';
64
56
  // the subscription logic; these add only the Solid lifecycle (signal + onCleanup).
65
57
  export { createColorScheme } from './primitives/create-color-scheme.js';
66
58
  export { createWindowDimensions } from './primitives/create-window-dimensions.js';
59
+ export { createAnimatedColor, createAnimatedValue, createAnimatedValueXY, } from '@symbiote-native/engine';
67
60
  export { createPermissionHook } from './primitives/create-permission-hook.js';
68
61
  export { createResourceHook } from './primitives/create-resource-hook.js';
69
62
  export { defineNativeViewComponent } from './primitives/define-native-view-component.js';
@@ -76,16 +69,15 @@ export { Animated, createAnimatedComponent } from './modules/animated/index.js';
76
69
  // Pure utilities and the color surface: no native event, no lifecycle, so they live in the engine
77
70
  // per <runtime_modules_layering>.
78
71
  export { Platform, StyleSheet, PixelRatio, PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
79
- // The three app-entry seams, wired once on a real host. setNativeViewConfigSource is how a
80
- // third-party Fabric view auto-derives its metadata:
81
- // setNativeViewConfigSource(name => ReactNativeViewConfigRegistry.get(name))
82
- export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
72
+ // Seams wired once on a real host: `setNativeViewConfigSource` derives third-party view configs
73
+ // `setNativeViewConfigSource(name => ReactNativeViewConfigRegistry.get(name))`
74
+ export { setNativeViewConfigSource, setColorProcessor, } from '@symbiote-native/engine';
83
75
  // Diagnostics gated by DEBUG (<keep_logs_gate_behind_DEBUG>): app code logs through the same seam
84
76
  // the engine does, never a bare console.log.
85
77
  export { dlog, isDebug } from '@symbiote-native/engine';
86
78
  // Imperative runtime modules: thin JS over getNativeModule + device events, no Fabric component
87
79
  // of their own, so there is nothing per-framework to write.
88
- export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
80
+ export { Alert, Share, ActionSheetIOS, Linking, Vibration, ToastAndroid, Settings, I18nManager, Dimensions, Appearance, AppState, Keyboard, KEYBOARD_EVENT, TextInputState, BackHandler, PermissionsAndroid, PERMISSIONS, RESULTS, LayoutAnimation, InteractionManager, } from '@symbiote-native/engine';
89
81
  // PanResponder: the gesture recognizer over the engine's responder events, shared by every adapter.
90
82
  export { PanResponder } from '@symbiote-native/engine';
91
83
  // AccessibilityInfo comes STRAIGHT from the engine, which owns the iOS/Android split
@@ -93,21 +85,12 @@ export { PanResponder } from '@symbiote-native/engine';
93
85
  // name, but that folder is a pure passthrough of these exact names — matching it means matching
94
86
  // the engine module, not growing a second one here.
95
87
  export { AccessibilityInfo } from '@symbiote-native/engine';
96
- // Solid's control flow is pure reactivity with no DOM dependency, so it works verbatim over the
97
- // universal renderer and is re-exported here to keep an app on one import. solid-js/web's own
98
- // `Portal` is NOT among them and cannot be — it allocates its container with
99
- // `document.createElement` — but the CAPABILITY is not missing: this package ships its own
100
- // `Portal` over the universal renderer (./create-portal, exported above), at the same
101
- // same-surface scope React and Angular chose for theirs. `Dynamic` is the one member of that DOM-bound pair still absent.
102
- // It is absent on its own terms, not as fallout from Portal: nothing here needed it (a tunnel
103
- // renders stored content by calling a thunk inside <For>, no dynamic component involved), and no
104
- // other adapter ships a twin of it for parity to require one.
105
- //
106
- // Solid's own control-flow `Switch`/`Match` pair is ABSENT for a different reason: `Switch` collides
107
- // head-on with RN's Switch component, which every other adapter exports under exactly that name and
108
- // which P0 parity (<adapters_reach_full_feature_parity>) pins here too. The RN component wins the
109
- // name; the control-flow pair keeps its canonical home, `import { Switch, Match } from 'solid-js'`
110
- // (alias it there if both are needed in one file). `Match` is withheld ALONGSIDE it deliberately —
111
- // re-exporting a lone `Match` while `Switch` means something else would compile fine and then fail
112
- // at runtime, which is worse than a missing export.
88
+ // Control flow from `solid-js` works over the universal renderer as is, `Portal` is our own above
89
+ // `Switch` is RN's component here, so the `Switch`/`Match` pair stays in `solid-js`
90
+ // A lone `Match` would compile and then fail at runtime, so it is withheld too
113
91
  export { For, Index, Show, ErrorBoundary, Suspense, SuspenseList, } from 'solid-js';
92
+ // The device event bus and the easing curves, shared verbatim from the engine
93
+ export { DeviceEventEmitter, Easing, EventEmitter, NativeAppEventEmitter, NativeEventEmitter, } from '@symbiote-native/engine';
94
+ // RN's dev and native-module utilities, shared verbatim from the engine. The ones RN owns forward
95
+ // to its own module, which `registerApp` hands over
96
+ export { codegenNativeCommands, codegenNativeComponent, DevMenu, DevSettings, LogBox, NativeComponentRegistry, NativeModules, Networking, PushNotificationIOS, ReactNativeVersion, registerCallableModule, requireNativeComponent, Systrace, Touchable, TurboModuleRegistry, UIManager, UTFSequence, } from '@symbiote-native/engine';