panelui-native 0.46.0 → 0.51.1
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 +12 -1
- package/lib/module/components/accordion/index.js +32 -4
- package/lib/module/components/accordion/index.js.map +1 -1
- package/lib/module/components/button/index.js +83 -15
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/button-group/index.js +186 -0
- package/lib/module/components/button-group/index.js.map +1 -0
- package/lib/module/components/color-picker/index.js +110 -1
- package/lib/module/components/color-picker/index.js.map +1 -1
- package/lib/module/components/date-time-picker/index.js +23 -5
- package/lib/module/components/date-time-picker/index.js.map +1 -1
- package/lib/module/components/fab/index.js +514 -0
- package/lib/module/components/fab/index.js.map +1 -0
- package/lib/module/components/markdown-editor/index.js +406 -0
- package/lib/module/components/markdown-editor/index.js.map +1 -0
- package/lib/module/components/markdown-editor/markdown-transforms.js +243 -0
- package/lib/module/components/markdown-editor/markdown-transforms.js.map +1 -0
- package/lib/module/components/questionnaire/index.js +1312 -0
- package/lib/module/components/questionnaire/index.js.map +1 -0
- package/lib/module/components/sortable/index.js +806 -0
- package/lib/module/components/sortable/index.js.map +1 -0
- package/lib/module/components/swipe/index.js +140 -5
- package/lib/module/components/swipe/index.js.map +1 -1
- package/lib/module/components/tabs/index.js +94 -18
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/time-picker/index.js +34 -6
- package/lib/module/components/time-picker/index.js.map +1 -1
- package/lib/module/components/tree/index.js +500 -0
- package/lib/module/components/tree/index.js.map +1 -0
- package/lib/module/icons/index.js +246 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +8 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/haptics.js +19 -0
- package/lib/module/utils/haptics.js.map +1 -1
- package/lib/typescript/src/components/accordion/index.d.ts +21 -0
- package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +21 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button-group/index.d.ts +212 -0
- package/lib/typescript/src/components/button-group/index.d.ts.map +1 -0
- package/lib/typescript/src/components/color-picker/index.d.ts +82 -1
- package/lib/typescript/src/components/color-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/date-time-picker/index.d.ts +7 -1
- package/lib/typescript/src/components/date-time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/fab/index.d.ts +285 -0
- package/lib/typescript/src/components/fab/index.d.ts.map +1 -0
- package/lib/typescript/src/components/markdown-editor/index.d.ts +102 -0
- package/lib/typescript/src/components/markdown-editor/index.d.ts.map +1 -0
- package/lib/typescript/src/components/markdown-editor/markdown-transforms.d.ts +76 -0
- package/lib/typescript/src/components/markdown-editor/markdown-transforms.d.ts.map +1 -0
- package/lib/typescript/src/components/questionnaire/index.d.ts +336 -0
- package/lib/typescript/src/components/questionnaire/index.d.ts.map +1 -0
- package/lib/typescript/src/components/sortable/index.d.ts +238 -0
- package/lib/typescript/src/components/sortable/index.d.ts.map +1 -0
- package/lib/typescript/src/components/swipe/index.d.ts +35 -0
- package/lib/typescript/src/components/swipe/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tabs/index.d.ts +9 -0
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/time-picker/index.d.ts +19 -1
- package/lib/typescript/src/components/time-picker/index.d.ts.map +1 -1
- package/lib/typescript/src/components/tree/index.d.ts +125 -0
- package/lib/typescript/src/components/tree/index.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +30 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +10 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/haptics.d.ts +14 -0
- package/lib/typescript/src/utils/haptics.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/accordion/index.tsx +48 -6
- package/src/components/button/index.tsx +97 -15
- package/src/components/button-group/index.tsx +199 -0
- package/src/components/color-picker/index.tsx +140 -3
- package/src/components/date-time-picker/index.tsx +39 -3
- package/src/components/fab/index.tsx +583 -0
- package/src/components/markdown-editor/index.tsx +526 -0
- package/src/components/markdown-editor/markdown-transforms.ts +228 -0
- package/src/components/questionnaire/index.tsx +1615 -0
- package/src/components/sortable/index.tsx +1080 -0
- package/src/components/swipe/index.tsx +165 -3
- package/src/components/tabs/index.tsx +100 -28
- package/src/components/time-picker/index.tsx +42 -6
- package/src/components/tree/index.tsx +564 -0
- package/src/icons/index.tsx +176 -0
- package/src/index.ts +89 -1
- package/src/utils/haptics.ts +21 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fab — the floating action button: one screen, one thing it is mostly for.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <Fab icon={<PlusIcon size={24} />} accessibilityLabel="New note" onPress={compose} />
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* ## It floats, which is the whole problem with it
|
|
9
|
+
*
|
|
10
|
+
* A button pinned over the content is a button covering some of it, and the
|
|
11
|
+
* bottom-right corner of a scrolling list is exactly where the last row goes. So
|
|
12
|
+
* a Fab is right when a screen has *one* action worth that trade and wrong when
|
|
13
|
+
* it has three — a corner with three buttons in it is a toolbar that has been
|
|
14
|
+
* put in the wrong place. Where a screen has several, the honest shapes are a
|
|
15
|
+
* `ButtonGroup` in a bar or, if one of them really does lead, this with a
|
|
16
|
+
* `Fab.Group` behind it.
|
|
17
|
+
*
|
|
18
|
+
* It positions itself absolutely against its nearest positioned ancestor, which
|
|
19
|
+
* on a screen means the screen. Give the content below it enough bottom padding
|
|
20
|
+
* to scroll clear — this cannot know how tall your list is, and a Fab sitting on
|
|
21
|
+
* the last row forever is the failure people actually hit.
|
|
22
|
+
*
|
|
23
|
+
* ## Extended, and when to bother
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* <Fab extended icon={<PencilIcon size={20} />}>Write</Fab>
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* A lone glyph is a guess unless the glyph is a plus. `extended` spells the
|
|
30
|
+
* action out, costs the width, and is worth it for anything a plus would not
|
|
31
|
+
* have said.
|
|
32
|
+
*
|
|
33
|
+
* ## The speed dial
|
|
34
|
+
*
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <Fab.Group icon={<PlusIcon size={24} />} accessibilityLabel="Add">
|
|
37
|
+
* <Fab.Action icon={<ImageIcon size={18} />} label="Photo" onPress={addPhoto} />
|
|
38
|
+
* <Fab.Action icon={<FileIcon size={18} />} label="File" onPress={addFile} />
|
|
39
|
+
* </Fab.Group>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* The actions come out of the button one after another rather than together —
|
|
43
|
+
* a stagger of a few frames each, which is enough to read as a list unfolding
|
|
44
|
+
* instead of a menu appearing. Every action carries its label beside it, because
|
|
45
|
+
* a column of unlabelled circles is a quiz.
|
|
46
|
+
*
|
|
47
|
+
* Opening also drops a scrim over the screen. Not for looks: an open dial is
|
|
48
|
+
* modal — the next tap either picks something or closes it — and a scrim is what
|
|
49
|
+
* says so, as well as what catches the tap that closes it.
|
|
50
|
+
*
|
|
51
|
+
* A group draws itself through a portal, above everything else — the scrim and
|
|
52
|
+
* the buttons together, so the backdrop cannot end up on top of the dial it is
|
|
53
|
+
* behind. That means its `offset` is measured from the screen's edges rather
|
|
54
|
+
* than from whatever it is written inside, which is what lets it be declared
|
|
55
|
+
* next to the content it belongs to instead of at the root of the screen.
|
|
56
|
+
*/
|
|
57
|
+
import { type ReactNode } from 'react';
|
|
58
|
+
import { View, type ViewProps } from 'react-native';
|
|
59
|
+
import { type VariantProps } from 'tailwind-variants';
|
|
60
|
+
/** Which corner the button sits in. */
|
|
61
|
+
export type FabPlacement = 'bottom-right' | 'bottom-center' | 'bottom-left';
|
|
62
|
+
declare const fabVariants: import("tailwind-variants").TVReturnType<{
|
|
63
|
+
size: {
|
|
64
|
+
sm: {
|
|
65
|
+
root: string;
|
|
66
|
+
label: string;
|
|
67
|
+
};
|
|
68
|
+
md: {
|
|
69
|
+
root: string;
|
|
70
|
+
label: string;
|
|
71
|
+
};
|
|
72
|
+
lg: {
|
|
73
|
+
root: string;
|
|
74
|
+
label: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
extended: {
|
|
78
|
+
true: {};
|
|
79
|
+
false: {};
|
|
80
|
+
};
|
|
81
|
+
variant: {
|
|
82
|
+
primary: {
|
|
83
|
+
root: string;
|
|
84
|
+
label: string;
|
|
85
|
+
};
|
|
86
|
+
secondary: {
|
|
87
|
+
root: string;
|
|
88
|
+
label: string;
|
|
89
|
+
};
|
|
90
|
+
surface: {
|
|
91
|
+
root: string;
|
|
92
|
+
label: string;
|
|
93
|
+
};
|
|
94
|
+
destructive: {
|
|
95
|
+
root: string;
|
|
96
|
+
label: string;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
disabled: {
|
|
100
|
+
true: {
|
|
101
|
+
root: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}, {
|
|
105
|
+
root: string;
|
|
106
|
+
label: string;
|
|
107
|
+
}, undefined, {
|
|
108
|
+
size: {
|
|
109
|
+
sm: {
|
|
110
|
+
root: string;
|
|
111
|
+
label: string;
|
|
112
|
+
};
|
|
113
|
+
md: {
|
|
114
|
+
root: string;
|
|
115
|
+
label: string;
|
|
116
|
+
};
|
|
117
|
+
lg: {
|
|
118
|
+
root: string;
|
|
119
|
+
label: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
extended: {
|
|
123
|
+
true: {};
|
|
124
|
+
false: {};
|
|
125
|
+
};
|
|
126
|
+
variant: {
|
|
127
|
+
primary: {
|
|
128
|
+
root: string;
|
|
129
|
+
label: string;
|
|
130
|
+
};
|
|
131
|
+
secondary: {
|
|
132
|
+
root: string;
|
|
133
|
+
label: string;
|
|
134
|
+
};
|
|
135
|
+
surface: {
|
|
136
|
+
root: string;
|
|
137
|
+
label: string;
|
|
138
|
+
};
|
|
139
|
+
destructive: {
|
|
140
|
+
root: string;
|
|
141
|
+
label: string;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
disabled: {
|
|
145
|
+
true: {
|
|
146
|
+
root: string;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
}, {
|
|
150
|
+
root: string;
|
|
151
|
+
label: string;
|
|
152
|
+
}, import("tailwind-variants").TVReturnType<{
|
|
153
|
+
size: {
|
|
154
|
+
sm: {
|
|
155
|
+
root: string;
|
|
156
|
+
label: string;
|
|
157
|
+
};
|
|
158
|
+
md: {
|
|
159
|
+
root: string;
|
|
160
|
+
label: string;
|
|
161
|
+
};
|
|
162
|
+
lg: {
|
|
163
|
+
root: string;
|
|
164
|
+
label: string;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
extended: {
|
|
168
|
+
true: {};
|
|
169
|
+
false: {};
|
|
170
|
+
};
|
|
171
|
+
variant: {
|
|
172
|
+
primary: {
|
|
173
|
+
root: string;
|
|
174
|
+
label: string;
|
|
175
|
+
};
|
|
176
|
+
secondary: {
|
|
177
|
+
root: string;
|
|
178
|
+
label: string;
|
|
179
|
+
};
|
|
180
|
+
surface: {
|
|
181
|
+
root: string;
|
|
182
|
+
label: string;
|
|
183
|
+
};
|
|
184
|
+
destructive: {
|
|
185
|
+
root: string;
|
|
186
|
+
label: string;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
disabled: {
|
|
190
|
+
true: {
|
|
191
|
+
root: string;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
}, {
|
|
195
|
+
root: string;
|
|
196
|
+
label: string;
|
|
197
|
+
}, undefined, unknown, unknown, undefined>>;
|
|
198
|
+
type FabVariantProps = VariantProps<typeof fabVariants>;
|
|
199
|
+
/** How big the button is. */
|
|
200
|
+
export type FabSize = NonNullable<FabVariantProps['size']>;
|
|
201
|
+
/** What the button is drawn in. */
|
|
202
|
+
export type FabVariant = NonNullable<FabVariantProps['variant']>;
|
|
203
|
+
export interface FabProps extends Omit<ViewProps, 'children'>, FabVariantProps {
|
|
204
|
+
className?: string;
|
|
205
|
+
/** The glyph. Sized by you — this is the one thing that should not guess. */
|
|
206
|
+
icon?: ReactNode;
|
|
207
|
+
/**
|
|
208
|
+
* The label, which turns the circle into a stadium. Needs `extended`; a
|
|
209
|
+
* label with nowhere to go is a label that gets clipped by the circle.
|
|
210
|
+
*/
|
|
211
|
+
children?: ReactNode;
|
|
212
|
+
/** Spell the action out beside the glyph. */
|
|
213
|
+
extended?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Pin it over the content, in a corner.
|
|
216
|
+
*
|
|
217
|
+
* Left out, it is an ordinary button in the flow — which is what you want
|
|
218
|
+
* inside a `Fab.Group`, or when the screen already has somewhere for it to
|
|
219
|
+
* sit.
|
|
220
|
+
*/
|
|
221
|
+
placement?: FabPlacement;
|
|
222
|
+
/** Distance from the edges when `placement` is set, in points. */
|
|
223
|
+
offset?: number;
|
|
224
|
+
onPress?: () => void;
|
|
225
|
+
disabled?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* A tick on press. Off by default — needs the optional `expo-haptics`, and
|
|
228
|
+
* is silent without it.
|
|
229
|
+
*/
|
|
230
|
+
haptics?: boolean;
|
|
231
|
+
/** Required for an icon-only button. A lone glyph reads out as nothing. */
|
|
232
|
+
accessibilityLabel?: string;
|
|
233
|
+
}
|
|
234
|
+
export interface FabGroupProps extends Omit<ViewProps, 'children'> {
|
|
235
|
+
className?: string;
|
|
236
|
+
/** The glyph on the trigger. */
|
|
237
|
+
icon?: ReactNode;
|
|
238
|
+
/** The trigger's label, if it should be extended while closed. */
|
|
239
|
+
label?: string;
|
|
240
|
+
/** `Fab.Action` children, in the order they should unfold. */
|
|
241
|
+
children?: ReactNode;
|
|
242
|
+
/** Controlled open state. */
|
|
243
|
+
open?: boolean;
|
|
244
|
+
onOpenChange?: (open: boolean) => void;
|
|
245
|
+
/** Which corner of the *screen* the whole dial parks in. */
|
|
246
|
+
placement?: FabPlacement;
|
|
247
|
+
/** Distance from the screen's edges, in points. Add your safe-area inset. */
|
|
248
|
+
offset?: number;
|
|
249
|
+
size?: FabSize;
|
|
250
|
+
variant?: FabVariant;
|
|
251
|
+
disabled?: boolean;
|
|
252
|
+
haptics?: boolean;
|
|
253
|
+
/** Frost the screen behind the open dial instead of dimming it. */
|
|
254
|
+
blur?: boolean;
|
|
255
|
+
/** Required — the trigger is a lone glyph until it is opened. */
|
|
256
|
+
accessibilityLabel?: string;
|
|
257
|
+
/**
|
|
258
|
+
* Turn the trigger's glyph a quarter circle while the dial is open.
|
|
259
|
+
*
|
|
260
|
+
* On by default, and it is doing real work when the glyph is a plus: the
|
|
261
|
+
* same mark becomes a cross, which says "this closes now" without a second
|
|
262
|
+
* icon that has to be swapped in. Turn it off for a glyph that means
|
|
263
|
+
* something at one angle only.
|
|
264
|
+
*/
|
|
265
|
+
rotateOnOpen?: boolean;
|
|
266
|
+
}
|
|
267
|
+
export interface FabActionProps extends Omit<ViewProps, 'children'> {
|
|
268
|
+
className?: string;
|
|
269
|
+
/** The glyph. */
|
|
270
|
+
icon?: ReactNode;
|
|
271
|
+
/** What it does, beside the glyph. A column of unlabelled circles is a quiz. */
|
|
272
|
+
label?: string;
|
|
273
|
+
onPress?: () => void;
|
|
274
|
+
disabled?: boolean;
|
|
275
|
+
/** Draws it in the destructive colour, for the one that removes something. */
|
|
276
|
+
destructive?: boolean;
|
|
277
|
+
/** Extra classes for the label chip. */
|
|
278
|
+
labelClassName?: string;
|
|
279
|
+
}
|
|
280
|
+
export declare const Fab: import("react").ForwardRefExoticComponent<FabProps & import("react").RefAttributes<View>> & {
|
|
281
|
+
Group: import("react").ForwardRefExoticComponent<FabGroupProps & import("react").RefAttributes<View>>;
|
|
282
|
+
Action: import("react").ForwardRefExoticComponent<FabActionProps & import("react").RefAttributes<View>>;
|
|
283
|
+
};
|
|
284
|
+
export {};
|
|
285
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/fab/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MarkdownEditor — a field for writing markdown, and a way to see it rendered.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* const [draft, setDraft] = useState('');
|
|
6
|
+
*
|
|
7
|
+
* <MarkdownEditor value={draft} onValueChange={setDraft} rows={12} />
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* ## Writing and reading are two modes, not two panes
|
|
11
|
+
*
|
|
12
|
+
* Side by side is how this is done on a desktop, and it does not survive the
|
|
13
|
+
* trip to a phone: two columns of a phone's width are two columns too narrow to
|
|
14
|
+
* read, and the keyboard covers the bottom half of the screen exactly when the
|
|
15
|
+
* writer is using it. So there is one pane and a switch — write, or read what
|
|
16
|
+
* you wrote. The toolbar carries the switch, because the toolbar is the one
|
|
17
|
+
* thing on screen in both modes.
|
|
18
|
+
*
|
|
19
|
+
* ## The toolbar edits the selection
|
|
20
|
+
*
|
|
21
|
+
* Every button here is a function of the text and where the caret is in it, and
|
|
22
|
+
* the answer that matters is not which characters get inserted but where the
|
|
23
|
+
* caret lands afterwards. Bolding a selected phrase leaves it selected, so it
|
|
24
|
+
* can be italicised next; bolding nothing puts the caret between the new
|
|
25
|
+
* markers, where the writing is about to go; and pressing the same button again
|
|
26
|
+
* takes the markers off. Those rules live in `markdown-transforms.ts` as pure
|
|
27
|
+
* functions over `(text, selection)`, away from anything to do with a field.
|
|
28
|
+
*
|
|
29
|
+
* ## Why it renders through Response
|
|
30
|
+
*
|
|
31
|
+
* The preview is `Response`, the same reader that renders a model's answer, so
|
|
32
|
+
* markdown means the same thing everywhere in the library and there is one
|
|
33
|
+
* parser to be right rather than two to keep in step. It renders through
|
|
34
|
+
* `Typography`, `CodeBlock` and `Table`, which is to say through the app's own
|
|
35
|
+
* type and colours — a preview that looked like a document viewer would be
|
|
36
|
+
* showing the writer something they are not going to ship.
|
|
37
|
+
*
|
|
38
|
+
* Nothing here reaches for a platform markdown renderer. `Text` in SwiftUI can
|
|
39
|
+
* parse markdown and Jetpack Compose's cannot, so a platform-backed editor
|
|
40
|
+
* would render on one of the two and be a plain string on the other.
|
|
41
|
+
*
|
|
42
|
+
* Works controlled (`value` + `onValueChange`) or uncontrolled (`defaultValue`).
|
|
43
|
+
*/
|
|
44
|
+
import { type ReactNode } from 'react';
|
|
45
|
+
import { View, type TextInput, type ViewProps } from 'react-native';
|
|
46
|
+
import { type TextareaProps } from '../textarea/index.js';
|
|
47
|
+
export type { EditorSelection, EditResult } from './markdown-transforms.js';
|
|
48
|
+
/** Which pane the editor is showing. */
|
|
49
|
+
export type MarkdownEditorMode = 'write' | 'preview';
|
|
50
|
+
/** A formatting action the toolbar can offer. */
|
|
51
|
+
export type MarkdownEditorAction = 'bold' | 'italic' | 'heading' | 'quote' | 'code' | 'bulletList' | 'orderedList' | 'link';
|
|
52
|
+
export interface MarkdownEditorProps extends Omit<ViewProps, 'children'> {
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Controlled text. Leave unset and pass `defaultValue` to run uncontrolled. */
|
|
55
|
+
value?: string;
|
|
56
|
+
/** Starting text when uncontrolled. */
|
|
57
|
+
defaultValue?: string;
|
|
58
|
+
onValueChange?: (value: string) => void;
|
|
59
|
+
/** Controlled pane. */
|
|
60
|
+
mode?: MarkdownEditorMode;
|
|
61
|
+
/** Starting pane when uncontrolled. */
|
|
62
|
+
defaultMode?: MarkdownEditorMode;
|
|
63
|
+
onModeChange?: (mode: MarkdownEditorMode) => void;
|
|
64
|
+
/** Stop the field being edited and the toolbar being pressed. */
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The parts, in the order they should stack. Left out, the editor draws its
|
|
68
|
+
* toolbar, its field and its preview in that order — which is the whole
|
|
69
|
+
* component, and the reason it usually needs no children.
|
|
70
|
+
*/
|
|
71
|
+
children?: ReactNode;
|
|
72
|
+
/** Forwarded to the field when the editor draws its own. */
|
|
73
|
+
placeholder?: string;
|
|
74
|
+
/** Height of the field, in lines. Forwarded to the field the editor draws. */
|
|
75
|
+
rows?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface MarkdownEditorToolbarProps extends Omit<ViewProps, 'children'> {
|
|
78
|
+
className?: string;
|
|
79
|
+
/** Which formatting actions to offer, in the order given. */
|
|
80
|
+
actions?: MarkdownEditorAction[];
|
|
81
|
+
/**
|
|
82
|
+
* Show the write/preview switch. On by default — a preview nobody can reach
|
|
83
|
+
* is a pane that does not exist.
|
|
84
|
+
*/
|
|
85
|
+
showModeSwitch?: boolean;
|
|
86
|
+
/** Anything else to put on the row: a word count, a save state, a submit. */
|
|
87
|
+
children?: ReactNode;
|
|
88
|
+
}
|
|
89
|
+
export interface MarkdownEditorInputProps extends Omit<TextareaProps, 'value' | 'onChangeText' | 'defaultValue'> {
|
|
90
|
+
className?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface MarkdownEditorPreviewProps extends Omit<ViewProps, 'children'> {
|
|
93
|
+
className?: string;
|
|
94
|
+
/** What to show when there is nothing written yet. */
|
|
95
|
+
emptyText?: string;
|
|
96
|
+
}
|
|
97
|
+
export declare const MarkdownEditor: import("react").ForwardRefExoticComponent<MarkdownEditorProps & import("react").RefAttributes<View>> & {
|
|
98
|
+
Toolbar: import("react").ForwardRefExoticComponent<MarkdownEditorToolbarProps & import("react").RefAttributes<View>>;
|
|
99
|
+
Input: import("react").ForwardRefExoticComponent<MarkdownEditorInputProps & import("react").RefAttributes<TextInput>>;
|
|
100
|
+
Preview: import("react").ForwardRefExoticComponent<MarkdownEditorPreviewProps & import("react").RefAttributes<View>>;
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/markdown-editor/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EACL,IAAI,EAEJ,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAmBtB,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,sBAAa,CAAC;AAW3D,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,0BAAuB,CAAC;AAEzE,wCAAwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,CAAC;AAErD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAC5B,MAAM,GACN,QAAQ,GACR,SAAS,GACT,OAAO,GACP,MAAM,GACN,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAwGX,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,uBAAuB;IACvB,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,uCAAuC;IACvC,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAClD,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA0HD,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACjC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmED,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,aAAa,EAAE,OAAO,GAAG,cAAc,GAAG,cAAc,CAAC;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoDD,MAAM,WAAW,0BAA2B,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+BD,eAAO,MAAM,cAAc;;;;CAIzB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edits a formatting toolbar makes, as functions of the text and where the
|
|
3
|
+
* caret is in it.
|
|
4
|
+
*
|
|
5
|
+
* Pure, and deliberately so. Every one of these takes `(text, selection)` and
|
|
6
|
+
* returns the text that should replace it together with where the selection
|
|
7
|
+
* should end up — no component, no ref, no input. What makes a formatting
|
|
8
|
+
* toolbar feel broken is never the characters it inserts; it is where the caret
|
|
9
|
+
* lands afterwards, and that is a property of these functions rather than of
|
|
10
|
+
* the field they are wired to.
|
|
11
|
+
*
|
|
12
|
+
* Three rules they all keep:
|
|
13
|
+
*
|
|
14
|
+
* - **Applying twice undoes it.** A toolbar button that only ever adds is a
|
|
15
|
+
* button you can press once, and every press after that damages the text.
|
|
16
|
+
* - **A selection stays selected.** Bolding three words and then italicising
|
|
17
|
+
* the same three has to be two presses, not a press and a re-selection.
|
|
18
|
+
* - **With nothing selected, the caret lands where the writing goes** — between
|
|
19
|
+
* the new markers rather than after them.
|
|
20
|
+
*/
|
|
21
|
+
export interface EditorSelection {
|
|
22
|
+
start: number;
|
|
23
|
+
end: number;
|
|
24
|
+
}
|
|
25
|
+
export interface EditResult {
|
|
26
|
+
text: string;
|
|
27
|
+
selection: EditorSelection;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Wraps the selection in `marker`, or unwraps it if it is already wrapped.
|
|
31
|
+
*
|
|
32
|
+
* "Already wrapped" is checked both ways round: the markers may be inside the
|
|
33
|
+
* selection (the writer selected `**bold**`) or immediately outside it (they
|
|
34
|
+
* selected `bold` in the middle of `**bold**`). Both are the same intent, and a
|
|
35
|
+
* toolbar that only recognised one of them would add a second pair of asterisks
|
|
36
|
+
* to text that already had them.
|
|
37
|
+
*/
|
|
38
|
+
export declare function toggleWrap(text: string, selection: EditorSelection, marker: string): EditResult;
|
|
39
|
+
/**
|
|
40
|
+
* Puts `prefix` on the front of every line the selection touches, or takes it
|
|
41
|
+
* off if every one of them already has it.
|
|
42
|
+
*
|
|
43
|
+
* Every one, not any one: a mixed block is a block the writer is trying to make
|
|
44
|
+
* uniform, so the useful answer there is to add rather than to remove.
|
|
45
|
+
*/
|
|
46
|
+
export declare function toggleLinePrefix(text: string, selection: EditorSelection, prefix: string): EditResult;
|
|
47
|
+
/**
|
|
48
|
+
* Numbers the lines the selection touches, or unnumbers them.
|
|
49
|
+
*
|
|
50
|
+
* Counted from one within the block rather than continuing whatever came above
|
|
51
|
+
* it: the numbers a writer types are not the numbers that get rendered — every
|
|
52
|
+
* markdown reader renumbers an ordered list from its first item — so the useful
|
|
53
|
+
* thing is for the source to read the way the output will.
|
|
54
|
+
*/
|
|
55
|
+
export declare function toggleOrderedList(text: string, selection: EditorSelection): EditResult;
|
|
56
|
+
/** Placeholder left where a link's target goes. */
|
|
57
|
+
export declare const LINK_PLACEHOLDER = "url";
|
|
58
|
+
/**
|
|
59
|
+
* Turns the selection into a link, and selects the part that still needs
|
|
60
|
+
* filling in.
|
|
61
|
+
*
|
|
62
|
+
* Which part that is depends on what was selected. With text selected, the
|
|
63
|
+
* label is written and the target is not, so the placeholder target is left
|
|
64
|
+
* selected and typing replaces it. With nothing selected there is neither, and
|
|
65
|
+
* the label comes first because it is the half the writer is thinking about.
|
|
66
|
+
*/
|
|
67
|
+
export declare function insertLink(text: string, selection: EditorSelection): EditResult;
|
|
68
|
+
/**
|
|
69
|
+
* Puts the selected lines in a fence, or takes them out of one.
|
|
70
|
+
*
|
|
71
|
+
* A fence is the one construct that has to sit on lines of its own, so this
|
|
72
|
+
* makes room for itself: a blank line before it when it is not already at the
|
|
73
|
+
* start of a paragraph, and one after it at the end.
|
|
74
|
+
*/
|
|
75
|
+
export declare function toggleFence(text: string, selection: EditorSelection): EditResult;
|
|
76
|
+
//# sourceMappingURL=markdown-transforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown-transforms.d.ts","sourceRoot":"","sources":["../../../../../src/components/markdown-editor/markdown-transforms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,eAAe,CAAC;CAC5B;AAaD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,MAAM,GACb,UAAU,CAmCZ;AAiCD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,MAAM,GACb,UAAU,CASZ;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,eAAe,GACzB,UAAU,CAQZ;AAMD,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,QAAQ,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,UAAU,CAY/E;AAMD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,GAAG,UAAU,CAiBhF"}
|