@webority-technologies/mobile-ui 0.0.1 → 0.0.2
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 +2 -1
- package/lib/commonjs/components/AuthedImage/AuthedImage.js +225 -0
- package/lib/commonjs/components/AuthedImage/authedImageCache.js +253 -0
- package/lib/commonjs/components/AuthedImage/index.js +45 -0
- package/lib/commonjs/components/BottomSheet/BottomSheet.js +10 -4
- package/lib/commonjs/components/Drawer/Drawer.js +6 -3
- package/lib/commonjs/components/ErrorBoundary/ErrorBoundary.js +136 -0
- package/lib/commonjs/components/ErrorBoundary/index.js +27 -0
- package/lib/commonjs/components/ErrorBoundary/useErrorHandler.js +24 -0
- package/lib/commonjs/components/ErrorBoundary/withErrorBoundary.js +28 -0
- package/lib/commonjs/components/FilePicker/FilePicker.js +1 -1
- package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +14 -1
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +13 -0
- package/lib/commonjs/components/InlineSelect/InlineSelect.js +1 -1
- package/lib/commonjs/components/Input/Input.js +6 -0
- package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +62 -13
- package/lib/commonjs/components/LoaderOverlay/LoaderOverlay.js +203 -0
- package/lib/commonjs/components/LoaderOverlay/index.js +25 -0
- package/lib/commonjs/components/Markdown/Markdown.js +274 -0
- package/lib/commonjs/components/Markdown/index.js +13 -0
- package/lib/commonjs/components/Markdown/parse.js +229 -0
- package/lib/commonjs/components/OTPInput/OTPInput.js +5 -0
- package/lib/commonjs/components/OptionSheet/OptionSheet.js +3 -3
- package/lib/commonjs/components/PhoneNumberInput/PhoneNumberInput.js +1 -1
- package/lib/commonjs/components/Radio/Radio.js +9 -0
- package/lib/commonjs/components/Rating/Rating.js +5 -0
- package/lib/commonjs/components/RichTextEditor/RichTextEditor.js +311 -0
- package/lib/commonjs/components/RichTextEditor/index.js +20 -0
- package/lib/commonjs/components/RichTextEditor/markdownActions.js +213 -0
- package/lib/commonjs/components/SearchBar/SearchBar.js +7 -0
- package/lib/commonjs/components/Select/Select.js +1 -1
- package/lib/commonjs/components/Skeleton/Skeleton.js +17 -36
- package/lib/commonjs/components/Slider/Slider.js +21 -5
- package/lib/commonjs/components/Swipeable/Swipeable.js +5 -2
- package/lib/commonjs/components/TimePicker/TimePicker.js +11 -0
- package/lib/commonjs/components/index.js +230 -141
- package/lib/commonjs/form/FormContext.js +13 -1
- package/lib/commonjs/form/useField.js +13 -1
- package/lib/commonjs/theme/Gradient.js +44 -41
- package/lib/commonjs/theme/index.js +24 -2
- package/lib/commonjs/utils/hapticUtils.js +38 -18
- package/lib/module/components/AuthedImage/AuthedImage.js +221 -0
- package/lib/module/components/AuthedImage/authedImageCache.js +239 -0
- package/lib/module/components/AuthedImage/index.js +5 -0
- package/lib/module/components/BottomSheet/BottomSheet.js +10 -4
- package/lib/module/components/Drawer/Drawer.js +6 -3
- package/lib/module/components/ErrorBoundary/ErrorBoundary.js +130 -0
- package/lib/module/components/ErrorBoundary/index.js +6 -0
- package/lib/module/components/ErrorBoundary/useErrorHandler.js +20 -0
- package/lib/module/components/ErrorBoundary/withErrorBoundary.js +23 -0
- package/lib/module/components/FilePicker/FilePicker.js +1 -1
- package/lib/module/components/FloatingActionButton/FloatingActionButton.js +14 -1
- package/lib/module/components/ImageGallery/ImageGallery.js +13 -0
- package/lib/module/components/InlineSelect/InlineSelect.js +1 -1
- package/lib/module/components/Input/Input.js +6 -0
- package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +63 -14
- package/lib/module/components/LoaderOverlay/LoaderOverlay.js +196 -0
- package/lib/module/components/LoaderOverlay/index.js +4 -0
- package/lib/module/components/Markdown/Markdown.js +268 -0
- package/lib/module/components/Markdown/index.js +4 -0
- package/lib/module/components/Markdown/parse.js +223 -0
- package/lib/module/components/OTPInput/OTPInput.js +5 -0
- package/lib/module/components/OptionSheet/OptionSheet.js +3 -3
- package/lib/module/components/PhoneNumberInput/PhoneNumberInput.js +1 -1
- package/lib/module/components/Radio/Radio.js +9 -0
- package/lib/module/components/Rating/Rating.js +5 -0
- package/lib/module/components/RichTextEditor/RichTextEditor.js +306 -0
- package/lib/module/components/RichTextEditor/index.js +5 -0
- package/lib/module/components/RichTextEditor/markdownActions.js +207 -0
- package/lib/module/components/SearchBar/SearchBar.js +7 -0
- package/lib/module/components/Select/Select.js +1 -1
- package/lib/module/components/Skeleton/Skeleton.js +17 -36
- package/lib/module/components/Slider/Slider.js +21 -5
- package/lib/module/components/Swipeable/Swipeable.js +5 -2
- package/lib/module/components/TimePicker/TimePicker.js +11 -0
- package/lib/module/components/index.js +5 -0
- package/lib/module/form/FormContext.js +13 -1
- package/lib/module/form/useField.js +13 -1
- package/lib/module/theme/Gradient.js +41 -38
- package/lib/module/theme/index.js +17 -1
- package/lib/module/utils/hapticUtils.js +37 -18
- package/lib/typescript/commonjs/components/AuthedImage/AuthedImage.d.ts +22 -0
- package/lib/typescript/commonjs/components/AuthedImage/authedImageCache.d.ts +48 -0
- package/lib/typescript/commonjs/components/AuthedImage/index.d.ts +5 -0
- package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -3
- package/lib/typescript/commonjs/components/Card/Card.d.ts +1 -2
- package/lib/typescript/commonjs/components/ErrorBoundary/ErrorBoundary.d.ts +38 -0
- package/lib/typescript/commonjs/components/ErrorBoundary/index.d.ts +5 -0
- package/lib/typescript/commonjs/components/ErrorBoundary/useErrorHandler.d.ts +9 -0
- package/lib/typescript/commonjs/components/ErrorBoundary/withErrorBoundary.d.ts +9 -0
- package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +15 -0
- package/lib/typescript/commonjs/components/LoaderOverlay/LoaderOverlay.d.ts +25 -0
- package/lib/typescript/commonjs/components/LoaderOverlay/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/Markdown/Markdown.d.ts +29 -0
- package/lib/typescript/commonjs/components/Markdown/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/Markdown/parse.d.ts +58 -0
- package/lib/typescript/commonjs/components/OptionSheet/OptionSheet.d.ts +8 -1
- package/lib/typescript/commonjs/components/RichTextEditor/RichTextEditor.d.ts +48 -0
- package/lib/typescript/commonjs/components/RichTextEditor/index.d.ts +5 -0
- package/lib/typescript/commonjs/components/RichTextEditor/markdownActions.d.ts +35 -0
- package/lib/typescript/commonjs/components/Skeleton/Skeleton.d.ts +1 -1
- package/lib/typescript/commonjs/components/index.d.ts +10 -0
- package/lib/typescript/commonjs/form/FormContext.d.ts +13 -3
- package/lib/typescript/commonjs/theme/Gradient.d.ts +18 -3
- package/lib/typescript/commonjs/theme/index.d.ts +16 -1
- package/lib/typescript/commonjs/theme/types.d.ts +2 -2
- package/lib/typescript/commonjs/utils/hapticUtils.d.ts +4 -0
- package/lib/typescript/module/components/AuthedImage/AuthedImage.d.ts +22 -0
- package/lib/typescript/module/components/AuthedImage/authedImageCache.d.ts +48 -0
- package/lib/typescript/module/components/AuthedImage/index.d.ts +5 -0
- package/lib/typescript/module/components/Button/Button.d.ts +1 -3
- package/lib/typescript/module/components/Card/Card.d.ts +1 -2
- package/lib/typescript/module/components/ErrorBoundary/ErrorBoundary.d.ts +38 -0
- package/lib/typescript/module/components/ErrorBoundary/index.d.ts +5 -0
- package/lib/typescript/module/components/ErrorBoundary/useErrorHandler.d.ts +9 -0
- package/lib/typescript/module/components/ErrorBoundary/withErrorBoundary.d.ts +9 -0
- package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +15 -0
- package/lib/typescript/module/components/LoaderOverlay/LoaderOverlay.d.ts +25 -0
- package/lib/typescript/module/components/LoaderOverlay/index.d.ts +3 -0
- package/lib/typescript/module/components/Markdown/Markdown.d.ts +29 -0
- package/lib/typescript/module/components/Markdown/index.d.ts +3 -0
- package/lib/typescript/module/components/Markdown/parse.d.ts +58 -0
- package/lib/typescript/module/components/OptionSheet/OptionSheet.d.ts +8 -1
- package/lib/typescript/module/components/RichTextEditor/RichTextEditor.d.ts +48 -0
- package/lib/typescript/module/components/RichTextEditor/index.d.ts +5 -0
- package/lib/typescript/module/components/RichTextEditor/markdownActions.d.ts +35 -0
- package/lib/typescript/module/components/Skeleton/Skeleton.d.ts +1 -1
- package/lib/typescript/module/components/index.d.ts +10 -0
- package/lib/typescript/module/form/FormContext.d.ts +13 -3
- package/lib/typescript/module/theme/Gradient.d.ts +18 -3
- package/lib/typescript/module/theme/index.d.ts +16 -1
- package/lib/typescript/module/theme/types.d.ts +2 -2
- package/lib/typescript/module/utils/hapticUtils.d.ts +4 -0
- package/package.json +20 -13
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.RichTextEditor = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _index = require("../../theme/index.js");
|
|
10
|
+
var _index2 = require("../../utils/index.js");
|
|
11
|
+
var _index3 = require("../Skeleton/index.js");
|
|
12
|
+
var _markdownActions = require("./markdownActions.js");
|
|
13
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
+
/** Glyph + spoken name per action. The glyph IS the markdown marker wherever one reads as itself. */
|
|
16
|
+
const ACTION_META = {
|
|
17
|
+
bold: {
|
|
18
|
+
glyph: 'B',
|
|
19
|
+
label: 'Bold'
|
|
20
|
+
},
|
|
21
|
+
italic: {
|
|
22
|
+
glyph: 'I',
|
|
23
|
+
label: 'Italic'
|
|
24
|
+
},
|
|
25
|
+
strikethrough: {
|
|
26
|
+
glyph: 'S',
|
|
27
|
+
label: 'Strikethrough'
|
|
28
|
+
},
|
|
29
|
+
code: {
|
|
30
|
+
glyph: '</>',
|
|
31
|
+
label: 'Inline code'
|
|
32
|
+
},
|
|
33
|
+
link: {
|
|
34
|
+
glyph: 'link',
|
|
35
|
+
label: 'Link'
|
|
36
|
+
},
|
|
37
|
+
heading1: {
|
|
38
|
+
glyph: 'H1',
|
|
39
|
+
label: 'Heading 1'
|
|
40
|
+
},
|
|
41
|
+
heading2: {
|
|
42
|
+
glyph: 'H2',
|
|
43
|
+
label: 'Heading 2'
|
|
44
|
+
},
|
|
45
|
+
bulletList: {
|
|
46
|
+
glyph: '•',
|
|
47
|
+
label: 'Bulleted list'
|
|
48
|
+
},
|
|
49
|
+
numberedList: {
|
|
50
|
+
glyph: '1.',
|
|
51
|
+
label: 'Numbered list'
|
|
52
|
+
},
|
|
53
|
+
quote: {
|
|
54
|
+
glyph: '>',
|
|
55
|
+
label: 'Quote'
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const DEFAULT_ACTIONS = ['bold', 'italic', 'strikethrough', 'code', 'link', 'heading1', 'heading2', 'bulletList', 'numberedList', 'quote'];
|
|
59
|
+
const DEFAULT_MIN_HEIGHT = 160;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A markdown editor: a themed multiline field plus a toolbar that wraps the
|
|
63
|
+
* current selection in the markers our own `<Markdown>` renderer reads back.
|
|
64
|
+
*
|
|
65
|
+
* Deliberately NOT a WYSIWYG surface. Rich-text-on-RN means a WebView, which is
|
|
66
|
+
* exactly what replacing the stale `react-native-pell-rich-editor` was meant to
|
|
67
|
+
* escape: markdown in, markdown out, one plain TextInput, no bridge.
|
|
68
|
+
*/
|
|
69
|
+
const RichTextEditor = exports.RichTextEditor = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
|
|
70
|
+
const {
|
|
71
|
+
value,
|
|
72
|
+
onChangeText,
|
|
73
|
+
placeholder,
|
|
74
|
+
actions = DEFAULT_ACTIONS,
|
|
75
|
+
disabled = false,
|
|
76
|
+
loading = false,
|
|
77
|
+
minHeight = DEFAULT_MIN_HEIGHT,
|
|
78
|
+
autoFocus = false,
|
|
79
|
+
accessibilityLabel,
|
|
80
|
+
haptic,
|
|
81
|
+
style,
|
|
82
|
+
toolbarStyle,
|
|
83
|
+
inputStyle,
|
|
84
|
+
testID
|
|
85
|
+
} = props;
|
|
86
|
+
const theme = (0, _index.useTheme)();
|
|
87
|
+
const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
|
|
88
|
+
const text = value ?? '';
|
|
89
|
+
const [selection, setSelection] = (0, _react.useState)({
|
|
90
|
+
start: 0,
|
|
91
|
+
end: 0
|
|
92
|
+
});
|
|
93
|
+
const [focused, setFocused] = (0, _react.useState)(false);
|
|
94
|
+
/**
|
|
95
|
+
* Only set for the render immediately after a toolbar action. Holding the
|
|
96
|
+
* TextInput's `selection` permanently fights the platform keyboard on
|
|
97
|
+
* Android, so it is released as soon as the field reports the caret moved.
|
|
98
|
+
*/
|
|
99
|
+
const [forcedSelection, setForcedSelection] = (0, _react.useState)(null);
|
|
100
|
+
const handleSelectionChange = (0, _react.useCallback)(event => {
|
|
101
|
+
setSelection(event.nativeEvent.selection);
|
|
102
|
+
setForcedSelection(null);
|
|
103
|
+
}, []);
|
|
104
|
+
const handleChangeText = (0, _react.useCallback)(next => {
|
|
105
|
+
setForcedSelection(null);
|
|
106
|
+
onChangeText?.(next);
|
|
107
|
+
}, [onChangeText]);
|
|
108
|
+
const runAction = (0, _react.useCallback)(action => {
|
|
109
|
+
if (disabled) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const h = (0, _index2.resolveHaptic)(haptic, 'selection');
|
|
113
|
+
if (h) {
|
|
114
|
+
(0, _index2.triggerHaptic)(h);
|
|
115
|
+
}
|
|
116
|
+
const result = (0, _markdownActions.applyMarkdownAction)({
|
|
117
|
+
text,
|
|
118
|
+
selection: (0, _markdownActions.normalizeSelection)(selection, text.length)
|
|
119
|
+
}, action);
|
|
120
|
+
setSelection(result.selection);
|
|
121
|
+
setForcedSelection(result.selection);
|
|
122
|
+
onChangeText?.(result.text);
|
|
123
|
+
}, [disabled, haptic, onChangeText, selection, text]);
|
|
124
|
+
if (loading) {
|
|
125
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(RichTextEditorSkeleton, {
|
|
126
|
+
actionCount: actions.length,
|
|
127
|
+
minHeight: minHeight,
|
|
128
|
+
style: style,
|
|
129
|
+
testID: testID
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
133
|
+
testID: testID,
|
|
134
|
+
style: [styles.container, {
|
|
135
|
+
borderColor: focused ? theme.colors.border.focus : theme.colors.border.primary,
|
|
136
|
+
opacity: disabled ? 0.55 : 1
|
|
137
|
+
}, style],
|
|
138
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
139
|
+
style: [styles.toolbar, toolbarStyle],
|
|
140
|
+
children: actions.map(action => /*#__PURE__*/(0, _jsxRuntime.jsx)(ToolbarButton, {
|
|
141
|
+
action: action,
|
|
142
|
+
disabled: disabled,
|
|
143
|
+
onPress: runAction,
|
|
144
|
+
styles: styles,
|
|
145
|
+
theme: theme,
|
|
146
|
+
testID: testID ? `${testID}-${action}` : undefined
|
|
147
|
+
}, action))
|
|
148
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
|
|
149
|
+
ref: ref,
|
|
150
|
+
value: text,
|
|
151
|
+
onChangeText: handleChangeText,
|
|
152
|
+
onSelectionChange: handleSelectionChange,
|
|
153
|
+
onFocus: () => setFocused(true),
|
|
154
|
+
onBlur: () => setFocused(false),
|
|
155
|
+
selection: forcedSelection ?? undefined,
|
|
156
|
+
editable: !disabled,
|
|
157
|
+
autoFocus: autoFocus,
|
|
158
|
+
multiline: true,
|
|
159
|
+
textAlignVertical: "top",
|
|
160
|
+
placeholder: placeholder,
|
|
161
|
+
placeholderTextColor: theme.colors.text.tertiary,
|
|
162
|
+
accessibilityLabel: accessibilityLabel,
|
|
163
|
+
style: [styles.input, {
|
|
164
|
+
minHeight
|
|
165
|
+
}, inputStyle],
|
|
166
|
+
testID: testID ? `${testID}-input` : undefined
|
|
167
|
+
})]
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
RichTextEditor.displayName = 'RichTextEditor';
|
|
171
|
+
const ToolbarButton = ({
|
|
172
|
+
action,
|
|
173
|
+
disabled,
|
|
174
|
+
onPress,
|
|
175
|
+
styles,
|
|
176
|
+
theme,
|
|
177
|
+
testID
|
|
178
|
+
}) => {
|
|
179
|
+
const meta = ACTION_META[action];
|
|
180
|
+
// Each glyph carries its own formatting, so the button reads as what it does.
|
|
181
|
+
// Built as one object rather than an array entry per rule: a `fontWeight:
|
|
182
|
+
// undefined` in a style array wipes the family weight `fontFor` just set.
|
|
183
|
+
const glyphStyle = {
|
|
184
|
+
color: disabled ? theme.colors.text.disabled : theme.colors.text.primary
|
|
185
|
+
};
|
|
186
|
+
if (action === 'bold') {
|
|
187
|
+
glyphStyle.fontWeight = theme.typography.fontWeight.bold;
|
|
188
|
+
}
|
|
189
|
+
if (action === 'italic') {
|
|
190
|
+
glyphStyle.fontStyle = 'italic';
|
|
191
|
+
}
|
|
192
|
+
if (action === 'strikethrough') {
|
|
193
|
+
glyphStyle.textDecorationLine = 'line-through';
|
|
194
|
+
}
|
|
195
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
196
|
+
onPress: () => onPress(action),
|
|
197
|
+
disabled: disabled,
|
|
198
|
+
accessibilityRole: "button",
|
|
199
|
+
accessibilityLabel: meta.label,
|
|
200
|
+
accessibilityState: {
|
|
201
|
+
disabled
|
|
202
|
+
},
|
|
203
|
+
hitSlop: 4,
|
|
204
|
+
testID: testID,
|
|
205
|
+
style: ({
|
|
206
|
+
pressed
|
|
207
|
+
}) => [styles.button, {
|
|
208
|
+
backgroundColor: pressed ? theme.colors.surface.pressed : theme.colors.background.secondary
|
|
209
|
+
}],
|
|
210
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
211
|
+
allowFontScaling: false,
|
|
212
|
+
style: [styles.buttonLabel, glyphStyle],
|
|
213
|
+
children: meta.glyph
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
ToolbarButton.displayName = 'RichTextEditorToolbarButton';
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Placeholder shape for `<RichTextEditor>`: the same bordered box, a row of
|
|
221
|
+
* button-sized blocks where the toolbar sits, and text lines filling the body,
|
|
222
|
+
* so nothing reflows when the draft arrives.
|
|
223
|
+
*/
|
|
224
|
+
const RichTextEditorSkeleton = ({
|
|
225
|
+
actionCount,
|
|
226
|
+
minHeight,
|
|
227
|
+
style,
|
|
228
|
+
testID
|
|
229
|
+
}) => {
|
|
230
|
+
const theme = (0, _index.useTheme)();
|
|
231
|
+
const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
|
|
232
|
+
const buttons = Array.from({
|
|
233
|
+
length: Math.max(1, actionCount)
|
|
234
|
+
}, (_, index) => index);
|
|
235
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
236
|
+
testID: testID,
|
|
237
|
+
style: [styles.container, style],
|
|
238
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
239
|
+
style: styles.toolbar,
|
|
240
|
+
children: buttons.map(index => /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Skeleton, {
|
|
241
|
+
width: 34,
|
|
242
|
+
height: 30,
|
|
243
|
+
radius: "sm"
|
|
244
|
+
}, index))
|
|
245
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
246
|
+
style: [styles.skeletonBody, {
|
|
247
|
+
minHeight
|
|
248
|
+
}],
|
|
249
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Skeleton, {
|
|
250
|
+
shape: "text",
|
|
251
|
+
width: "94%",
|
|
252
|
+
height: 14
|
|
253
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Skeleton, {
|
|
254
|
+
shape: "text",
|
|
255
|
+
width: "100%",
|
|
256
|
+
height: 14
|
|
257
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_index3.Skeleton, {
|
|
258
|
+
shape: "text",
|
|
259
|
+
width: "72%",
|
|
260
|
+
height: 14
|
|
261
|
+
})]
|
|
262
|
+
})]
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
RichTextEditorSkeleton.displayName = 'RichTextEditorSkeleton';
|
|
266
|
+
const buildStyles = theme => _reactNative.StyleSheet.create({
|
|
267
|
+
container: {
|
|
268
|
+
borderWidth: theme.colors.border.width,
|
|
269
|
+
borderColor: theme.colors.border.primary,
|
|
270
|
+
borderRadius: theme.radius.md,
|
|
271
|
+
backgroundColor: theme.colors.background.primary,
|
|
272
|
+
overflow: 'hidden'
|
|
273
|
+
},
|
|
274
|
+
toolbar: {
|
|
275
|
+
flexDirection: 'row',
|
|
276
|
+
flexWrap: 'wrap',
|
|
277
|
+
alignItems: 'center',
|
|
278
|
+
gap: theme.spacing.xs,
|
|
279
|
+
padding: theme.spacing.xs,
|
|
280
|
+
borderBottomWidth: _reactNative.StyleSheet.hairlineWidth,
|
|
281
|
+
borderBottomColor: theme.colors.border.secondary,
|
|
282
|
+
backgroundColor: theme.colors.background.secondary
|
|
283
|
+
},
|
|
284
|
+
button: {
|
|
285
|
+
minWidth: 34,
|
|
286
|
+
height: 30,
|
|
287
|
+
paddingHorizontal: theme.spacing.xs,
|
|
288
|
+
borderRadius: theme.radius.sm,
|
|
289
|
+
alignItems: 'center',
|
|
290
|
+
justifyContent: 'center'
|
|
291
|
+
},
|
|
292
|
+
buttonLabel: {
|
|
293
|
+
...(0, _index.fontFor)(theme, 'medium'),
|
|
294
|
+
fontSize: theme.typography.fontSize.sm
|
|
295
|
+
},
|
|
296
|
+
input: {
|
|
297
|
+
...(0, _index.fontFor)(theme, 'normal'),
|
|
298
|
+
color: theme.colors.text.primary,
|
|
299
|
+
fontSize: theme.typography.fontSize.base,
|
|
300
|
+
lineHeight: theme.typography.fontSize.base * 1.5,
|
|
301
|
+
paddingHorizontal: theme.spacing.md,
|
|
302
|
+
paddingVertical: theme.spacing.sm
|
|
303
|
+
},
|
|
304
|
+
skeletonBody: {
|
|
305
|
+
paddingHorizontal: theme.spacing.md,
|
|
306
|
+
paddingVertical: theme.spacing.sm,
|
|
307
|
+
gap: theme.spacing.sm
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
var _default = exports.default = RichTextEditor;
|
|
311
|
+
//# sourceMappingURL=RichTextEditor.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "RichTextEditor", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _RichTextEditor.RichTextEditor;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "applyMarkdownAction", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _markdownActions.applyMarkdownAction;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
var _markdownActions = require("./markdownActions.js");
|
|
19
|
+
var _RichTextEditor = require("./RichTextEditor.js");
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.normalizeSelection = exports.applyMarkdownAction = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* The text transforms behind the editor's toolbar, kept out of the component so
|
|
9
|
+
* the behaviour people actually notice — toggling bold OFF again, and where the
|
|
10
|
+
* caret lands when nothing is selected — can be tested as data, with no
|
|
11
|
+
* rendering involved. Same split as Markdown/parse.ts.
|
|
12
|
+
*
|
|
13
|
+
* The output targets our own `<Markdown>` renderer, so the vocabulary is the
|
|
14
|
+
* subset that parser reads: `**`, `*`, `~~`, backtick, `[](…)`, `#`, `##`,
|
|
15
|
+
* `- `, `1. `, `> `. Nothing here emits anything that renderer would show as
|
|
16
|
+
* literal text.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const INLINE_MARKERS = {
|
|
20
|
+
bold: '**',
|
|
21
|
+
italic: '*',
|
|
22
|
+
strikethrough: '~~',
|
|
23
|
+
code: '`'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** A whole link, and only a whole link — used to decide "is this already one?". */
|
|
27
|
+
const WHOLE_LINK = /^\[([^\]]*)\]\(([^)\s]*)\)$/;
|
|
28
|
+
const LINK_URL_PLACEHOLDER = 'url';
|
|
29
|
+
const BLOCK_RULES = {
|
|
30
|
+
heading1: {
|
|
31
|
+
has: l => /^#\s/.test(l),
|
|
32
|
+
prefix: () => '# '
|
|
33
|
+
},
|
|
34
|
+
heading2: {
|
|
35
|
+
has: l => /^##\s/.test(l),
|
|
36
|
+
prefix: () => '## '
|
|
37
|
+
},
|
|
38
|
+
bulletList: {
|
|
39
|
+
has: l => /^[-*+]\s/.test(l),
|
|
40
|
+
prefix: () => '- '
|
|
41
|
+
},
|
|
42
|
+
numberedList: {
|
|
43
|
+
has: l => /^\d+[.)]\s/.test(l),
|
|
44
|
+
prefix: index => `${index + 1}. `
|
|
45
|
+
},
|
|
46
|
+
quote: {
|
|
47
|
+
has: l => /^>\s?/.test(l),
|
|
48
|
+
prefix: () => '> '
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Every block marker this editor writes, so switching kinds replaces rather than stacks. */
|
|
53
|
+
const ANY_BLOCK_PREFIX = /^(?:#{1,6}\s+|[-*+]\s+|\d+[.)]\s+|>\s?)/;
|
|
54
|
+
const isBlockAction = action => action in BLOCK_RULES;
|
|
55
|
+
const clamp = (value, max) => {
|
|
56
|
+
// React Native reports -1 for "no selection yet" on some Android builds.
|
|
57
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
return Math.min(Math.trunc(value), max);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Selection as given can be reversed, unset, or past the end after an edit. */
|
|
64
|
+
const normalizeSelection = (selection, length) => {
|
|
65
|
+
const a = clamp(selection.start, length);
|
|
66
|
+
const b = clamp(selection.end, length);
|
|
67
|
+
return {
|
|
68
|
+
start: Math.min(a, b),
|
|
69
|
+
end: Math.max(a, b)
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
exports.normalizeSelection = normalizeSelection;
|
|
73
|
+
const caret = at => ({
|
|
74
|
+
start: at,
|
|
75
|
+
end: at
|
|
76
|
+
});
|
|
77
|
+
const applyInlineMarker = (text, selection, marker) => {
|
|
78
|
+
const {
|
|
79
|
+
start,
|
|
80
|
+
end
|
|
81
|
+
} = selection;
|
|
82
|
+
const selected = text.slice(start, end);
|
|
83
|
+
const size = marker.length;
|
|
84
|
+
|
|
85
|
+
// `*` is a prefix of `**`, so italic must never mistake bold for itself:
|
|
86
|
+
// unwrapping one star per side would silently downgrade bold to italic.
|
|
87
|
+
const boldCollision = marker === '*';
|
|
88
|
+
const wrappedInside = selected.length >= size * 2 && selected.startsWith(marker) && selected.endsWith(marker);
|
|
89
|
+
if (wrappedInside && !(boldCollision && selected.startsWith('**') && selected.endsWith('**'))) {
|
|
90
|
+
const inner = selected.slice(size, selected.length - size);
|
|
91
|
+
return {
|
|
92
|
+
text: text.slice(0, start) + inner + text.slice(end),
|
|
93
|
+
selection: {
|
|
94
|
+
start,
|
|
95
|
+
end: start + inner.length
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const before = text.slice(Math.max(0, start - size), start);
|
|
100
|
+
const after = text.slice(end, end + size);
|
|
101
|
+
const wrappedOutside = start >= size && before === marker && after === marker && !(boldCollision && text.slice(Math.max(0, start - 2), start) === '**');
|
|
102
|
+
if (wrappedOutside) {
|
|
103
|
+
return {
|
|
104
|
+
text: text.slice(0, start - size) + selected + text.slice(end + size),
|
|
105
|
+
selection: {
|
|
106
|
+
start: start - size,
|
|
107
|
+
end: end - size
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const next = text.slice(0, start) + marker + selected + marker + text.slice(end);
|
|
112
|
+
return {
|
|
113
|
+
text: next,
|
|
114
|
+
selection: start === end ? caret(start + size) : {
|
|
115
|
+
start: start + size,
|
|
116
|
+
end: end + size
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const applyLink = (text, selection) => {
|
|
121
|
+
const {
|
|
122
|
+
start,
|
|
123
|
+
end
|
|
124
|
+
} = selection;
|
|
125
|
+
const selected = text.slice(start, end);
|
|
126
|
+
const existing = WHOLE_LINK.exec(selected);
|
|
127
|
+
if (existing) {
|
|
128
|
+
const label = existing[1];
|
|
129
|
+
return {
|
|
130
|
+
text: text.slice(0, start) + label + text.slice(end),
|
|
131
|
+
selection: {
|
|
132
|
+
start,
|
|
133
|
+
end: start + label.length
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const inserted = `[${selected}](${LINK_URL_PLACEHOLDER})`;
|
|
138
|
+
const next = text.slice(0, start) + inserted + text.slice(end);
|
|
139
|
+
if (start === end) {
|
|
140
|
+
// Nothing selected: the label is what the user types first, so the caret
|
|
141
|
+
// goes between the brackets rather than onto the URL placeholder.
|
|
142
|
+
return {
|
|
143
|
+
text: next,
|
|
144
|
+
selection: caret(start + 1)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const urlStart = start + selected.length + 3;
|
|
148
|
+
return {
|
|
149
|
+
text: next,
|
|
150
|
+
selection: {
|
|
151
|
+
start: urlStart,
|
|
152
|
+
end: urlStart + LINK_URL_PLACEHOLDER.length
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
const applyBlockPrefix = (text, selection, action) => {
|
|
157
|
+
const rule = BLOCK_RULES[action];
|
|
158
|
+
const lines = text.split('\n');
|
|
159
|
+
const offsets = [];
|
|
160
|
+
let cursor = 0;
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
offsets.push(cursor);
|
|
163
|
+
cursor += line.length + 1;
|
|
164
|
+
}
|
|
165
|
+
const lineIndexAt = position => {
|
|
166
|
+
let index = 0;
|
|
167
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
168
|
+
if (offsets[i] <= position) {
|
|
169
|
+
index = i;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return index;
|
|
173
|
+
};
|
|
174
|
+
const first = lineIndexAt(selection.start);
|
|
175
|
+
const last = lineIndexAt(selection.end);
|
|
176
|
+
const affected = lines.slice(first, last + 1);
|
|
177
|
+
const allHave = affected.every(line => rule.has(line));
|
|
178
|
+
const rewritten = affected.map((line, index) => allHave ? line.replace(ANY_BLOCK_PREFIX, '') : rule.prefix(index) + line.replace(ANY_BLOCK_PREFIX, ''));
|
|
179
|
+
const firstDelta = rewritten[0].length - affected[0].length;
|
|
180
|
+
const totalDelta = rewritten.reduce((sum, line, index) => sum + (line.length - affected[index].length), 0);
|
|
181
|
+
const nextLines = [...lines.slice(0, first), ...rewritten, ...lines.slice(last + 1)];
|
|
182
|
+
const nextText = nextLines.join('\n');
|
|
183
|
+
return {
|
|
184
|
+
text: nextText,
|
|
185
|
+
selection: normalizeSelection({
|
|
186
|
+
start: Math.max(offsets[first], selection.start + firstDelta),
|
|
187
|
+
end: selection.end + totalDelta
|
|
188
|
+
}, nextText.length)
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Applies one toolbar action to the current text + selection and returns the
|
|
194
|
+
* next pair. Pure: the caller owns the state.
|
|
195
|
+
*
|
|
196
|
+
* Wrapping applies to the selection; with nothing selected the markers are
|
|
197
|
+
* inserted and the caret lands between them. Applying an action to text that
|
|
198
|
+
* already carries it removes it instead of stacking a second copy — which is
|
|
199
|
+
* the half people notice immediately when it is missing.
|
|
200
|
+
*/
|
|
201
|
+
const applyMarkdownAction = (state, action) => {
|
|
202
|
+
const text = state.text ?? '';
|
|
203
|
+
const selection = normalizeSelection(state.selection, text.length);
|
|
204
|
+
if (isBlockAction(action)) {
|
|
205
|
+
return applyBlockPrefix(text, selection, action);
|
|
206
|
+
}
|
|
207
|
+
if (action === 'link') {
|
|
208
|
+
return applyLink(text, selection);
|
|
209
|
+
}
|
|
210
|
+
return applyInlineMarker(text, selection, INLINE_MARKERS[action]);
|
|
211
|
+
};
|
|
212
|
+
exports.applyMarkdownAction = applyMarkdownAction;
|
|
213
|
+
//# sourceMappingURL=markdownActions.js.map
|
|
@@ -68,6 +68,13 @@ const SearchBar = exports.SearchBar = /*#__PURE__*/(0, _react.forwardRef)((props
|
|
|
68
68
|
const debouncedValue = (0, _useDebounce.useDebounce)(internalValue, debounceMs ?? 0);
|
|
69
69
|
const cancelAnim = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
|
|
70
70
|
|
|
71
|
+
// Equivalent-mutant note: removing the `debounceMs === undefined` guard
|
|
72
|
+
// below cannot change observable behaviour. `internalValue` is only ever
|
|
73
|
+
// read (via `displayValue`) when `debounceMs !== undefined`; without the
|
|
74
|
+
// guard, a non-debounced instance would keep resyncing `internalValue`
|
|
75
|
+
// from `value` on every prop change, but that resynced value is never
|
|
76
|
+
// displayed or sent anywhere while non-debounced, so no test can observe
|
|
77
|
+
// the difference.
|
|
71
78
|
// biome-ignore lint/correctness/useExhaustiveDependencies: syncs the internal draft FROM the `value` prop; adding `internalValue` would revert every keystroke while the debounce is still pending
|
|
72
79
|
(0, _react.useEffect)(() => {
|
|
73
80
|
if (debounceMs === undefined) {
|
|
@@ -231,7 +231,7 @@ const Select = exports.Select = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
|
|
|
231
231
|
accessibilityLabel: label ?? placeholder ?? 'Options',
|
|
232
232
|
options: options,
|
|
233
233
|
labelOf: labelOf,
|
|
234
|
-
|
|
234
|
+
getValue: valueOf,
|
|
235
235
|
disabledOf: disabledOf,
|
|
236
236
|
descriptionOf: descriptionOf,
|
|
237
237
|
iconOf: iconOf,
|
|
@@ -7,6 +7,7 @@ exports.default = exports.SkeletonText = exports.SkeletonCircle = exports.Skelet
|
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _index = require("../../theme/index.js");
|
|
10
|
+
var _Gradient = require("../../theme/Gradient.js");
|
|
10
11
|
var _index2 = require("../../utils/index.js");
|
|
11
12
|
var _SkeletonClock = require("./SkeletonClock.js");
|
|
12
13
|
var _SkeletonProvider = require("./SkeletonProvider.js");
|
|
@@ -24,7 +25,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
24
25
|
* - A View with the base color and `overflow: hidden` + radius gives the
|
|
25
26
|
* placeholder its shape.
|
|
26
27
|
* - For `animation: 'shimmer'`, an Animated.View carrying a 3-stop
|
|
27
|
-
*
|
|
28
|
+
* linear gradient (transparent → highlight → transparent) translates
|
|
28
29
|
* across the box. The radius clips it to the right outline automatically.
|
|
29
30
|
* - For `animation: 'pulse'`, a full-fill Animated.View fades its opacity
|
|
30
31
|
* between 0 and 0.6. Cheap and useful for tiny shapes where a sweep
|
|
@@ -37,17 +38,6 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
37
38
|
* height directly for full control.
|
|
38
39
|
*/
|
|
39
40
|
|
|
40
|
-
// `react-native-linear-gradient` is an OPTIONAL peer dep. When it's installed
|
|
41
|
-
// we sweep a real gradient band across the shimmer. When it isn't, we fall
|
|
42
|
-
// back to a solid-color band — still works, just less polished. This keeps
|
|
43
|
-
// the library deployable in apps that don't need gradients elsewhere.
|
|
44
|
-
let LinearGradientImpl = null;
|
|
45
|
-
try {
|
|
46
|
-
LinearGradientImpl = require('react-native-linear-gradient').default;
|
|
47
|
-
} catch {
|
|
48
|
-
// Optional peer; resolved lazily by consumers that need shimmer polish.
|
|
49
|
-
}
|
|
50
|
-
|
|
51
41
|
/** Animation pattern. */
|
|
52
42
|
|
|
53
43
|
/** Sweep speed for the shimmer band. Ignored for pulse / none. */
|
|
@@ -165,36 +155,27 @@ const Skeleton = ({
|
|
|
165
155
|
}
|
|
166
156
|
|
|
167
157
|
// 3-stop gradient: transparent → highlight → transparent. Feathered
|
|
168
|
-
// edges feel premium; a solid band looks like a bug.
|
|
169
|
-
// gradient package isn't installed we render the band as a solid
|
|
170
|
-
// highlight rectangle — visibly different but still recognizably a
|
|
171
|
-
// shimmer effect.
|
|
158
|
+
// edges feel premium; a solid band looks like a bug.
|
|
172
159
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
173
160
|
pointerEvents: "none",
|
|
174
161
|
style: [styles.shimmerBand, {
|
|
175
162
|
width: bandWidth,
|
|
176
163
|
transform: [{
|
|
177
164
|
translateX
|
|
178
|
-
}]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
193
|
-
style: [_reactNative.StyleSheet.absoluteFill, {
|
|
194
|
-
backgroundColor: highlightColor,
|
|
195
|
-
opacity: 0.55
|
|
196
|
-
}]
|
|
197
|
-
})
|
|
165
|
+
}],
|
|
166
|
+
experimental_backgroundImage: [(0, _Gradient.toBackgroundImage)({
|
|
167
|
+
colors: ['transparent', highlightColor, 'transparent'],
|
|
168
|
+
locations: [0, 0.5, 1],
|
|
169
|
+
start: {
|
|
170
|
+
x: 0,
|
|
171
|
+
y: 0.5
|
|
172
|
+
},
|
|
173
|
+
end: {
|
|
174
|
+
x: 1,
|
|
175
|
+
y: 0.5
|
|
176
|
+
}
|
|
177
|
+
})]
|
|
178
|
+
}]
|
|
198
179
|
});
|
|
199
180
|
}, [effectiveVariant, highlightColor, clock.pulse, containerWidth, styles.shimmerBand, bandWidth, translateX]);
|
|
200
181
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|