@inkra/react 0.8.0 → 0.10.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 +137 -2
- package/THIRD_PARTY_NOTICES.md +9 -0
- package/Xiaolai-OFL.txt +94 -0
- package/dist/PencilIcon.d.ts +14 -1
- package/dist/PencilIcon.js +88 -52
- package/dist/PencilText.js +4 -1
- package/dist/assets/ui-hand-xiaolai-extended.woff2 +0 -0
- package/dist/assets/ui-hand-xiaolai.woff2 +0 -0
- package/dist/assets/xiaolai.css +17 -0
- package/dist/form/EditablePencilText.js +4 -2
- package/dist/guide/index.d.ts +77 -0
- package/dist/guide/index.js +379 -0
- package/dist/i18n/catalog.d.ts +1 -1
- package/dist/i18n/catalog.js +8 -0
- package/dist/image-viewer/index.d.ts +2 -0
- package/dist/image-viewer/index.js +28 -28
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/infinite-scroll/gesture.d.ts +19 -0
- package/dist/infinite-scroll/gesture.js +218 -0
- package/dist/infinite-scroll/index.d.ts +55 -0
- package/dist/infinite-scroll/index.js +543 -0
- package/dist/infinite-scroll/model.d.ts +33 -0
- package/dist/infinite-scroll/model.js +168 -0
- package/dist/internal/iconFill.d.ts +16 -0
- package/dist/internal/iconFill.js +275 -0
- package/dist/internal/surfaceRenderer.d.ts +4 -4
- package/dist/internal/surfaceRenderer.js +2 -2
- package/dist/internal/textRenderer.d.ts +1 -1
- package/dist/internal/textRenderer.js +23 -11
- package/dist/inventory-grid/index.js +13 -3
- package/dist/inventory-grid/outline.d.ts +9 -0
- package/dist/inventory-grid/outline.js +54 -0
- package/dist/loading/index.d.ts +58 -0
- package/dist/loading/index.js +188 -0
- package/dist/loading/locales.d.ts +15 -0
- package/dist/loading/locales.js +34 -0
- package/dist/loading/model.d.ts +6 -0
- package/dist/loading/model.js +29 -0
- package/dist/masonry/index.d.ts +25 -0
- package/dist/masonry/index.js +227 -0
- package/dist/masonry/layout.d.ts +35 -0
- package/dist/masonry/layout.js +67 -0
- package/dist/number-ticker/RollingValue.d.ts +6 -0
- package/dist/number-ticker/RollingValue.js +10 -0
- package/dist/number-ticker/index.d.ts +4 -0
- package/dist/number-ticker/index.js +55 -7
- package/dist/number-ticker/rolling.d.ts +35 -0
- package/dist/number-ticker/rolling.js +87 -0
- package/dist/selection/CircularSwitchVisual.d.ts +8 -0
- package/dist/selection/CircularSwitchVisual.js +82 -0
- package/dist/selection/Switch.d.ts +7 -0
- package/dist/selection/Switch.js +15 -3
- package/dist/slider/circular.d.ts +35 -0
- package/dist/slider/circular.js +196 -0
- package/dist/slider/circularMath.d.ts +10 -0
- package/dist/slider/circularMath.js +34 -0
- package/dist/slider/index.d.ts +5 -0
- package/dist/slider/index.js +5 -3
- package/dist/styles/base.css +4 -2
- package/dist/styles/breadcrumb.css +1 -1
- package/dist/styles/guide.css +202 -0
- package/dist/styles/image-viewer.css +20 -0
- package/dist/styles/infinite-scroll.css +171 -0
- package/dist/styles/inventory-grid.css +20 -8
- package/dist/styles/loading.css +348 -0
- package/dist/styles/markdown.css +1 -1
- package/dist/styles/masonry.css +68 -0
- package/dist/styles/number-ticker.css +47 -1
- package/dist/styles/selection.css +50 -0
- package/dist/styles/slider.css +65 -0
- package/dist/styles/slot.css +1 -1
- package/dist/styles.css +1180 -192
- package/dist/toast/manager.d.ts +4 -4
- package/dist/tokens.css +13 -10
- package/package.json +28 -2
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { dampPullDistance, directionAxis, directionSign, hasNestedScrollOwner, isEditableOrInteractiveTarget, isPageScrollContainer, physicalBoundaryDistance, } from './model.js';
|
|
2
|
+
const slop = 8;
|
|
3
|
+
const axisRatio = 1.25;
|
|
4
|
+
export function createInfiniteScrollGesture(read) {
|
|
5
|
+
let cancelCurrent = () => { };
|
|
6
|
+
let clearClick = () => { };
|
|
7
|
+
let cancelWheel = () => { };
|
|
8
|
+
let wheelSession = null;
|
|
9
|
+
function wheel(event) {
|
|
10
|
+
const options = read();
|
|
11
|
+
const { root, container } = options;
|
|
12
|
+
const view = root?.ownerDocument.defaultView;
|
|
13
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
14
|
+
if (!root || !container || !view || !target || !root.contains(target)
|
|
15
|
+
|| event.defaultPrevented || event.ctrlKey || !event.cancelable
|
|
16
|
+
|| isEditableOrInteractiveTarget(target))
|
|
17
|
+
return;
|
|
18
|
+
const dx = event.shiftKey && !event.deltaX ? event.deltaY : event.deltaX;
|
|
19
|
+
const dy = event.shiftKey && !event.deltaX ? 0 : event.deltaY;
|
|
20
|
+
const horizontal = Math.abs(dx) > Math.abs(dy) * axisRatio;
|
|
21
|
+
const vertical = Math.abs(dy) > Math.abs(dx) * axisRatio;
|
|
22
|
+
if (!horizontal && !vertical) {
|
|
23
|
+
cancelWheel();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const direction = horizontal ? (dx < 0 ? 'left' : 'right') : (dy < 0 ? 'top' : 'bottom');
|
|
27
|
+
if (!options.directions.includes(direction) || !options.isEnabled(direction)
|
|
28
|
+
|| hasNestedScrollOwner(target, root, container, directionAxis(direction))
|
|
29
|
+
|| (isPageScrollContainer(container) && !options.isExplicitlyEnabled(direction))) {
|
|
30
|
+
cancelWheel();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (wheelSession && wheelSession.direction !== direction)
|
|
34
|
+
cancelWheel();
|
|
35
|
+
// Native scrolling reaches the edge first; only later outward input belongs to the pull.
|
|
36
|
+
if (!wheelSession && physicalBoundaryDistance(container, options.sentinel(direction), direction, root) > 1)
|
|
37
|
+
return;
|
|
38
|
+
if (!wheelSession) {
|
|
39
|
+
cancelCurrent();
|
|
40
|
+
const session = { direction, distance: 0, timer: 0 };
|
|
41
|
+
wheelSession = session;
|
|
42
|
+
const abort = () => {
|
|
43
|
+
view.clearTimeout(session.timer);
|
|
44
|
+
view.removeEventListener('blur', abort);
|
|
45
|
+
wheelSession = null;
|
|
46
|
+
options.onPull(null, 0, options.threshold(direction), false);
|
|
47
|
+
cancelWheel = () => { };
|
|
48
|
+
};
|
|
49
|
+
cancelWheel = abort;
|
|
50
|
+
view.addEventListener('blur', abort);
|
|
51
|
+
}
|
|
52
|
+
const session = wheelSession;
|
|
53
|
+
const threshold = Math.max(1, options.threshold(direction));
|
|
54
|
+
const unit = event.deltaMode === 1 ? 16 : event.deltaMode === 2
|
|
55
|
+
? (horizontal ? container.clientWidth : container.clientHeight) : 1;
|
|
56
|
+
const rect = container.getBoundingClientRect();
|
|
57
|
+
const scale = horizontal ? container.clientWidth / (rect.width || container.clientWidth)
|
|
58
|
+
: container.clientHeight / (rect.height || container.clientHeight);
|
|
59
|
+
session.distance += Math.min(Math.abs(horizontal ? dx : dy) * unit * scale, threshold);
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
options.onPull(direction, dampPullDistance(session.distance, threshold), threshold, session.distance >= threshold);
|
|
63
|
+
view.clearTimeout(session.timer);
|
|
64
|
+
session.timer = view.setTimeout(() => {
|
|
65
|
+
const latest = read();
|
|
66
|
+
const commit = session.distance >= threshold && root.isConnected
|
|
67
|
+
&& latest.root === root && latest.container === container && latest.isEnabled(direction);
|
|
68
|
+
cancelWheel();
|
|
69
|
+
if (commit)
|
|
70
|
+
latest.onCommit(direction);
|
|
71
|
+
}, 160);
|
|
72
|
+
}
|
|
73
|
+
function start(event) {
|
|
74
|
+
cancelWheel();
|
|
75
|
+
cancelCurrent();
|
|
76
|
+
clearClick();
|
|
77
|
+
const options = read();
|
|
78
|
+
const { root, container } = options;
|
|
79
|
+
const view = root?.ownerDocument.defaultView;
|
|
80
|
+
if (!root || !container || !view || event.defaultPrevented || event.touches.length !== 1)
|
|
81
|
+
return;
|
|
82
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
83
|
+
if (!target || !root.contains(target) || isEditableOrInteractiveTarget(target))
|
|
84
|
+
return;
|
|
85
|
+
const viewportWidth = root.ownerDocument.documentElement.clientWidth || view.innerWidth;
|
|
86
|
+
const touch = event.touches[0];
|
|
87
|
+
const inset = Math.max(0, options.systemGestureInset);
|
|
88
|
+
const page = isPageScrollContainer(container);
|
|
89
|
+
const candidates = options.directions.filter(direction => {
|
|
90
|
+
if (!options.isEnabled(direction))
|
|
91
|
+
return false;
|
|
92
|
+
const axis = directionAxis(direction);
|
|
93
|
+
const scrollable = axis === 'vertical'
|
|
94
|
+
? container.scrollHeight > container.clientHeight + 1
|
|
95
|
+
: container.scrollWidth > container.clientWidth + 1;
|
|
96
|
+
if ((page || (container === root && !scrollable)) && !options.isExplicitlyEnabled(direction))
|
|
97
|
+
return false;
|
|
98
|
+
if (physicalBoundaryDistance(container, options.sentinel(direction), direction, root) > 1)
|
|
99
|
+
return false;
|
|
100
|
+
if (direction === 'left' && touch.clientX <= inset)
|
|
101
|
+
return false;
|
|
102
|
+
if (direction === 'right' && viewportWidth - touch.clientX <= inset)
|
|
103
|
+
return false;
|
|
104
|
+
return !hasNestedScrollOwner(target, root, container, axis);
|
|
105
|
+
});
|
|
106
|
+
if (!candidates.length)
|
|
107
|
+
return;
|
|
108
|
+
const doc = root.ownerDocument;
|
|
109
|
+
const rect = container.getBoundingClientRect();
|
|
110
|
+
const scaleX = rect.width > 0 && container.clientWidth > 0 ? container.clientWidth / rect.width : 1;
|
|
111
|
+
const scaleY = rect.height > 0 && container.clientHeight > 0 ? container.clientHeight / rect.height : 1;
|
|
112
|
+
let direction = null;
|
|
113
|
+
let locked = false;
|
|
114
|
+
let stopped = false;
|
|
115
|
+
let rawDistance = 0;
|
|
116
|
+
let threshold = 1;
|
|
117
|
+
const suppressClick = () => {
|
|
118
|
+
clearClick();
|
|
119
|
+
const block = (click) => {
|
|
120
|
+
if (!click.detail || ('pointerType' in click && click.pointerType !== 'touch'))
|
|
121
|
+
return;
|
|
122
|
+
click.preventDefault();
|
|
123
|
+
click.stopImmediatePropagation();
|
|
124
|
+
clearClick();
|
|
125
|
+
};
|
|
126
|
+
root.addEventListener('click', block, { capture: true });
|
|
127
|
+
const timer = view.setTimeout(() => clearClick(), 700);
|
|
128
|
+
clearClick = () => {
|
|
129
|
+
root.removeEventListener('click', block, { capture: true });
|
|
130
|
+
view.clearTimeout(timer);
|
|
131
|
+
clearClick = () => { };
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const cleanup = () => {
|
|
135
|
+
if (stopped)
|
|
136
|
+
return;
|
|
137
|
+
stopped = true;
|
|
138
|
+
doc.removeEventListener('touchstart', multi);
|
|
139
|
+
doc.removeEventListener('touchmove', move);
|
|
140
|
+
doc.removeEventListener('touchend', end);
|
|
141
|
+
doc.removeEventListener('touchcancel', abort);
|
|
142
|
+
view.removeEventListener('blur', abort);
|
|
143
|
+
options.onPull(null, 0, threshold, false);
|
|
144
|
+
if (locked)
|
|
145
|
+
suppressClick();
|
|
146
|
+
cancelCurrent = () => { };
|
|
147
|
+
};
|
|
148
|
+
const valid = () => {
|
|
149
|
+
const latest = read();
|
|
150
|
+
return root.isConnected && latest.root === root && latest.container === container
|
|
151
|
+
&& (!direction || latest.isEnabled(direction));
|
|
152
|
+
};
|
|
153
|
+
const abort = () => cleanup();
|
|
154
|
+
const multi = (next) => { if (next.touches.length !== 1)
|
|
155
|
+
cleanup(); };
|
|
156
|
+
const chooseDirection = (dx, dy) => {
|
|
157
|
+
const horizontal = Math.abs(dx) >= Math.abs(dy) * axisRatio;
|
|
158
|
+
const vertical = Math.abs(dy) >= Math.abs(dx) * axisRatio;
|
|
159
|
+
if (!horizontal && !vertical)
|
|
160
|
+
return null;
|
|
161
|
+
return candidates.find(candidate => {
|
|
162
|
+
if (horizontal !== (directionAxis(candidate) === 'horizontal'))
|
|
163
|
+
return false;
|
|
164
|
+
const distance = (horizontal ? dx : dy) * directionSign(candidate);
|
|
165
|
+
return distance > 0;
|
|
166
|
+
}) ?? null;
|
|
167
|
+
};
|
|
168
|
+
const move = (next) => {
|
|
169
|
+
if (stopped)
|
|
170
|
+
return;
|
|
171
|
+
const point = Array.from(next.touches).find(item => item.identifier === touch.identifier);
|
|
172
|
+
if (!valid() || next.defaultPrevented || next.touches.length !== 1 || !point) {
|
|
173
|
+
cleanup();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const dx = (point.clientX - touch.clientX) * scaleX;
|
|
177
|
+
const dy = (point.clientY - touch.clientY) * scaleY;
|
|
178
|
+
if (!locked) {
|
|
179
|
+
if (Math.max(Math.abs(dx), Math.abs(dy)) < slop)
|
|
180
|
+
return;
|
|
181
|
+
direction = chooseDirection(dx, dy);
|
|
182
|
+
if (!direction || hasNestedScrollOwner(target, root, container, directionAxis(direction))) {
|
|
183
|
+
cleanup();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
threshold = Math.max(1, options.threshold(direction));
|
|
187
|
+
}
|
|
188
|
+
if (!next.cancelable || !direction) {
|
|
189
|
+
cleanup();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
locked = true;
|
|
193
|
+
rawDistance = Math.max(0, (directionAxis(direction) === 'horizontal' ? dx : dy) * directionSign(direction));
|
|
194
|
+
next.preventDefault();
|
|
195
|
+
options.onPull(direction, dampPullDistance(rawDistance, threshold), threshold, rawDistance >= threshold);
|
|
196
|
+
};
|
|
197
|
+
const end = (next) => {
|
|
198
|
+
const commit = !next.defaultPrevented && next.touches.length === 0 && valid() && locked
|
|
199
|
+
&& direction !== null && rawDistance >= threshold;
|
|
200
|
+
const committedDirection = direction;
|
|
201
|
+
cleanup();
|
|
202
|
+
if (commit && committedDirection)
|
|
203
|
+
read().onCommit(committedDirection);
|
|
204
|
+
};
|
|
205
|
+
doc.addEventListener('touchstart', multi, { passive: true });
|
|
206
|
+
doc.addEventListener('touchmove', move, { passive: false });
|
|
207
|
+
doc.addEventListener('touchend', end);
|
|
208
|
+
doc.addEventListener('touchcancel', abort);
|
|
209
|
+
view.addEventListener('blur', abort);
|
|
210
|
+
cancelCurrent = cleanup;
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
start,
|
|
214
|
+
wheel,
|
|
215
|
+
cancel: () => { cancelCurrent(); cancelWheel(); },
|
|
216
|
+
dispose: () => { cancelCurrent(); cancelWheel(); clearClick(); },
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
import { type InfiniteScrollDirection, type InfiniteScrollLoadSource, type InfiniteScrollStatus } from './model.js';
|
|
3
|
+
export type { InfiniteScrollDirection, InfiniteScrollLoadSource, InfiniteScrollStatus } from './model.js';
|
|
4
|
+
export type InfiniteScrollDirectional<T> = T | Partial<Record<InfiniteScrollDirection, T>>;
|
|
5
|
+
export interface InfiniteScrollDirectionState {
|
|
6
|
+
status: InfiniteScrollStatus;
|
|
7
|
+
error?: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface InfiniteScrollLoadContext {
|
|
10
|
+
direction: InfiniteScrollDirection;
|
|
11
|
+
source: InfiniteScrollLoadSource;
|
|
12
|
+
signal: AbortSignal;
|
|
13
|
+
scrollContainer: HTMLElement;
|
|
14
|
+
}
|
|
15
|
+
export interface InfiniteScrollStateChangeDetails {
|
|
16
|
+
source?: InfiniteScrollLoadSource;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
reason?: 'container-changed' | 'direction-removed' | 'disabled';
|
|
19
|
+
}
|
|
20
|
+
export interface InfiniteScrollAnchorContext {
|
|
21
|
+
direction: 'top' | 'left';
|
|
22
|
+
scrollContainer: HTMLElement;
|
|
23
|
+
content: HTMLElement;
|
|
24
|
+
}
|
|
25
|
+
export interface InfiniteScrollIndicatorRenderProps {
|
|
26
|
+
direction: InfiniteScrollDirection;
|
|
27
|
+
status: InfiniteScrollStatus;
|
|
28
|
+
progress: number;
|
|
29
|
+
error?: unknown;
|
|
30
|
+
label: string;
|
|
31
|
+
retry: () => void;
|
|
32
|
+
load: () => void;
|
|
33
|
+
}
|
|
34
|
+
export type InfiniteScrollLabelKey = InfiniteScrollStatus | 'retry' | 'loadMore';
|
|
35
|
+
export interface InfiniteScrollProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> {
|
|
36
|
+
directions?: InfiniteScrollDirection | readonly InfiniteScrollDirection[];
|
|
37
|
+
onLoad: (context: InfiniteScrollLoadContext) => void | Promise<void>;
|
|
38
|
+
state?: Partial<Record<InfiniteScrollDirection, InfiniteScrollDirectionState>>;
|
|
39
|
+
defaultState?: Partial<Record<InfiniteScrollDirection, InfiniteScrollDirectionState>>;
|
|
40
|
+
onStateChange?: (direction: InfiniteScrollDirection, state: InfiniteScrollDirectionState, details: InfiniteScrollStateChangeDetails) => void;
|
|
41
|
+
hasMore?: InfiniteScrollDirectional<boolean>;
|
|
42
|
+
disabled?: InfiniteScrollDirectional<boolean>;
|
|
43
|
+
automatic?: InfiniteScrollDirectional<boolean>;
|
|
44
|
+
scrollContainer?: HTMLElement | null | (() => HTMLElement | null);
|
|
45
|
+
triggerDistance?: InfiniteScrollDirectional<number>;
|
|
46
|
+
gestureThreshold?: InfiniteScrollDirectional<number>;
|
|
47
|
+
gesture?: InfiniteScrollDirectional<boolean>;
|
|
48
|
+
systemGestureInset?: number;
|
|
49
|
+
getAnchor?: (context: InfiniteScrollAnchorContext) => HTMLElement | null;
|
|
50
|
+
renderIndicator?: (props: InfiniteScrollIndicatorRenderProps) => ReactNode;
|
|
51
|
+
labels?: Partial<Record<InfiniteScrollLabelKey, string>>;
|
|
52
|
+
errorContent?: ReactNode | ((direction: InfiniteScrollDirection, error: unknown) => ReactNode);
|
|
53
|
+
endContent?: ReactNode | ((direction: InfiniteScrollDirection) => ReactNode);
|
|
54
|
+
}
|
|
55
|
+
export declare const InfiniteScroll: import("react").ForwardRefExoticComponent<InfiniteScrollProps & import("react").RefAttributes<HTMLDivElement>>;
|