loadshape 0.0.0-stage → 0.2.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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +269 -2
- package/dist/RouteProgress-Cq28cihc.js +81 -0
- package/dist/index-KGl0rvk4.d.ts +19 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +78 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/styles.css +173 -0
- package/dist/tanstack-router.d.ts +6 -0
- package/dist/tanstack-router.js +16 -0
- package/package.json +58 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# loadshape
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ff154bc: Add React 18 support alongside React 19, preserving forwarded refs, callback cleanup, and inert loading roots.
|
|
8
|
+
- 6426e69: Add Skeleton.Repeat to render a placeholder template multiple times without extra DOM wrappers. The application keeps ownership of placeholder data, loading state, and layout.
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- Первый публичный Skeleton для React 19: Root и Static с render и ref, заглушка из настоящего компонента, inert и CSS-переменные. В поставку входят ES modules, TypeScript declarations, отдельный CSS в @layer loadshape, примеры подключения и лицензия MIT. Скилл интеграции поставляется в репозитории отдельно от npm-пакета.
|
|
15
|
+
- b6e45b5: Добавлен RouteProgress с неопределённым прогрессом, общей задержкой появления и поддержкой reduced-motion. Отдельный импорт loadshape/tanstack-router подключает полосу к TanStack Router через optional peer; основной импорт библиотеки остаётся независимым от роутера.
|
|
16
|
+
- fc1e157: Добавлен Spinner с размером и цветом из окружения, пропами render и label и поддержкой reduced-motion. Переменная --loadshape_delay задаёт общую задержку появления Skeleton и Spinner; частные --skeleton_delay и --spinner_delay сохраняют приоритет.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Semyon Surkov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,270 @@
|
|
|
1
|
-
#
|
|
1
|
+
# loadshape
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Loading states for React 18 and 19 that use your component's layout and your application's visual tokens.
|
|
4
|
+
Skeleton paints the rendered content, Spinner inherits its size and color, and RouteProgress
|
|
5
|
+
indicates navigation without inventing a percentage.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Requires React 18 or 19.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add loadshape
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Import CSS once at the application entry:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import 'loadshape/styles.css';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Styles live in `@layer loadshape`. There is no theme or runtime stylesheet injection.
|
|
22
|
+
The core entry depends only on React; using it does not require TanStack Router.
|
|
23
|
+
|
|
24
|
+
## Skeleton
|
|
25
|
+
|
|
26
|
+
Render the same component in both states, using realistic placeholder data while loading:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { Skeleton } from 'loadshape';
|
|
30
|
+
|
|
31
|
+
type Post = { id: string; title: string; excerpt: string };
|
|
32
|
+
|
|
33
|
+
const PLACEHOLDER_POSTS: Post[] = [
|
|
34
|
+
{ id: 'placeholder-1', title: 'A typical article title', excerpt: 'A representative summary.' },
|
|
35
|
+
{ id: 'placeholder-2', title: 'Another article title', excerpt: 'A slightly longer summary.' },
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export function PostList({ data, loading }: { data?: Post[]; loading: boolean }) {
|
|
39
|
+
const posts = data ?? PLACEHOLDER_POSTS;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<section aria-busy={loading || undefined}>
|
|
43
|
+
<Skeleton.Root loading={loading}>
|
|
44
|
+
<Skeleton.Static render={<h2 />}>Recent posts</Skeleton.Static>
|
|
45
|
+
<ul>
|
|
46
|
+
{posts.map((post) => (
|
|
47
|
+
<li key={post.id}>
|
|
48
|
+
<h3>{post.title}</h3>
|
|
49
|
+
<p>{post.excerpt}</p>
|
|
50
|
+
</li>
|
|
51
|
+
))}
|
|
52
|
+
</ul>
|
|
53
|
+
</Skeleton.Root>
|
|
54
|
+
</section>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Keep the application's existing error and empty branches. Placeholder values must satisfy its
|
|
60
|
+
real types and formatters, including dates and enums. With TanStack Query, provide placeholderData
|
|
61
|
+
and pass isPlaceholderData to loading: placeholder data puts the query in success, so isPending
|
|
62
|
+
is false. Preserve cached real data during background refresh and respect disabled queries.
|
|
63
|
+
|
|
64
|
+
### Repeated placeholders
|
|
65
|
+
|
|
66
|
+
`Skeleton.Repeat` repeats a render function without adding a DOM wrapper:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
const PLACEHOLDER_COUNT = 8;
|
|
70
|
+
const PLACEHOLDER_POST = {
|
|
71
|
+
title: 'A representative article title',
|
|
72
|
+
excerpt: 'A typical summary with realistic length.',
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
<section aria-busy={loading || undefined}>
|
|
76
|
+
<Skeleton.Root loading={loading} render={<ul className={styles.list} />}>
|
|
77
|
+
{loading ? (
|
|
78
|
+
<Skeleton.Repeat count={PLACEHOLDER_COUNT}>
|
|
79
|
+
{(index) => (
|
|
80
|
+
<li>
|
|
81
|
+
<PostCard post={PLACEHOLDER_POST} position={index + 1} />
|
|
82
|
+
</li>
|
|
83
|
+
)}
|
|
84
|
+
</Skeleton.Repeat>
|
|
85
|
+
) : (
|
|
86
|
+
posts.map((post) => (
|
|
87
|
+
<li key={post.id}>
|
|
88
|
+
<PostCard post={post} />
|
|
89
|
+
</li>
|
|
90
|
+
))
|
|
91
|
+
)}
|
|
92
|
+
</Skeleton.Root>
|
|
93
|
+
</section>;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`count` is a required non-negative safe integer. Zero renders nothing and does not call
|
|
97
|
+
`children`; invalid counts throw `RangeError`. `children(index)` receives a zero-based index.
|
|
98
|
+
Repeat supplies stable positional keys, so the returned placeholder needs no key.
|
|
99
|
+
For real data, keep entity keys in your normal map.
|
|
100
|
+
|
|
101
|
+
Repeat does not enable skeleton loading, choose data, create list elements, set `aria-busy`,
|
|
102
|
+
or apply layout and spacing. Keep those in the application. If the repeated component already
|
|
103
|
+
contains `Skeleton.Root`, pass its loading prop instead of adding another Root.
|
|
104
|
+
|
|
105
|
+
### Root and Static
|
|
106
|
+
|
|
107
|
+
| Component | Props | Default element |
|
|
108
|
+
| ----------------- | ----------------------------------------------------------------------------------- | ------------------------------- |
|
|
109
|
+
| `Skeleton.Root` | Required `loading: boolean`, `render`, HTML attributes, `className`, `style`, `ref` | `div` |
|
|
110
|
+
| `Skeleton.Static` | `render`, HTML attributes, `className`, `style`, `ref` | `span` with `display: contents` |
|
|
111
|
+
|
|
112
|
+
Use render to apply the mechanism to an existing element:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<Skeleton.Root loading={loading} render={<table className={styles.table} />}>
|
|
116
|
+
<Skeleton.Static render={<thead />}>
|
|
117
|
+
<tr>
|
|
118
|
+
<th>Name</th>
|
|
119
|
+
<th>Total</th>
|
|
120
|
+
</tr>
|
|
121
|
+
</Skeleton.Static>
|
|
122
|
+
<tbody>{rows}</tbody>
|
|
123
|
+
</Skeleton.Root>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Supply valid elements inside tables, lists, and select; a default span is not a valid child there.
|
|
127
|
+
Custom components passed through render must forward HTML attributes and ref to their DOM root.
|
|
128
|
+
In React 18, wrap these components in forwardRef; React 19 also supports ref as a prop.
|
|
129
|
+
Static preserves its whole subtree, so include only content known without the request. Put a
|
|
130
|
+
dynamic value in its own element beside a static label. A static element anywhere inside a text
|
|
131
|
+
block turns off that block's text lines, because an ancestor's underline would be drawn across it;
|
|
132
|
+
keep Static a direct child of the block and wrap dynamic values in their own elements.
|
|
133
|
+
|
|
134
|
+
Root becomes inert while loading. Place aria-busy on the application's container outside Root,
|
|
135
|
+
since inert removes Root and its descendants from focus and the accessibility tree. Controls
|
|
136
|
+
that remain usable, such as cancellation, belong outside Root even if they are visually static.
|
|
137
|
+
|
|
138
|
+
### Attributes
|
|
139
|
+
|
|
140
|
+
| Attribute | Meaning |
|
|
141
|
+
| ------------------------- | ---------------------------------------------------- |
|
|
142
|
+
| `data-skeleton="loading"` | Enables the loading paint on Root |
|
|
143
|
+
| `data-skeleton-static` | Preserves a request-independent subtree |
|
|
144
|
+
| `data-skeleton-bone` | Makes an intentionally empty element a bone |
|
|
145
|
+
| `data-skeleton-contents` | Gives the default Static wrapper `display: contents` |
|
|
146
|
+
|
|
147
|
+
Skeleton paints text lines, media, and controls. Container backgrounds, borders, spacing, and SVG
|
|
148
|
+
icons remain visible. It preserves the rendered tree's geometry; it cannot predict unknown text
|
|
149
|
+
length or the number of rows in a response.
|
|
150
|
+
|
|
151
|
+
Reserve image dimensions or aspect-ratio in the component. Give inputs, selects, and buttons the
|
|
152
|
+
application's styling: painting an unstyled native control can change its size in WebKit. A chart
|
|
153
|
+
whose content is entirely SVG needs an application-owned loading representation.
|
|
154
|
+
|
|
155
|
+
## Spinner
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { Spinner } from 'loadshape';
|
|
159
|
+
|
|
160
|
+
<Spinner />;
|
|
161
|
+
<Spinner label="Loading the report" />;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Spinner defaults to span and accepts label, render, HTML attributes, className, style, and a forwarded
|
|
165
|
+
ref. Its ring is 1em and currentColor; reserve its slot or match the replaced icon to preserve
|
|
166
|
+
layout. It appears after the shared delay and slows rotation threefold under reduced-motion.
|
|
167
|
+
|
|
168
|
+
Without label it is decorative (`aria-hidden`). With label it has role=status and a visually
|
|
169
|
+
hidden text child. Mounting a live region with its initial text does not guarantee an announcement;
|
|
170
|
+
keep an application status region mounted before updating its text when that is required.
|
|
171
|
+
|
|
172
|
+
## RouteProgress
|
|
173
|
+
|
|
174
|
+
Use the general component with any router or navigation loading signal:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { RouteProgress } from 'loadshape';
|
|
178
|
+
|
|
179
|
+
<RouteProgress pending={pending} className={styles.progress} />;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
It defaults to div and accepts required pending, render, HTML attributes, className, style, and
|
|
183
|
+
forwarded ref. Keep it mounted in the navigation layout. The application owns positioning, width,
|
|
184
|
+
stacking order, color, and accessible status. In normal flow it reserves its height while hidden.
|
|
185
|
+
Completion and cancellation hide it immediately. Reduced-motion shows a stationary full-width bar.
|
|
186
|
+
|
|
187
|
+
If a new operation replaces a pending operation without an intermediate false, give the general
|
|
188
|
+
component a new React key to restart its appearance delay.
|
|
189
|
+
|
|
190
|
+
### TanStack Router
|
|
191
|
+
|
|
192
|
+
Install the optional peer `@tanstack/react-router` at a compatible `^1.170.41` version, then use
|
|
193
|
+
the adapter inside RouterProvider's persistent root layout:
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { Outlet } from '@tanstack/react-router';
|
|
197
|
+
import { RouteProgress } from 'loadshape/tanstack-router';
|
|
198
|
+
|
|
199
|
+
function RootLayout() {
|
|
200
|
+
return (
|
|
201
|
+
<>
|
|
202
|
+
<RouteProgress className={styles.progress} />
|
|
203
|
+
<Outlet />
|
|
204
|
+
</>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The adapter owns pending and resets the delay when the destination href changes. Its remaining
|
|
210
|
+
props match the general component. Both components are decorative; the application owns aria-busy
|
|
211
|
+
and any accessible navigation status.
|
|
212
|
+
|
|
213
|
+
## CSS variables
|
|
214
|
+
|
|
215
|
+
Override variables using the application's tokens:
|
|
216
|
+
|
|
217
|
+
```css
|
|
218
|
+
.progress {
|
|
219
|
+
position: fixed;
|
|
220
|
+
inset-block-start: 0;
|
|
221
|
+
inset-inline: 0;
|
|
222
|
+
color: var(--accent);
|
|
223
|
+
--route_progress_thickness: var(--spacing_half);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
| Variable | Default |
|
|
228
|
+
| ---------------------------- | ---------------------------------------------------- |
|
|
229
|
+
| `--loadshape_delay` | `150ms` |
|
|
230
|
+
| `--skeleton_delay` | Shared delay |
|
|
231
|
+
| `--skeleton_color` | `color-mix(in oklab, currentColor 14%, transparent)` |
|
|
232
|
+
| `--skeleton_duration` | `1.4s` |
|
|
233
|
+
| `--skeleton_shimmer_opacity` | `0.35` |
|
|
234
|
+
| `--spinner_delay` | Shared delay |
|
|
235
|
+
| `--spinner_size` | `1em` |
|
|
236
|
+
| `--spinner_thickness` | `0.125em` |
|
|
237
|
+
| `--spinner_duration` | `0.8s` |
|
|
238
|
+
| `--route_progress_delay` | Shared delay |
|
|
239
|
+
| `--route_progress_thickness` | `0.125em` |
|
|
240
|
+
| `--route_progress_duration` | `1.4s` |
|
|
241
|
+
|
|
242
|
+
Component delays override the shared delay. All three components wait before appearing, so short
|
|
243
|
+
loads do not flash. Skeleton shimmer stops under reduced-motion. Variables starting with `--_`
|
|
244
|
+
are internal implementation details.
|
|
245
|
+
|
|
246
|
+
## Compatibility and scope
|
|
247
|
+
|
|
248
|
+
ES modules and TypeScript declarations are supplied for both JavaScript entry points. CSS must
|
|
249
|
+
be imported separately. CommonJS and legacy CSS engines are outside this release's scope.
|
|
250
|
+
The styles use modern CSS including layers, :has(), color-mix(), masks, and the rotate property.
|
|
251
|
+
Checks cover current Chromium and WebKit, without claiming every browser or screen reader version.
|
|
252
|
+
|
|
253
|
+
loadshape provides loading paint and indicators. Fetching, error and empty states, visual tokens,
|
|
254
|
+
and predicting unknown content remain application responsibilities.
|
|
255
|
+
|
|
256
|
+
## Local checkout
|
|
257
|
+
|
|
258
|
+
To try an unreleased build, pack the library and install the archive:
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
pnpm --filter loadshape build
|
|
262
|
+
pnpm --filter loadshape pack --pack-destination /tmp
|
|
263
|
+
# In the consuming application:
|
|
264
|
+
pnpm add /tmp/loadshape-0.2.0.tgz
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Use pnpm for the archive: npm does not apply `publishConfig.exports` and leaves `source` conditions
|
|
268
|
+
and no license file in the tarball.
|
|
269
|
+
|
|
270
|
+
License: MIT.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cloneElement, forwardRef, useMemo, version } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
const IS_REACT_19 = Number.parseInt(version, 10) >= 19;
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/utils/useRenderElement.ts
|
|
6
|
+
const EVENT_HANDLER = /^on[A-Z]/;
|
|
7
|
+
/**
|
|
8
|
+
* Слитый ref возвращает общую очистку: колбэк-ref в React 19 может вернуть свою очистку, и без
|
|
9
|
+
* этого подписка из него (например, observer.disconnect) копилась бы с каждым рендером.
|
|
10
|
+
*/
|
|
11
|
+
function mergeRefs(refs) {
|
|
12
|
+
let cleanups = [];
|
|
13
|
+
const cleanup = () => {
|
|
14
|
+
const pending = cleanups;
|
|
15
|
+
cleanups = [];
|
|
16
|
+
for (const dispose of pending) dispose();
|
|
17
|
+
};
|
|
18
|
+
return (node) => {
|
|
19
|
+
cleanup();
|
|
20
|
+
if (node === null) return;
|
|
21
|
+
cleanups = refs.flatMap((ref) => {
|
|
22
|
+
if (typeof ref === "function") {
|
|
23
|
+
const dispose = ref(node);
|
|
24
|
+
return [typeof dispose === "function" ? dispose : () => ref(null)];
|
|
25
|
+
}
|
|
26
|
+
if (ref) {
|
|
27
|
+
ref.current = node;
|
|
28
|
+
return [() => {
|
|
29
|
+
ref.current = null;
|
|
30
|
+
}];
|
|
31
|
+
}
|
|
32
|
+
return [];
|
|
33
|
+
});
|
|
34
|
+
if (IS_REACT_19) return cleanup;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function mergeHandlers(own, passed) {
|
|
38
|
+
if (typeof own !== "function" || typeof passed !== "function") return passed ?? own;
|
|
39
|
+
return (...args) => {
|
|
40
|
+
own(...args);
|
|
41
|
+
passed(...args);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Рисует элемент из пропа render с пропами компонента, как render у Base UI: className и style
|
|
46
|
+
* объединяются, обработчики событий вызываются оба, ref сливаются, остальные пропы компонента
|
|
47
|
+
* перебивают пропы элемента. Атрибуты состояния передаются последними и перебивают всё: проект
|
|
48
|
+
* не должен случайно снять data-skeleton или inert, передав свой атрибут с тем же именем.
|
|
49
|
+
*/
|
|
50
|
+
function useRenderElement(element, props, state = {}) {
|
|
51
|
+
const own = element.props;
|
|
52
|
+
const merged = { ...props };
|
|
53
|
+
for (const key of Object.keys(props)) if (EVENT_HANDLER.test(key)) merged[key] = mergeHandlers(own[key], props[key]);
|
|
54
|
+
const className = [own.className, props.className].filter(Boolean).join(" ");
|
|
55
|
+
const ownRef = IS_REACT_19 ? own.ref : element.ref;
|
|
56
|
+
const ref = useMemo(() => ownRef || props.ref ? mergeRefs([ownRef, props.ref]) : void 0, [ownRef, props.ref]);
|
|
57
|
+
return cloneElement(element, {
|
|
58
|
+
...merged,
|
|
59
|
+
...state,
|
|
60
|
+
className: className || void 0,
|
|
61
|
+
style: own.style || props.style ? {
|
|
62
|
+
...own.style,
|
|
63
|
+
...props.style
|
|
64
|
+
} : void 0,
|
|
65
|
+
ref,
|
|
66
|
+
children: state.children ?? props.children ?? own.children
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/route-progress/RouteProgress.tsx
|
|
71
|
+
const RouteProgress = forwardRef(function RouteProgress({ pending, render = /* @__PURE__ */ jsx("div", {}), ...props }, ref) {
|
|
72
|
+
return useRenderElement(render, {
|
|
73
|
+
...props,
|
|
74
|
+
ref
|
|
75
|
+
}, {
|
|
76
|
+
"data-route-progress": pending ? "pending" : "",
|
|
77
|
+
"aria-hidden": true
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { useRenderElement as n, IS_REACT_19 as r, RouteProgress as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CSSProperties, ForwardRefExoticComponent, HTMLAttributes, PropsWithoutRef, ReactElement, ReactNode, Ref, RefAttributes } from "react";
|
|
2
|
+
//#region src/utils/useRenderElement.d.ts
|
|
3
|
+
type ElementProps = Record<string, unknown> & {
|
|
4
|
+
className?: string;
|
|
5
|
+
style?: CSSProperties;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
ref?: Ref<unknown>;
|
|
8
|
+
};
|
|
9
|
+
type RenderElement = ReactElement<ElementProps>;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/route-progress/RouteProgress.d.ts
|
|
12
|
+
interface RouteProgressProps extends HTMLAttributes<HTMLElement> {
|
|
13
|
+
pending: boolean;
|
|
14
|
+
render?: RenderElement;
|
|
15
|
+
ref?: Ref<HTMLElement>;
|
|
16
|
+
}
|
|
17
|
+
declare const RouteProgress: ForwardRefExoticComponent<PropsWithoutRef<RouteProgressProps> & RefAttributes<HTMLElement>>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { RouteProgressProps as n, RenderElement as r, RouteProgress as t };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { n as RouteProgressProps, r as RenderElement, t as RouteProgress } from "./index-KGl0rvk4.js";
|
|
2
|
+
import { ForwardRefExoticComponent, HTMLAttributes, PropsWithoutRef, ReactElement, ReactNode, Ref, RefAttributes } from "react";
|
|
3
|
+
//#region src/skeleton/Root.d.ts
|
|
4
|
+
interface RootProps extends HTMLAttributes<HTMLElement> {
|
|
5
|
+
/** Пока true, поддерево рисуется заглушкой той же геометрии. */
|
|
6
|
+
loading: boolean;
|
|
7
|
+
/** Элемент, которым рисуется корень вместо div, например render={<ul />}. */
|
|
8
|
+
render?: RenderElement;
|
|
9
|
+
ref?: Ref<HTMLElement>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* На время загрузки корень получает inert: заглушка не берёт фокус и не читается скринридером.
|
|
13
|
+
* inert убирает из дерева доступности и сам корень, поэтому aria-busy на нём никто бы не
|
|
14
|
+
* услышал; aria-busy ставит проект на свой контейнер вокруг корня.
|
|
15
|
+
*/
|
|
16
|
+
declare const Root: ForwardRefExoticComponent<PropsWithoutRef<RootProps> & RefAttributes<HTMLElement>>;
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/skeleton/Static.d.ts
|
|
19
|
+
interface StaticProps extends HTMLAttributes<HTMLElement> {
|
|
20
|
+
/** Элемент, который помечается статичным вместо обёртки, например render={<h2 />}. */
|
|
21
|
+
render?: RenderElement;
|
|
22
|
+
ref?: Ref<HTMLElement>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Без render содержимое оборачивается в span с display: contents из styles.css: у такой обёртки
|
|
26
|
+
* нет своего бокса, поэтому в потоке и во флексе раскладка вокруг статичного не меняется, а
|
|
27
|
+
* className на ней задаёт только наследуемые свойства. Внутри table, thead, tbody, tr, ul и select
|
|
28
|
+
* span ребёнком быть не может, там нужен render с элементом этого уровня, например
|
|
29
|
+
* render={<thead />}.
|
|
30
|
+
*/
|
|
31
|
+
declare const Static: ForwardRefExoticComponent<PropsWithoutRef<StaticProps> & RefAttributes<HTMLElement>>;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/skeleton/Repeat.d.ts
|
|
34
|
+
interface RepeatProps {
|
|
35
|
+
/** Количество повторений; неотрицательное безопасное целое число. */
|
|
36
|
+
count: number;
|
|
37
|
+
/** Шаблон элемента. Получает индекс от нуля; данные и loading задаёт приложение. */
|
|
38
|
+
children: (index: number) => ReactNode;
|
|
39
|
+
}
|
|
40
|
+
/** Повторяет шаблон со стабильными ключами без дополнительных DOM-элементов. */
|
|
41
|
+
declare function Repeat({ count, children }: RepeatProps): ReactElement;
|
|
42
|
+
declare namespace index_d_exports {
|
|
43
|
+
export { Repeat, RepeatProps, Root, RootProps, Static, StaticProps };
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/spinner/Spinner.d.ts
|
|
47
|
+
/**
|
|
48
|
+
* children не принимаются: с label содержимым элемента становится визуально скрытая подпись, и
|
|
49
|
+
* переданные children молча терялись бы.
|
|
50
|
+
*/
|
|
51
|
+
interface SpinnerProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
|
|
52
|
+
/** Подпись для скринридера; без неё спиннер декоративный. */
|
|
53
|
+
label?: string;
|
|
54
|
+
/** Элемент, которым рисуется спиннер вместо span. */
|
|
55
|
+
render?: RenderElement;
|
|
56
|
+
ref?: Ref<HTMLElement>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Без label спиннер скрыт от скринридера: обычно он стоит в кнопке или контейнере, где загрузку
|
|
60
|
+
* уже сообщает aria-busy, и второй сигнал был бы лишним. С label он становится role="status", а
|
|
61
|
+
* подпись лежит визуально скрытым текстом внутри, потому что живой регион озвучивает содержимое,
|
|
62
|
+
* а не aria-label. Смонтированный сразу с подписью регион скринридер может не объявить сам, тогда
|
|
63
|
+
* подпись служит именем при навигации; для гарантированного объявления регион статуса держат в
|
|
64
|
+
* разметке заранее.
|
|
65
|
+
*/
|
|
66
|
+
export declare const Spinner: ForwardRefExoticComponent<PropsWithoutRef<SpinnerProps> & RefAttributes<HTMLElement>>;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { RouteProgress, type RouteProgressProps, index_d_exports as Skeleton, type SpinnerProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { n as useRenderElement, r as IS_REACT_19, t as RouteProgress } from "./RouteProgress-Cq28cihc.js";
|
|
3
|
+
import { Fragment, forwardRef } from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/skeleton/Root.tsx
|
|
6
|
+
const LOADING_STATE = {
|
|
7
|
+
"data-skeleton": "loading",
|
|
8
|
+
inert: IS_REACT_19 ? true : ""
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* На время загрузки корень получает inert: заглушка не берёт фокус и не читается скринридером.
|
|
12
|
+
* inert убирает из дерева доступности и сам корень, поэтому aria-busy на нём никто бы не
|
|
13
|
+
* услышал; aria-busy ставит проект на свой контейнер вокруг корня.
|
|
14
|
+
*/
|
|
15
|
+
const Root = forwardRef(function Root({ loading, render = /* @__PURE__ */ jsx("div", {}), ...props }, ref) {
|
|
16
|
+
return useRenderElement(render, {
|
|
17
|
+
...props,
|
|
18
|
+
ref
|
|
19
|
+
}, loading ? LOADING_STATE : {});
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/skeleton/Static.tsx
|
|
23
|
+
const STATIC_STATE = { "data-skeleton-static": "" };
|
|
24
|
+
/**
|
|
25
|
+
* Без render содержимое оборачивается в span с display: contents из styles.css: у такой обёртки
|
|
26
|
+
* нет своего бокса, поэтому в потоке и во флексе раскладка вокруг статичного не меняется, а
|
|
27
|
+
* className на ней задаёт только наследуемые свойства. Внутри table, thead, tbody, tr, ul и select
|
|
28
|
+
* span ребёнком быть не может, там нужен render с элементом этого уровня, например
|
|
29
|
+
* render={<thead />}.
|
|
30
|
+
*/
|
|
31
|
+
const Static = forwardRef(function Static({ render = /* @__PURE__ */ jsx("span", { "data-skeleton-contents": "" }), ...props }, ref) {
|
|
32
|
+
return useRenderElement(render, {
|
|
33
|
+
...props,
|
|
34
|
+
ref
|
|
35
|
+
}, STATIC_STATE);
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/skeleton/Repeat.tsx
|
|
39
|
+
/** Повторяет шаблон со стабильными ключами без дополнительных DOM-элементов. */
|
|
40
|
+
function Repeat({ count, children }) {
|
|
41
|
+
if (!Number.isSafeInteger(count) || count < 0) throw new RangeError("Skeleton.Repeat count must be a non-negative safe integer.");
|
|
42
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: Array.from({ length: count }, (_, index) => /* @__PURE__ */ jsx(Fragment, { children: children(index) }, index)) });
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/skeleton/index.ts
|
|
46
|
+
var skeleton_exports = /* @__PURE__ */ __exportAll({
|
|
47
|
+
Repeat: () => Repeat,
|
|
48
|
+
Root: () => Root,
|
|
49
|
+
Static: () => Static
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/spinner/Spinner.tsx
|
|
53
|
+
/**
|
|
54
|
+
* Без label спиннер скрыт от скринридера: обычно он стоит в кнопке или контейнере, где загрузку
|
|
55
|
+
* уже сообщает aria-busy, и второй сигнал был бы лишним. С label он становится role="status", а
|
|
56
|
+
* подпись лежит визуально скрытым текстом внутри, потому что живой регион озвучивает содержимое,
|
|
57
|
+
* а не aria-label. Смонтированный сразу с подписью регион скринридер может не объявить сам, тогда
|
|
58
|
+
* подпись служит именем при навигации; для гарантированного объявления регион статуса держат в
|
|
59
|
+
* разметке заранее.
|
|
60
|
+
*/
|
|
61
|
+
const Spinner = forwardRef(function Spinner({ label, render = /* @__PURE__ */ jsx("span", {}), ...props }, ref) {
|
|
62
|
+
return useRenderElement(render, {
|
|
63
|
+
...props,
|
|
64
|
+
ref
|
|
65
|
+
}, label ? {
|
|
66
|
+
"data-spinner": "",
|
|
67
|
+
role: "status",
|
|
68
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
69
|
+
"data-spinner-label": "",
|
|
70
|
+
children: label
|
|
71
|
+
})
|
|
72
|
+
} : {
|
|
73
|
+
"data-spinner": "",
|
|
74
|
+
"aria-hidden": true
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//#endregion
|
|
78
|
+
export { RouteProgress, skeleton_exports as Skeleton, Spinner };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { __exportAll as t };
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
@layer loadshape {
|
|
2
|
+
[data-skeleton="loading"] {
|
|
3
|
+
-webkit-text-fill-color: transparent !important;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
[data-skeleton="loading"] :where(:not([data-skeleton-static], [data-skeleton-static] *)) {
|
|
7
|
+
text-underline-offset: -.62em !important;
|
|
8
|
+
text-decoration-skip-ink: none !important;
|
|
9
|
+
text-decoration-line: none !important;
|
|
10
|
+
text-decoration-thickness: .72em !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-skeleton="loading"]:where(:not(:has( > :not(a, abbr, b, bdi, bdo, br, cite, code, data, del, dfn, em, i, img, input, ins, kbd, label, mark, output, q, ruby, s, samp, select, small, span, strong, sub, sup, svg, textarea, time, u, var, wbr), [data-skeleton-static])):not(:empty, img, svg, video, canvas, picture, input, select, textarea, button, [role="button"], [data-skeleton-bone], [data-skeleton-static], [data-skeleton-static] *)), [data-skeleton="loading"] :where(:not(:has( > :not(a, abbr, b, bdi, bdo, br, cite, code, data, del, dfn, em, i, img, input, ins, kbd, label, mark, output, q, ruby, s, samp, select, small, span, strong, sub, sup, svg, textarea, time, u, var, wbr), [data-skeleton-static])):not(:is([data-skeleton="loading"], [data-skeleton="loading"] *):not(:has( > :not(a, abbr, b, bdi, bdo, br, cite, code, data, del, dfn, em, i, img, input, ins, kbd, label, mark, output, q, ruby, s, samp, select, small, span, strong, sub, sup, svg, textarea, time, u, var, wbr), [data-skeleton-static])) > *):not(:empty, img, svg, video, canvas, picture, input, select, textarea, button, [role="button"], [data-skeleton-bone], [data-skeleton-static], [data-skeleton-static] *)) {
|
|
14
|
+
--_skeleton_paint: var(--skeleton_color, color-mix(in oklab, currentColor 14%, transparent));
|
|
15
|
+
text-decoration-line: underline !important;
|
|
16
|
+
text-decoration-style: solid !important;
|
|
17
|
+
text-decoration-color: var(--_skeleton_paint) !important;
|
|
18
|
+
text-underline-offset: -.62em !important;
|
|
19
|
+
text-decoration-skip-ink: none !important;
|
|
20
|
+
animation: skeleton_delay 0s var(--skeleton_delay, var(--loadshape_delay, .15s)) backwards !important;
|
|
21
|
+
text-decoration-thickness: .72em !important;
|
|
22
|
+
|
|
23
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
24
|
+
animation: skeleton_delay 0s var(--skeleton_delay, var(--loadshape_delay, .15s)) backwards,
|
|
25
|
+
skeleton_shimmer var(--skeleton_duration, 1.4s) linear var(--skeleton_delay, var(--loadshape_delay, .15s)) infinite !important;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
[data-skeleton="loading"]:where(img, video, canvas, input, select, textarea, button, [role="button"], [data-skeleton-bone]), [data-skeleton="loading"] :where(img, video, canvas, input, select, textarea, button, [role="button"], [data-skeleton-bone]):where(:not([data-skeleton-static], [data-skeleton-static] *)) {
|
|
30
|
+
--_skeleton_paint: var(--skeleton_color, color-mix(in oklab, currentColor 14%, transparent));
|
|
31
|
+
color: inherit !important;
|
|
32
|
+
background: var(--_skeleton_paint) !important;
|
|
33
|
+
object-position: -100000px 0 !important;
|
|
34
|
+
resize: none !important;
|
|
35
|
+
animation: skeleton_delay 0s var(--skeleton_delay, var(--loadshape_delay, .15s)) backwards !important;
|
|
36
|
+
|
|
37
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
38
|
+
animation: skeleton_delay 0s var(--skeleton_delay, var(--loadshape_delay, .15s)) backwards,
|
|
39
|
+
skeleton_shimmer var(--skeleton_duration, 1.4s) linear var(--skeleton_delay, var(--loadshape_delay, .15s)) infinite !important;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
[data-skeleton="loading"]:where(button, [role="button"], [data-skeleton-bone]) > :not([data-skeleton-static], [data-skeleton-static] *), [data-skeleton="loading"] :where(button, [role="button"], [data-skeleton-bone]):where(:not([data-skeleton-static], [data-skeleton-static] *)) > :not([data-skeleton-static], [data-skeleton-static] *) {
|
|
44
|
+
visibility: hidden !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
[data-skeleton-contents] {
|
|
48
|
+
display: contents;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
[data-skeleton="loading"] [data-skeleton-static] {
|
|
52
|
+
-webkit-text-fill-color: currentcolor !important;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-spinner] {
|
|
56
|
+
box-sizing: border-box;
|
|
57
|
+
inline-size: var(--spinner_size, 1em);
|
|
58
|
+
block-size: var(--spinner_size, 1em);
|
|
59
|
+
vertical-align: -.125em;
|
|
60
|
+
border: var(--spinner_thickness, .125em) solid
|
|
61
|
+
color-mix(in oklab, currentColor 25%, transparent);
|
|
62
|
+
animation: spinner_delay 0s var(--spinner_delay, var(--loadshape_delay, .15s)) backwards,
|
|
63
|
+
spinner_rotate var(--spinner_duration, .8s) linear infinite;
|
|
64
|
+
border-block-start-color: currentColor;
|
|
65
|
+
border-radius: 50%;
|
|
66
|
+
flex-shrink: 0;
|
|
67
|
+
display: inline-block;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (prefers-reduced-motion: reduce) {
|
|
71
|
+
[data-spinner] {
|
|
72
|
+
animation-duration: 0s, calc(var(--spinner_duration, .8s) * 3);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@keyframes spinner_rotate {
|
|
77
|
+
to {
|
|
78
|
+
rotate: 1turn;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
[data-spinner-label] {
|
|
83
|
+
clip-path: inset(50%);
|
|
84
|
+
white-space: nowrap;
|
|
85
|
+
block-size: 1px;
|
|
86
|
+
inline-size: 1px;
|
|
87
|
+
position: absolute;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@keyframes spinner_delay {
|
|
92
|
+
from, to {
|
|
93
|
+
opacity: 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[data-route-progress] {
|
|
98
|
+
block-size: var(--route_progress_thickness, .125em);
|
|
99
|
+
opacity: 0;
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
display: block;
|
|
102
|
+
position: relative;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[data-route-progress]:before {
|
|
107
|
+
content: "";
|
|
108
|
+
background: currentColor;
|
|
109
|
+
inline-size: 40%;
|
|
110
|
+
position: absolute;
|
|
111
|
+
inset-block: 0;
|
|
112
|
+
inset-inline-start: 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
[data-route-progress="pending"] {
|
|
116
|
+
opacity: 1;
|
|
117
|
+
animation: route_progress_delay 0s var(--route_progress_delay, var(--loadshape_delay, .15s))
|
|
118
|
+
backwards;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[data-route-progress="pending"]:before {
|
|
122
|
+
animation: route_progress_slide var(--route_progress_duration, 1.4s) linear infinite;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@media (prefers-reduced-motion: reduce) {
|
|
126
|
+
[data-route-progress="pending"]:before {
|
|
127
|
+
inline-size: 100%;
|
|
128
|
+
animation: none;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@keyframes route_progress_delay {
|
|
133
|
+
from, to {
|
|
134
|
+
opacity: 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes route_progress_slide {
|
|
139
|
+
from {
|
|
140
|
+
transform: translateX(-100%);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
to {
|
|
144
|
+
transform: translateX(250%);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@keyframes skeleton_shimmer {
|
|
149
|
+
from {
|
|
150
|
+
mask-image: linear-gradient(90deg,
|
|
151
|
+
#000 0 35%,
|
|
152
|
+
rgb(0 0 0 / var(--skeleton_shimmer_opacity, .35)) 50%,
|
|
153
|
+
#000 65% 100%);
|
|
154
|
+
mask-position: 100% 0;
|
|
155
|
+
mask-size: 300% 100%;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
to {
|
|
159
|
+
mask-image: linear-gradient(90deg,
|
|
160
|
+
#000 0 35%,
|
|
161
|
+
rgb(0 0 0 / var(--skeleton_shimmer_opacity, .35)) 50%,
|
|
162
|
+
#000 65% 100%);
|
|
163
|
+
mask-position: 0 0;
|
|
164
|
+
mask-size: 300% 100%;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@keyframes skeleton_delay {
|
|
169
|
+
from, to {
|
|
170
|
+
--_skeleton_paint: transparent;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { n as RouteProgressProps$1 } from "./index-KGl0rvk4.js";
|
|
2
|
+
import { ForwardRefExoticComponent, PropsWithoutRef, RefAttributes } from "react";
|
|
3
|
+
//#region src/tanstack-router.d.ts
|
|
4
|
+
export type RouteProgressProps = Omit<RouteProgressProps$1, "pending">;
|
|
5
|
+
export declare const RouteProgress: ForwardRefExoticComponent<PropsWithoutRef<RouteProgressProps> & RefAttributes<HTMLElement>>;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { t as RouteProgress$1 } from "./RouteProgress-Cq28cihc.js";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { useRouterState } from "@tanstack/react-router";
|
|
5
|
+
//#region src/tanstack-router.tsx
|
|
6
|
+
const RouteProgress = forwardRef(function RouteProgress(props, ref) {
|
|
7
|
+
const pending = useRouterState({ select: (state) => state.status === "pending" });
|
|
8
|
+
const destination = useRouterState({ select: (state) => state.location.href });
|
|
9
|
+
return /* @__PURE__ */ jsx(RouteProgress$1, {
|
|
10
|
+
...props,
|
|
11
|
+
ref,
|
|
12
|
+
pending
|
|
13
|
+
}, destination);
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { RouteProgress };
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loadshape",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Состояния загрузки для React: скелетон из самого компонента, спиннер и полоса перехода",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"**/*.css"
|
|
9
|
+
],
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"CHANGELOG.md"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./styles.css": "./dist/styles.css",
|
|
20
|
+
"./tanstack-router": {
|
|
21
|
+
"types": "./dist/tanstack-router.d.ts",
|
|
22
|
+
"default": "./dist/tanstack-router.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@tanstack/react-router": "^1.170.41",
|
|
28
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependenciesMeta": {
|
|
31
|
+
"@tanstack/react-router": {
|
|
32
|
+
"optional": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@tanstack/react-router": "^1.170.41",
|
|
37
|
+
"@tsdown/css": "^0.23.0",
|
|
38
|
+
"@types/react": "^19.3.0",
|
|
39
|
+
"@typescript/native": "npm:typescript@^7.0.2",
|
|
40
|
+
"react": "^19.3.0",
|
|
41
|
+
"tsdown": "^0.23.0",
|
|
42
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/semyonsurkov/loadshape.git",
|
|
50
|
+
"directory": "packages/loadshape"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/semyonsurkov/loadshape#readme",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/semyonsurkov/loadshape/issues"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsdown",
|
|
58
|
+
"dev": "tsdown --watch",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
60
|
+
}
|
|
6
61
|
}
|