@krapwoo/ds-viewer 0.14.2 → 0.15.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.
- package/README.md +224 -2
- package/dist/cli/doctor.js +306 -17
- package/dist/cli/explain.js +26 -0
- package/dist/cli/staticPage.js +90 -6
- package/dist/cli/tokenDefinition.js +125 -0
- package/dist/native/catalog/catalogNavigation.js +27 -11
- package/dist/native/catalog/pageApi.js +557 -26
- package/dist/native/catalog/tokenDefinition.js +205 -0
- package/native/catalog/CatalogSearchInput.tsx +18 -3
- package/native/catalog/CatalogShell.tsx +318 -46
- package/native/catalog/CatalogSidebar.tsx +37 -1
- package/native/catalog/FamilyPage.tsx +567 -137
- package/native/catalog/GroupPage.tsx +230 -0
- package/native/catalog/JumpRow.tsx +81 -0
- package/native/catalog/ReferenceDetails.tsx +144 -16
- package/native/catalog/SectionBlock.tsx +49 -5
- package/native/catalog/SourceReferencePicker.tsx +180 -0
- package/native/catalog/StructuredTokenPage.tsx +592 -0
- package/native/catalog/Swatch.tsx +29 -2
- package/native/catalog/TokenLayouts.tsx +30 -9
- package/native/catalog/catalogNavigation.ts +30 -10
- package/native/catalog/catalogReferences.ts +212 -0
- package/native/catalog/familyModel.ts +10 -4
- package/native/catalog/index.ts +22 -0
- package/native/catalog/pageApi.ts +595 -23
- package/native/catalog/tokenDefinition.ts +430 -0
- package/native/catalog/types.ts +77 -5
- package/package.json +1 -1
|
@@ -5,10 +5,12 @@ import { ComparisonGrid } from './ComparisonGrid';
|
|
|
5
5
|
import { ComparisonGroups } from './ComparisonGroups';
|
|
6
6
|
import { ComparisonList } from './ComparisonList';
|
|
7
7
|
import { ReferenceDetails } from './ReferenceDetails';
|
|
8
|
+
import type { ReferenceNavigation } from './catalogReferences';
|
|
8
9
|
import { SpecimenSurface } from './SpecimenSurface';
|
|
9
10
|
import { SpecimenAddressProvider } from './SpecimenAddress';
|
|
10
11
|
import { DEFAULT_SPECIMEN_SURFACE } from './specimenSurfaceStyle';
|
|
11
12
|
import { TokenSections } from './TokenLayouts';
|
|
13
|
+
import { TokenDefinitionBlock } from './StructuredTokenPage';
|
|
12
14
|
import { MATRIX_LAYOUT, PREVIEW_CARD_CHROME, blockMinWidth, choosePlacement, presentationBlocks, type PlacementBlock, type PresentationBlock } from './comparison';
|
|
13
15
|
import type { PreviewWidths, SectionDef, SpecimenSurfaceKind } from './types';
|
|
14
16
|
|
|
@@ -164,7 +166,18 @@ function TablePreview({ render, widths, surface }: { render: () => React.ReactNo
|
|
|
164
166
|
);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
function BlockContent<TId extends string>({
|
|
169
|
+
function BlockContent<TId extends string>({
|
|
170
|
+
block, def, surface, focusSectionId, focusNonce,
|
|
171
|
+
}: {
|
|
172
|
+
block: PresentationBlock;
|
|
173
|
+
def: SectionDef<TId>;
|
|
174
|
+
surface: SpecimenSurfaceKind;
|
|
175
|
+
/** #64 B5: a tokenDefinition page's own section to scroll into view on arrival (a section-alias
|
|
176
|
+
* route) — only ever consumed by the 'tokenSections' case below; every other block kind ignores
|
|
177
|
+
* it. */
|
|
178
|
+
focusSectionId?: string;
|
|
179
|
+
focusNonce?: number;
|
|
180
|
+
}) {
|
|
168
181
|
switch (block.kind) {
|
|
169
182
|
case 'grid':
|
|
170
183
|
return <ComparisonGrid def={block.comparison} size={block.size} sectionId={def.id} surface={surface} />;
|
|
@@ -182,8 +195,18 @@ function BlockContent<TId extends string>({ block, def, surface }: { block: Pres
|
|
|
182
195
|
<Preview render={def.render} widths={block.widths} surface={def.tokenGallery ? undefined : surface} layout={def.previewLayout === 'table' ? 'table' : undefined} />
|
|
183
196
|
</SpecimenAddressProvider>
|
|
184
197
|
) : null;
|
|
185
|
-
case 'tokenSections':
|
|
198
|
+
case 'tokenSections': {
|
|
199
|
+
// #61: a page's own `tokenDefinition` (when set) carries the real entry data —
|
|
200
|
+
// `buildCatalogSections` only ever derives `block.sections` from it as placeholder geometry
|
|
201
|
+
// (`render: () => null`), so the real rendering happens here instead, at this file's own
|
|
202
|
+
// existing JSX boundary. #64 B2/B5: `TokenDefinitionBlock` additionally renders the jump row
|
|
203
|
+
// and consumes a pending section arrival; a legacy `tokenSections` page (no `tokenDefinition`)
|
|
204
|
+
// keeps the plain `TokenSections` rendering, unchanged.
|
|
205
|
+
if (def.tokenDefinition) {
|
|
206
|
+
return <TokenDefinitionBlock definition={def.tokenDefinition} columns={block.columns} label={def.id} focusSectionId={focusSectionId} focusNonce={focusNonce} />;
|
|
207
|
+
}
|
|
186
208
|
return <TokenSections sections={block.sections} columns={block.columns} label={def.id} />;
|
|
209
|
+
}
|
|
187
210
|
default:
|
|
188
211
|
return (
|
|
189
212
|
<View style={styles.previewCard}>
|
|
@@ -229,6 +252,8 @@ export function Blocks<TId extends string>({
|
|
|
229
252
|
surface,
|
|
230
253
|
showLabels = true,
|
|
231
254
|
trimOuterMargin = false,
|
|
255
|
+
focusSectionId,
|
|
256
|
+
focusNonce,
|
|
232
257
|
}: {
|
|
233
258
|
blocks: PresentationBlock[];
|
|
234
259
|
def: SectionDef<TId>;
|
|
@@ -242,6 +267,10 @@ export function Blocks<TId extends string>({
|
|
|
242
267
|
* two never double-stack. A page's own `SectionBlock` keeps the margin (the default,
|
|
243
268
|
* unchanged). */
|
|
244
269
|
trimOuterMargin?: boolean;
|
|
270
|
+
/** #64 B5: forwarded to `BlockContent`'s own 'tokenSections' case only — every other block kind
|
|
271
|
+
* ignores it. */
|
|
272
|
+
focusSectionId?: string;
|
|
273
|
+
focusNonce?: number;
|
|
245
274
|
}) {
|
|
246
275
|
const [available, setAvailable] = useState(0);
|
|
247
276
|
const [heights, setHeights] = useState<number[]>([]);
|
|
@@ -297,7 +326,7 @@ export function Blocks<TId extends string>({
|
|
|
297
326
|
carrying one shows its (already informative) title on a family page, which otherwise
|
|
298
327
|
hides every generic block label via `showLabels={false}`. */}
|
|
299
328
|
{(showLabels && block.title) || ('axis' in block && block.axis) ? <Text style={styles.blockLabel}>{('axis' in block && block.axis) || block.title}</Text> : null}
|
|
300
|
-
<BlockContent block={block} def={def} surface={surface} />
|
|
329
|
+
<BlockContent block={block} def={def} surface={surface} focusSectionId={focusSectionId} focusNonce={focusNonce} />
|
|
301
330
|
</View>
|
|
302
331
|
))}
|
|
303
332
|
</View>
|
|
@@ -319,6 +348,9 @@ export function SectionBlock<TId extends string>({
|
|
|
319
348
|
headingRef,
|
|
320
349
|
headingLevel = 1,
|
|
321
350
|
defaultPreviewWidths,
|
|
351
|
+
navigation,
|
|
352
|
+
focusSectionId,
|
|
353
|
+
focusNonce,
|
|
322
354
|
}: {
|
|
323
355
|
def: SectionDef<TId>;
|
|
324
356
|
groupLabel?: string;
|
|
@@ -331,6 +363,18 @@ export function SectionBlock<TId extends string>({
|
|
|
331
363
|
headingLevel?: 1 | 2;
|
|
332
364
|
/** Preview widths for pages without `previewWidths` (CatalogShell passes its catalog default). */
|
|
333
365
|
defaultPreviewWidths?: PreviewWidths;
|
|
366
|
+
/** Task 2B2: `CatalogShell`'s own shared reference navigation, passed straight through to
|
|
367
|
+
* `ReferenceDetails` so a resolvable Composition name becomes a real canonical link. Omitted for
|
|
368
|
+
* a standalone-mounted `SectionBlock` (this framework's own catalog demo included), which then
|
|
369
|
+
* keeps its prior, unlinked rendering — see `ReferenceDetails`'s own doc comment. */
|
|
370
|
+
navigation?: ReferenceNavigation<TId>;
|
|
371
|
+
/** #64 B5: a tokenDefinition page's own section to scroll into view on arrival — set by
|
|
372
|
+
* `CatalogShell` from `resolveRoute`'s own `sectionId` (a section-alias route). Omitted for a
|
|
373
|
+
* standalone-mounted `SectionBlock`, which then never auto-scrolls to a section. */
|
|
374
|
+
focusSectionId?: string;
|
|
375
|
+
/** Bumped by `CatalogShell` on every activation, even a repeat one — the same
|
|
376
|
+
* `focusMemberId`/`focusNonce` convention `FamilyPage` already uses. */
|
|
377
|
+
focusNonce?: number;
|
|
334
378
|
}) {
|
|
335
379
|
checkCompleteness(def);
|
|
336
380
|
// Memoized so Blocks' placement effect runs on real changes, not on every render.
|
|
@@ -361,9 +405,9 @@ export function SectionBlock<TId extends string>({
|
|
|
361
405
|
{pager && <SectionPagerRow pager={pager} />}
|
|
362
406
|
</View>
|
|
363
407
|
|
|
364
|
-
{blocks.length > 0 && <Blocks blocks={blocks} def={def} surface={surface} />}
|
|
408
|
+
{blocks.length > 0 && <Blocks blocks={blocks} def={def} surface={surface} focusSectionId={focusSectionId} focusNonce={focusNonce} />}
|
|
365
409
|
|
|
366
|
-
<ReferenceDetails def={def} />
|
|
410
|
+
<ReferenceDetails def={def} navigation={navigation} />
|
|
367
411
|
</View>
|
|
368
412
|
);
|
|
369
413
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { Modal, Pressable, ScrollView, StyleSheet, Text, TextInput, View, type TextStyle } from 'react-native';
|
|
3
|
+
import { CATALOG_COLOR, CATALOG_LAYOUT, CATALOG_RADIUS, CATALOG_SPACE, CATALOG_TEXT } from './tokens';
|
|
4
|
+
import { CatalogSearchInput } from './CatalogSearchInput';
|
|
5
|
+
import { CatalogButton } from './CatalogButton';
|
|
6
|
+
import { hashForId, sortIds } from './catalogNavigation';
|
|
7
|
+
import { searchReferenceIndex, type ReferenceEntry } from './catalogReferences';
|
|
8
|
+
|
|
9
|
+
// react-native-web reads `aria-level`; React Native's own prop types do not declare it (same
|
|
10
|
+
// convention `ReferenceDetails.tsx`'s own `HEADING_LEVEL_2` already uses).
|
|
11
|
+
const HEADING_LEVEL_2 = { 'aria-level': 2 } as Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
function entryKey<TId extends string>(entry: ReferenceEntry<TId>): string {
|
|
14
|
+
return entry.memberId ?? entry.pageId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A–Z by displayed title, deterministic id ties — the catalog's own canonical comparator
|
|
18
|
+
* (`catalogNavigation.ts`'s `sortIds`), never a second, bespoke sort here. `entryKey` is safe as
|
|
19
|
+
* `sortIds`'s own id: `validateFamily` already enforces that every page id, member id, and alias
|
|
20
|
+
* shares one catalog-wide-unique namespace. */
|
|
21
|
+
function sortedEntries<TId extends string>(entries: readonly ReferenceEntry<TId>[]): ReferenceEntry<TId>[] {
|
|
22
|
+
const byKey = new Map(entries.map((entry) => [entryKey(entry), entry]));
|
|
23
|
+
const labels = new Map(entries.map((entry) => [entryKey(entry), entry.title]));
|
|
24
|
+
return sortIds([...byKey.keys()], labels).map((key) => byKey.get(key)!);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** A modified click (open in a new tab/window) or a non-primary button must reach the browser's
|
|
28
|
+
* own anchor handling untouched — only a plain primary click/Enter/tap becomes a selection. */
|
|
29
|
+
function isModifiedPress(event: unknown): boolean {
|
|
30
|
+
const e = event as { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean; button?: number } | null | undefined;
|
|
31
|
+
return Boolean(e?.metaKey || e?.ctrlKey || e?.shiftKey || (typeof e?.button === 'number' && e.button !== 0));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ResultRow<TId extends string>({
|
|
35
|
+
entry, label, onSelect,
|
|
36
|
+
}: {
|
|
37
|
+
entry: ReferenceEntry<TId>;
|
|
38
|
+
label: string;
|
|
39
|
+
onSelect: (entry: ReferenceEntry<TId>) => void;
|
|
40
|
+
}) {
|
|
41
|
+
const [hovered, setHovered] = useState(false);
|
|
42
|
+
const [focused, setFocused] = useState(false);
|
|
43
|
+
return (
|
|
44
|
+
<Pressable
|
|
45
|
+
onPress={(event) => {
|
|
46
|
+
if (isModifiedPress(event)) return; // let the browser open it in a new tab/window
|
|
47
|
+
(event as { preventDefault?: () => void } | undefined)?.preventDefault?.();
|
|
48
|
+
onSelect(entry);
|
|
49
|
+
}}
|
|
50
|
+
onHoverIn={() => setHovered(true)}
|
|
51
|
+
onHoverOut={() => setHovered(false)}
|
|
52
|
+
onFocus={() => setFocused(true)}
|
|
53
|
+
onBlur={() => setFocused(false)}
|
|
54
|
+
accessibilityRole="link"
|
|
55
|
+
// A real `<a href="#Id">` on web — Enter activates it, and copy/modified-click work exactly
|
|
56
|
+
// as every other catalog link already does (`CatalogSidebar.tsx`'s own `NavItem`).
|
|
57
|
+
{...({ href: hashForId(entryKey(entry)) } as Record<string, unknown>)}
|
|
58
|
+
style={({ pressed }) => [styles.row, (pressed || hovered) && styles.rowHover, focused && styles.focusRing]}
|
|
59
|
+
>
|
|
60
|
+
<Text style={styles.rowTitle}>{label}</Text>
|
|
61
|
+
<Text style={styles.rowMeta}>{entry.component ? `${entry.component} · ${entry.source ?? entry.path}` : (entry.source ?? entry.path)}</Text>
|
|
62
|
+
{entry.source && entry.source !== entry.path ? <Text style={styles.rowMeta}>{`Implementation: ${entry.path}`}</Text> : null}
|
|
63
|
+
</Pressable>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface SourceReferencePickerProps<TId extends string = string> {
|
|
68
|
+
/** Controlled — the parent (its Task 2B `CatalogShell` caller) owns open/close state. */
|
|
69
|
+
visible: boolean;
|
|
70
|
+
/** The catalog-wide index to search (`catalogReferences.ts`'s own `buildReferenceIndex`). */
|
|
71
|
+
entries: readonly ReferenceEntry<TId>[];
|
|
72
|
+
/** Escape, the native request-close (Android back / RNW's own Escape handling), or the explicit
|
|
73
|
+
* Close control — dismissal with no selection. */
|
|
74
|
+
onDismiss: () => void;
|
|
75
|
+
/** A plain click/Enter/tap on one result. This leaf never closes itself or navigates on
|
|
76
|
+
* selection — the parent owns what happens next (closing the picker, updating the route, and
|
|
77
|
+
* focusing the destination); see this file's own module doc comment. */
|
|
78
|
+
onSelect: (entry: ReferenceEntry<TId>) => void;
|
|
79
|
+
/** Durable, visibly associated label for the search field (`CatalogSearchInput`'s own
|
|
80
|
+
* `accessibilityLabel`). @default 'Search source references' */
|
|
81
|
+
searchLabel?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* #60's catalog-wide Source reference picker (plan Task 2, "the accepted A access model"): a
|
|
86
|
+
* controlled modal searching every registered page/member address (`ReferenceEntry[]`), never a
|
|
87
|
+
* page-local member search. Built entirely from existing catalog primitives (`Modal`,
|
|
88
|
+
* `CatalogSearchInput`, `CatalogButton`, the same link/row conventions `CatalogSidebar.tsx`
|
|
89
|
+
* already uses) and catalog tokens — no new dependency, theme, or layout system.
|
|
90
|
+
*
|
|
91
|
+
* This component owns none of the application's navigation/history: `onSelect` only reports a
|
|
92
|
+
* choice, and closing (`visible`) stays fully under the parent's control, so selecting never
|
|
93
|
+
* races this component's own close against whatever focus move the parent makes afterwards. Its
|
|
94
|
+
* real `CatalogShell` caller, wiring `visible`/`onSelect` to an actual route change, is Task 2B —
|
|
95
|
+
* this file and its own self-catalog demo are available infrastructure, not yet adopted product
|
|
96
|
+
* behavior. Keyboard focus trapping and restoring the initiating control on cancellation reuse
|
|
97
|
+
* `Modal`'s own built-in behavior (react-native-web's `ModalFocusTrap`) rather than a second,
|
|
98
|
+
* hand-built dialog engine.
|
|
99
|
+
*/
|
|
100
|
+
export function SourceReferencePicker<TId extends string = string>({
|
|
101
|
+
visible, entries, onDismiss, onSelect, searchLabel = 'Search source references',
|
|
102
|
+
}: SourceReferencePickerProps<TId>) {
|
|
103
|
+
const [query, setQuery] = useState('');
|
|
104
|
+
const searchInputRef = useRef<TextInput>(null);
|
|
105
|
+
const focusSearch = useCallback(() => searchInputRef.current?.focus(), []);
|
|
106
|
+
// Let Modal capture the trigger before moving focus inside. Mount-time autoFocus would
|
|
107
|
+
// instead make its focus-restoration effect remember the search field that is being removed.
|
|
108
|
+
// A member row's own title is just its id (`buildReferenceIndex`'s own convention) — this looks
|
|
109
|
+
// up the family's displayed title for the "<id> — in <family title>" line below, the same
|
|
110
|
+
// wording `CatalogSidebar.tsx`'s own `MemberResultItem` already uses for a member search hit.
|
|
111
|
+
const pageTitleByPageId = useMemo(
|
|
112
|
+
() => new Map(entries.filter((entry) => entry.memberId === undefined).map((entry) => [entry.pageId, entry.title])),
|
|
113
|
+
[entries],
|
|
114
|
+
);
|
|
115
|
+
const trimmedQuery = query.trim();
|
|
116
|
+
const results = useMemo(
|
|
117
|
+
() => sortedEntries(trimmedQuery ? searchReferenceIndex(entries, trimmedQuery) : entries),
|
|
118
|
+
[entries, trimmedQuery],
|
|
119
|
+
);
|
|
120
|
+
return (
|
|
121
|
+
<Modal visible={visible} transparent animationType="none" accessibilityLabel="Source reference" onRequestClose={onDismiss} onShow={focusSearch}>
|
|
122
|
+
<View style={styles.scrim}>
|
|
123
|
+
<View style={styles.dialog}>
|
|
124
|
+
<View style={styles.headerRow}>
|
|
125
|
+
<Text role="heading" {...HEADING_LEVEL_2} style={styles.heading}>Source reference</Text>
|
|
126
|
+
<CatalogButton label="Close" variant="secondary" accessibilityLabel="Close source reference picker" onPress={onDismiss} />
|
|
127
|
+
</View>
|
|
128
|
+
<Text style={styles.searchLabel}>{searchLabel}</Text>
|
|
129
|
+
<CatalogSearchInput
|
|
130
|
+
value={query}
|
|
131
|
+
onChangeText={setQuery}
|
|
132
|
+
placeholder="Search by name, component, alias, or path"
|
|
133
|
+
accessibilityLabel={searchLabel}
|
|
134
|
+
inputRef={searchInputRef}
|
|
135
|
+
/>
|
|
136
|
+
<ScrollView style={styles.list} accessibilityRole="list">
|
|
137
|
+
{entries.length === 0 ? (
|
|
138
|
+
<Text style={styles.empty}>No source references are registered.</Text>
|
|
139
|
+
) : results.length === 0 ? (
|
|
140
|
+
<Text style={styles.empty}>{`No matches for "${trimmedQuery}".`}</Text>
|
|
141
|
+
) : (
|
|
142
|
+
results.map((entry) => {
|
|
143
|
+
const label = entry.memberId
|
|
144
|
+
? `${entry.title} — in ${pageTitleByPageId.get(entry.pageId) ?? entry.pageId}`
|
|
145
|
+
: entry.title;
|
|
146
|
+
return <View key={entryKey(entry)} role="listitem"><ResultRow entry={entry} label={label} onSelect={onSelect} /></View>;
|
|
147
|
+
})
|
|
148
|
+
)}
|
|
149
|
+
</ScrollView>
|
|
150
|
+
</View>
|
|
151
|
+
</View>
|
|
152
|
+
</Modal>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const styles = StyleSheet.create({
|
|
157
|
+
// No existing catalog token covers a scrim or a modal dialog's own bounds — both are
|
|
158
|
+
// component-local, the same way `OverlayDemo.catalog.tsx`'s own demo scrim already is.
|
|
159
|
+
scrim: {
|
|
160
|
+
flex: 1, backgroundColor: 'rgba(0,0,0,0.35)', alignItems: 'center', justifyContent: 'center', padding: CATALOG_SPACE.xl,
|
|
161
|
+
},
|
|
162
|
+
dialog: {
|
|
163
|
+
width: '100%', maxWidth: 480, maxHeight: '80%',
|
|
164
|
+
backgroundColor: CATALOG_COLOR.surface, borderRadius: CATALOG_RADIUS.card,
|
|
165
|
+
padding: CATALOG_LAYOUT.panelPadding, gap: CATALOG_SPACE.sm,
|
|
166
|
+
},
|
|
167
|
+
headerRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: CATALOG_SPACE.md },
|
|
168
|
+
heading: { ...CATALOG_TEXT.label, color: CATALOG_COLOR.text },
|
|
169
|
+
searchLabel: { ...CATALOG_TEXT.captionStrong, color: CATALOG_COLOR.text },
|
|
170
|
+
list: { marginTop: CATALOG_SPACE.xs },
|
|
171
|
+
row: { paddingVertical: CATALOG_SPACE.sm, paddingHorizontal: CATALOG_SPACE.sm, borderRadius: CATALOG_RADIUS.control, gap: 2, minHeight: CATALOG_LAYOUT.controlSize },
|
|
172
|
+
rowHover: { backgroundColor: CATALOG_COLOR.surfacePressed },
|
|
173
|
+
focusRing: { outlineWidth: CATALOG_LAYOUT.focusRingWidth, outlineStyle: 'solid', outlineColor: CATALOG_COLOR.focusRing },
|
|
174
|
+
rowTitle: { ...CATALOG_TEXT.bodyStrong, color: CATALOG_COLOR.text },
|
|
175
|
+
// A real source path never truncates and may have no spaces to wrap at — `wordBreak` isn't in
|
|
176
|
+
// the installed (React Native, not RNW) `TextStyle` type, the same cast `ReferenceDetails.tsx`'s
|
|
177
|
+
// own `mono` style already needs.
|
|
178
|
+
rowMeta: { ...CATALOG_TEXT.codeSmall, color: CATALOG_COLOR.textMuted, wordBreak: 'break-word' } as TextStyle,
|
|
179
|
+
empty: { ...CATALOG_TEXT.caption, color: CATALOG_COLOR.textMuted, paddingVertical: CATALOG_SPACE.md },
|
|
180
|
+
});
|