@symbiote-native/solid 3.1.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 (74) hide show
  1. package/build/components/checkbox-props.d.ts +6 -0
  2. package/build/components/checkbox-props.js +1 -0
  3. package/build/components/flat-list/index.d.ts +3 -2
  4. package/build/components/flat-list/index.jsx +8 -10
  5. package/build/components/input-accessory-view-props.d.ts +4 -4
  6. package/build/components/keyboard-avoiding-view.d.ts +2 -2
  7. package/build/components/keyboard-avoiding-view.jsx +34 -114
  8. package/build/components/layout-conformance-props.d.ts +6 -0
  9. package/build/components/layout-conformance-props.js +1 -0
  10. package/build/components/modal.d.ts +4 -4
  11. package/build/components/modal.jsx +11 -11
  12. package/build/components/refresh-control-props.d.ts +7 -7
  13. package/build/components/scroll-view-props.d.ts +8 -5
  14. package/build/components/text-input-props.d.ts +2 -1
  15. package/build/components/text-props.d.ts +3 -3
  16. package/build/components/touchable-props.d.ts +6 -6
  17. package/build/components/virtualized-list/index.android.d.ts +2 -2
  18. package/build/components/virtualized-list/index.ios.d.ts +2 -2
  19. package/build/components/virtualized-list/item-content.d.ts +6 -0
  20. package/build/components/virtualized-list/item-content.jsx +9 -0
  21. package/build/components/virtualized-list/list-body.d.ts +15 -0
  22. package/build/components/virtualized-list/list-body.jsx +20 -0
  23. package/build/components/virtualized-list/list-parts.d.ts +14 -0
  24. package/build/components/virtualized-list/list-parts.js +27 -0
  25. package/build/components/virtualized-list/list-rows.d.ts +18 -0
  26. package/build/components/virtualized-list/list-rows.jsx +91 -0
  27. package/build/components/virtualized-list/list-window.d.ts +11 -0
  28. package/build/components/virtualized-list/list-window.js +29 -0
  29. package/build/components/virtualized-list/nested-scope.d.ts +7 -0
  30. package/build/components/virtualized-list/nested-scope.js +21 -0
  31. package/build/components/virtualized-list/scroll-handlers.d.ts +12 -0
  32. package/build/components/virtualized-list/scroll-handlers.js +31 -0
  33. package/build/components/virtualized-list/scroll-tree.d.ts +18 -0
  34. package/build/components/virtualized-list/scroll-tree.jsx +143 -0
  35. package/build/components/virtualized-list/shared.d.ts +3 -74
  36. package/build/components/virtualized-list/shared.jsx +53 -752
  37. package/build/components/virtualized-list/sticky-binding.d.ts +13 -0
  38. package/build/components/virtualized-list/sticky-binding.js +111 -0
  39. package/build/components/virtualized-list/sticky-header.d.ts +1 -0
  40. package/build/components/virtualized-list/sticky-header.jsx +11 -159
  41. package/build/components/virtualized-list/sticky-state.d.ts +14 -0
  42. package/build/components/virtualized-list/sticky-state.js +54 -0
  43. package/build/components/virtualized-list/use-list-driver.d.ts +22 -0
  44. package/build/components/virtualized-list/use-list-driver.js +128 -0
  45. package/build/components/virtualized-list/use-separators.d.ts +7 -0
  46. package/build/components/virtualized-list/use-separators.js +22 -0
  47. package/build/components/virtualized-list/virtualized-list-props.d.ts +86 -0
  48. package/build/components/virtualized-list/virtualized-list-props.js +53 -0
  49. package/build/components/virtualized-section-list/index.d.ts +22 -9
  50. package/build/components/virtualized-section-list/index.jsx +87 -115
  51. package/build/components/virtualized-section-list/section-item-cell.d.ts +18 -0
  52. package/build/components/virtualized-section-list/section-item-cell.jsx +37 -0
  53. package/build/components.d.ts +6 -1
  54. package/build/components.js +1 -0
  55. package/build/define-descriptor-component.d.ts +6 -0
  56. package/build/define-descriptor-component.js +10 -0
  57. package/build/define-optional-descriptor-component.d.ts +3 -0
  58. package/build/define-optional-descriptor-component.js +12 -0
  59. package/build/descriptor-to-solid.d.ts +2 -1
  60. package/build/descriptor-to-solid.js +7 -3
  61. package/build/index.d.ts +13 -4
  62. package/build/index.js +24 -37
  63. package/build/jsx-runtime.d.ts +2 -0
  64. package/build/modules/animated/index.d.ts +54 -41
  65. package/build/modules/animated/index.js +2 -26
  66. package/build/modules/app-registry/index.d.ts +3 -3
  67. package/build/modules/app-registry/index.js +2 -2
  68. package/build/modules/status-bar.js +14 -27
  69. package/build/primitives/create-color-scheme.d.ts +1 -1
  70. package/build/primitives/define-native-view-component.d.ts +5 -0
  71. package/build/primitives/define-native-view-component.js +22 -0
  72. package/build/primitives/use-event.d.ts +6 -0
  73. package/build/primitives/use-event.js +18 -0
  74. 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';
@@ -29,5 +33,6 @@ export { TouchableNativeFeedback } from '@symbiote-native/components';
29
33
  export type { INativeFeedbackBackground, IThemeAttrBackground, IRippleBackground, } from '@symbiote-native/components';
30
34
  export type { ITouchableNativeFeedbackProps } from './components/touchable-native-feedback/touchable-native-feedback-props';
31
35
  export type { IButtonProps } from './components/button-props';
36
+ export type { ICheckboxProps } from './components/checkbox-props';
32
37
  export type { IImageBackgroundProps } from './components/image-background-props';
33
38
  export type { IInputAccessoryViewProps } from './components/input-accessory-view-props';
@@ -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
@@ -0,0 +1,6 @@
1
+ import type { JSX } from 'solid-js';
2
+ import type { IDescriptor } from '@symbiote-native/components';
3
+ import type { ISymbioteNode } from '@symbiote-native/engine';
4
+ export declare function defineDescriptorComponent<P extends {
5
+ children?: JSX.Element;
6
+ }>(render: (props: object) => IDescriptor): (props: P) => ISymbioteNode;
@@ -0,0 +1,10 @@
1
+ import { splitProps } from 'solid-js';
2
+ import { descriptorToSolid } from './descriptor-to-solid.js';
3
+ // A component whose host is one shared render function: children are the caller's own and go
4
+ // inside the host, every other prop reaches the render function
5
+ export function defineDescriptorComponent(render) {
6
+ return props => {
7
+ const [, rest] = splitProps(props, ['children']);
8
+ return descriptorToSolid(() => render({ ...rest }), () => props.children);
9
+ };
10
+ }
@@ -0,0 +1,3 @@
1
+ import type { IDescriptor } from '@symbiote-native/components';
2
+ import type { ISymbioteNode } from '@symbiote-native/engine';
3
+ export declare function defineOptionalDescriptorComponent<P extends object>(render: (props: object) => IDescriptor | null): (props: P) => ISymbioteNode | null;
@@ -0,0 +1,12 @@
1
+ import { descriptorToSolid } from './descriptor-to-solid.js';
2
+ // A childless component whose render function may answer `null`, for a native view that exists
3
+ // on one platform only. The answer is decided once at mount: the bridge builds the node once
4
+ // and then only diffs prop values, so the descriptor's shape must not change afterwards
5
+ export function defineOptionalDescriptorComponent(render) {
6
+ return props => {
7
+ const initial = render({ ...props });
8
+ if (!initial)
9
+ return null;
10
+ return descriptorToSolid(() => render({ ...props }) ?? initial);
11
+ };
12
+ }
@@ -1,3 +1,4 @@
1
+ import { type JSX } from 'solid-js';
1
2
  import { type ISymbioteNode } from '@symbiote-native/engine';
2
3
  import type { IDescriptor } from '@symbiote-native/components';
3
- export declare function descriptorToSolid(descriptor: () => IDescriptor): ISymbioteNode;
4
+ export declare function descriptorToSolid(descriptor: () => IDescriptor, children?: () => JSX.Element): ISymbioteNode;
@@ -23,7 +23,7 @@
23
23
  import { createMemo, createRenderEffect } from 'solid-js';
24
24
  import { isSymbioteNode } from '@symbiote-native/engine';
25
25
  import { createDescriptorShapeGuard } from '@symbiote-native/components';
26
- import { createElement, createTextNode, insertNode, replaceText, spread, } from './renderer.js';
26
+ import { createElement, createTextNode, insert, insertNode, replaceText, spread, } from './renderer.js';
27
27
  // The vanished-key widening the render effect below stands on. Shared with the host primitives
28
28
  // (components/view, components/text), which fold their props through the same two-branch
29
29
  // resolveAccessibilityProps — see that module's header.
@@ -86,6 +86,10 @@ function mountChildren(parent, children) {
86
86
  }
87
87
  // The memo is what keeps the render fn running ONCE per change no matter how many accessors read
88
88
  // it — the root's props, every child's props, every text child all derive from the same call.
89
- export function descriptorToSolid(descriptor) {
90
- return buildNode(createMemo(descriptor));
89
+ // `children` is a component's own slot, inserted after the descriptor's children
90
+ export function descriptorToSolid(descriptor, children) {
91
+ const root = buildNode(createMemo(descriptor));
92
+ if (children)
93
+ insert(root, children);
94
+ return root;
91
95
  }
package/build/index.d.ts CHANGED
@@ -6,8 +6,10 @@ export * from './components';
6
6
  export type { IAccessibilityProps, IAriaProps, IAccessibilityRole, IRole, IAccessibilityStateValue, IAccessibilityValue, IAccessibilityActionInfo, IResponderProps, IImageSource, IImageSourceProp, IResizeMode, IImageSize, IImageCacheStatus, IEnterKeyHint, IInputMode, ISubmitBehavior, } from '@symbiote-native/components';
7
7
  export { setImageSourceResolver } from '@symbiote-native/components';
8
8
  export { descriptorToSolid } from './descriptor-to-solid';
9
- export { AppRegistry, setHostRegistrar } from './modules/app-registry';
10
- export type { IComponentProvider, IWrapperComponentProvider, IAppParameters, IRunnable, IHostRegistrar, IRegistry, IHeadlessTask, ITaskProvider, ITaskCanceller, ITaskCancelProvider, } from './modules/app-registry';
9
+ export { defineDescriptorComponent } from './define-descriptor-component';
10
+ export { defineOptionalDescriptorComponent } from './define-optional-descriptor-component';
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';
11
13
  export { StatusBar } from './modules/status-bar';
12
14
  export type { IStatusBarProps, IStatusBarStyle } from './modules/status-bar';
13
15
  export { Image } from './modules/image';
@@ -17,19 +19,26 @@ export { createTunnel } from './create-tunnel';
17
19
  export type { ITunnel, ITunnelInProps } from './create-tunnel';
18
20
  export { createColorScheme } from './primitives/create-color-scheme';
19
21
  export { createWindowDimensions } from './primitives/create-window-dimensions';
22
+ export { createAnimatedColor, createAnimatedValue, createAnimatedValueXY, } from '@symbiote-native/engine';
20
23
  export { createPermissionHook } from './primitives/create-permission-hook';
21
24
  export { createResourceHook } from './primitives/create-resource-hook';
25
+ export { defineNativeViewComponent } from './primitives/define-native-view-component';
22
26
  export { createEventValueHook } from './primitives/create-event-value-hook';
27
+ export { useEvent, useEventListener } from './primitives/use-event';
23
28
  export { Animated, createAnimatedComponent } from './modules/animated';
24
29
  export type { IAnimatedComponentProps } from './modules/animated';
25
30
  export { Platform, StyleSheet, PixelRatio, PlatformColor, DynamicColorIOS, processColor, } from '@symbiote-native/engine';
26
- export { setNativeViewConfigSource, setColorProcessor, setDeviceEventSource, } from '@symbiote-native/engine';
31
+ export { setNativeViewConfigSource, setColorProcessor, } from '@symbiote-native/engine';
27
32
  export { dlog, isDebug } from '@symbiote-native/engine';
28
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';
29
- 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';
30
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';
31
36
  export { PanResponder } from '@symbiote-native/engine';
32
37
  export type { IPanResponderGestureState, IPanResponderCallbacks, IGestureResponderHandlers, IPanResponderInstance, } from '@symbiote-native/engine';
33
38
  export { AccessibilityInfo } from '@symbiote-native/engine';
34
39
  export type { IAccessibilityChangeEvent, IAccessibilityChangeEventName, IAccessibilityChangeEventHandler, IAccessibilityAnnouncementFinishedEvent, IAnnounceForAccessibilityOptions, IAccessibilityEventType, } from '@symbiote-native/engine';
35
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';