react-loadly 2.3.0 → 2.4.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 +135 -140
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/dist/types/components/organisms/AutoSkeletonLoader.d.ts.map +1 -1
- package/dist/types/examples/AutoSkeletonV2Example.d.ts +3 -0
- package/dist/types/examples/AutoSkeletonV2Example.d.ts.map +1 -0
- package/package.json +71 -22
package/README.md
CHANGED
|
@@ -1,16 +1,129 @@
|
|
|
1
|
-
# ⚛️ React Loadly — React Loader, Spinner & Loading Indicator Components
|
|
1
|
+
# ⚛️ React Loadly — React Loader, Spinner & Loading Indicator Components Library
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./public/images/react-loadly-hero.png" alt="React Loadly - Modern React Loaders, Spinners, Skeleton Loaders, and Loading Indicators for React and Next.js" width="900" />
|
|
5
|
+
</p>
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
**React Loadly** is a modern, high-performance **React loader, spinner, and skeleton loader components library** with **30+ customizable loading animations**. Built with **TypeScript**, optimized for **Next.js and SSR**, and designed with **accessibility (ARIA)** and **developer experience** in mind.
|
|
8
|
+
|
|
9
|
+
Perfect for building **React applications, React dashboards, forms, and data-driven UIs** where you need smooth, customizable loading states and skeleton screens. Includes **AutoSkeletonLoader** for automatically generating skeleton loaders based on your component structure.
|
|
10
|
+
|
|
11
|
+
🔍 **Search-friendly features**: React loading spinner | React loader components | React skeleton loader | React loading indicator | Next.js loader | TypeScript loader | Accessible React loader | React loading animation
|
|
7
12
|
|
|
8
13
|
🏠 **Home Page**: [https://Mostafashadow1.github.io/react-loadly-showCases/](https://Mostafashadow1.github.io/react-loadly-showCases/)
|
|
9
14
|
|
|
10
|
-
[](https://www.npmjs.com/package/react-loadly)
|
|
11
|
-
[](https://www.npmjs.com/package/react-loadly)
|
|
16
|
+
[](https://github.com/Mostafashadow1/react-loadly)
|
|
17
|
+
[](https://bundlephobia.com/result?p=react-loadly)
|
|
12
18
|
[](https://github.com/Mostafashadow1/react-loadly/blob/main/LICENSE)
|
|
13
19
|
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Getting Started
|
|
23
|
+
|
|
24
|
+
### Installation
|
|
25
|
+
|
|
26
|
+
Install the package using your preferred package manager.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install react-loadly
|
|
30
|
+
# or
|
|
31
|
+
yarn add react-loadly
|
|
32
|
+
# or
|
|
33
|
+
pnpm i react-loadly
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🤖 AutoSkeletonLoader — The Zero-Manual-Work Skeleton Engine
|
|
39
|
+
|
|
40
|
+
Stop wasting time designing manual skeleton screens for every UI update. The **AutoSkeletonLoader** is an industry-leading React engine that dynamically analyzes your components at runtime and generates matching, high-fidelity skeleton placeholders automatically.
|
|
41
|
+
|
|
42
|
+
### 🧠 The Tech Behind the Magic
|
|
43
|
+
|
|
44
|
+
The AutoSkeletonLoader doesn't just show a generic grey box; it uses **Dynamic Runtime VDOM Traversal** to mirror your UI's exact hierarchy.
|
|
45
|
+
|
|
46
|
+
* **Advanced Component Decoding**: Fully supports modern React patterns including `React.memo`, `forwardRef`, `React.lazy`, and `React.Fragments`. It recursively "unwraps" Higher-Order Components to find the underlying UI structure.
|
|
47
|
+
* **Hooks-Resilient "Safe Render"**: Our proprietary `safeRender` mechanism allows the engine to traverse components that use `useState`, `useTranslation`, or `useContext` without crashing. It gracefully handles missing data dependencies that normally break skeleton rendering.
|
|
48
|
+
* **Intelligence (Heuristic Estimation)**: Built-in analysis predicts dimensions for Atoms (Text, Buttons, Badges, Images) and Molecules (Cards, Lists) based on naming conventions and HTML metadata.
|
|
49
|
+
* **Animation Continuity (Stable Key Algorithm)**: v2.4.x introduces a stable keying algorithm that ensures shimmer animations don't "reset" or flicker when your component's state changes.
|
|
50
|
+
* **Smart Memoization**: The VDOM analysis is extremely fast and memoized, occurring only when the component structure or configuration changes.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### 🚀 Full Example: The WorkerCard Molecule
|
|
55
|
+
|
|
56
|
+
In this example, we take a complex card with multiple hooks and nested components. See how `AutoSkeletonLoader` handles it with zero extra code.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { AutoSkeletonLoader } from "react-loadly";
|
|
60
|
+
import "react-loadly/styles.css";
|
|
61
|
+
|
|
62
|
+
// A complex component with hooks, memo, and sub-components
|
|
63
|
+
const WorkerCard = React.memo(({ data }) => {
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
return (
|
|
66
|
+
<div className="card-container">
|
|
67
|
+
<div className="flex gap-4">
|
|
68
|
+
<img src={data.avatar} className="rounded-full w-12 h-12" />
|
|
69
|
+
<div>
|
|
70
|
+
<h3 className="text-lg font-bold">{data.name}</h3>
|
|
71
|
+
<p className="text-gray-500">{data.role}</p>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="mt-4">
|
|
75
|
+
<BadgeAtom text={data.status} variant="success" />
|
|
76
|
+
</div>
|
|
77
|
+
<BaseButtonAtom text="View Profile" onClick={handleProfile} />
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Implementation
|
|
83
|
+
function Dashboard({ isLoading, worker }) {
|
|
84
|
+
return (
|
|
85
|
+
<AutoSkeletonLoader
|
|
86
|
+
loading={isLoading}
|
|
87
|
+
inheritStyles={true}
|
|
88
|
+
shimmer={true}
|
|
89
|
+
component={<WorkerCard data={worker} />}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### 📊 Comparison: Why AutoSkeletonLoader?
|
|
98
|
+
|
|
99
|
+
| Feature | Manual Skeletons (`SkeletonLoader`) | **AutoSkeletonLoader** (AI-Powered) |
|
|
100
|
+
| :--- | :--- | :--- |
|
|
101
|
+
| **Setup Time** | High (Design every line/block) | **Near-Zero** (Drop in and go) |
|
|
102
|
+
| **Maintenance** | Brittle (Breaks on UI changes) | **Self-Healing** (Adapts to UI changes) |
|
|
103
|
+
| **Accuracy** | Approximate | **Dynamic Match** (1:1 VDOM Mapping) |
|
|
104
|
+
| **Code Weight** | Verbose JSX boilerplate | **Single Component Wrapper** |
|
|
105
|
+
| **HOC Support** | Manual | **Automatic** (`memo`, `forwardRef`) |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### ⚙️ AutoSkeletonLoader Props (IAutoSkeletonProps)
|
|
110
|
+
|
|
111
|
+
| Prop | Type | Default | Description |
|
|
112
|
+
| :--- | :--- | :--- | :--- |
|
|
113
|
+
| `component` | `ReactElement` | **Required** | The actual component to analyze and mirror. |
|
|
114
|
+
| `loading` | `boolean` | `true` | When `false`, the real component is rendered with a smooth transition. |
|
|
115
|
+
| `inheritStyles` | `boolean` | `false` | If `true`, skeleton blocks will copy `style` and `className` from original elements. |
|
|
116
|
+
| `styless` | `Record<string, CSSProperties>` | `{}` | Key-value pairs to override dimensions for specific tags (e.g., `{ h1: { width: '50%'} }`). |
|
|
117
|
+
| `shimmer` | `boolean` | `true` | Enables the high-performance GPU-accelerated shimmer wave. |
|
|
118
|
+
| `speed` | `number` | `1` | Animation speed multiplier (higher is faster). |
|
|
119
|
+
| `color` | `string` | `"#e2e8f0"` | The base background color of the skeleton blocks. |
|
|
120
|
+
| `highlightColor`| `string` | `"#f1f5f9"` | The highlight color used in the shimmer gradient. |
|
|
121
|
+
| `waveWidthValue` | `string` | `"200px"` | Width of the shimmer wave gradient. |
|
|
122
|
+
| `waveDirection` | `String` | `"left-to-right"` | Shimmer direction: `left-to-right`, `right-to-left`, etc. |
|
|
123
|
+
| `aria-label` | `string` | `"Loading content..."` | Accessible label for screen readers. |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
14
127
|
## ✨ Key Features
|
|
15
128
|
|
|
16
129
|
- **High Performance**: Uses hardware-accelerated CSS transforms and animations.
|
|
@@ -25,7 +138,7 @@ Perfect for building **React apps, dashboards, forms, and data-driven UIs** wher
|
|
|
25
138
|
|
|
26
139
|
---
|
|
27
140
|
|
|
28
|
-
## 🆕 What's New
|
|
141
|
+
## 🆕 What's New (v2.4.0)
|
|
29
142
|
|
|
30
143
|
We've added **7 exciting new loaders** to expand your loading animation options:
|
|
31
144
|
|
|
@@ -47,20 +160,6 @@ All new loaders support:
|
|
|
47
160
|
|
|
48
161
|
---
|
|
49
162
|
|
|
50
|
-
## 🚀 Getting Started
|
|
51
|
-
|
|
52
|
-
### Installation
|
|
53
|
-
|
|
54
|
-
Install the package using your preferred package manager.
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npm install react-loadly
|
|
58
|
-
# or
|
|
59
|
-
yarn add react-loadly
|
|
60
|
-
# or
|
|
61
|
-
pnpm i react-loadly
|
|
62
|
-
```
|
|
63
|
-
|
|
64
163
|
### Bundle Optimization
|
|
65
164
|
|
|
66
165
|
React Loadly is optimized for minimal bundle size and supports tree shaking:
|
|
@@ -163,6 +262,7 @@ The `ProgressRingLoader` is an accessible progress ring component that supports
|
|
|
163
262
|
|
|
164
263
|
```jsx
|
|
165
264
|
import { ProgressRingLoader } from "react-loadly";
|
|
265
|
+
import "react-loadly/styles.css";
|
|
166
266
|
|
|
167
267
|
function App() {
|
|
168
268
|
return (
|
|
@@ -201,6 +301,7 @@ The `MorphLoader` creates smooth morphing SVG shapes that interpolate between di
|
|
|
201
301
|
|
|
202
302
|
```jsx
|
|
203
303
|
import { MorphLoader } from "react-loadly";
|
|
304
|
+
import "react-loadly/styles.css";
|
|
204
305
|
|
|
205
306
|
function App() {
|
|
206
307
|
return (
|
|
@@ -242,6 +343,7 @@ The `SpinDotsLoader` creates a circular orbit of dots, perfect for inline text l
|
|
|
242
343
|
|
|
243
344
|
```jsx
|
|
244
345
|
import { SpinDotsLoader } from "react-loadly";
|
|
346
|
+
import "react-loadly/styles.css";
|
|
245
347
|
|
|
246
348
|
function App() {
|
|
247
349
|
return (
|
|
@@ -281,6 +383,7 @@ The `HeatmapLoader` displays a grid of pulses with staggered timing, useful for
|
|
|
281
383
|
|
|
282
384
|
```jsx
|
|
283
385
|
import { HeatmapLoader } from "react-loadly";
|
|
386
|
+
import "react-loadly/styles.css";
|
|
284
387
|
|
|
285
388
|
function App() {
|
|
286
389
|
return (
|
|
@@ -320,6 +423,7 @@ The `ClockLoader` animates like a traditional clock with hour, minute, and secon
|
|
|
320
423
|
|
|
321
424
|
```jsx
|
|
322
425
|
import { ClockLoader } from "react-loadly";
|
|
426
|
+
import "react-loadly/styles.css";
|
|
323
427
|
|
|
324
428
|
function App() {
|
|
325
429
|
return (
|
|
@@ -357,6 +461,7 @@ The `NeumorphicLoader` creates a soft neumorphic pill/dots loader for modern UI
|
|
|
357
461
|
|
|
358
462
|
```jsx
|
|
359
463
|
import { NeumorphicLoader } from "react-loadly";
|
|
464
|
+
import "react-loadly/styles.css";
|
|
360
465
|
|
|
361
466
|
function App() {
|
|
362
467
|
return (
|
|
@@ -395,6 +500,7 @@ The `OrbitLoader` creates a beautiful orbital animation with elements rotating a
|
|
|
395
500
|
|
|
396
501
|
```jsx
|
|
397
502
|
import { OrbitLoader } from "react-loadly";
|
|
503
|
+
import "react-loadly/styles.css";
|
|
398
504
|
|
|
399
505
|
function App() {
|
|
400
506
|
return (
|
|
@@ -434,6 +540,7 @@ The `PlaneLoader` displays a 3D rotating cube with perspective transforms, creat
|
|
|
434
540
|
|
|
435
541
|
```jsx
|
|
436
542
|
import { PlaneLoader } from "react-loadly";
|
|
543
|
+
import "react-loadly/styles.css";
|
|
437
544
|
|
|
438
545
|
function App() {
|
|
439
546
|
return (
|
|
@@ -472,6 +579,7 @@ The `RippleLoader` creates expanding ripple rings from a central point, similar
|
|
|
472
579
|
|
|
473
580
|
```jsx
|
|
474
581
|
import { RippleLoader } from "react-loadly";
|
|
582
|
+
import "react-loadly/styles.css";
|
|
475
583
|
|
|
476
584
|
function App() {
|
|
477
585
|
return (
|
|
@@ -512,6 +620,7 @@ The `SquaresLoader` displays multiple rotating squares with varying delays and s
|
|
|
512
620
|
|
|
513
621
|
```jsx
|
|
514
622
|
import { SquaresLoader } from "react-loadly";
|
|
623
|
+
import "react-loadly/styles.css";
|
|
515
624
|
|
|
516
625
|
function App() {
|
|
517
626
|
return (
|
|
@@ -551,6 +660,7 @@ The `StairLoader` creates cascading stair steps that animate in sequence, creati
|
|
|
551
660
|
|
|
552
661
|
```jsx
|
|
553
662
|
import { StairLoader } from "react-loadly";
|
|
663
|
+
import "react-loadly/styles.css";
|
|
554
664
|
|
|
555
665
|
function App() {
|
|
556
666
|
return (
|
|
@@ -590,6 +700,7 @@ The `HashtagLoader` animates a hashtag symbol with a progressive drawing effect.
|
|
|
590
700
|
|
|
591
701
|
```jsx
|
|
592
702
|
import { HashtagLoader } from "react-loadly";
|
|
703
|
+
import "react-loadly/styles.css";
|
|
593
704
|
|
|
594
705
|
function App() {
|
|
595
706
|
return (
|
|
@@ -628,6 +739,7 @@ The `SnakeLoader` creates a snake-like animation with flowing segments that move
|
|
|
628
739
|
|
|
629
740
|
```jsx
|
|
630
741
|
import { SnakeLoader } from "react-loadly";
|
|
742
|
+
import "react-loadly/styles.css";
|
|
631
743
|
|
|
632
744
|
function App() {
|
|
633
745
|
return (
|
|
@@ -667,6 +779,7 @@ The `SkeletonLoader` is perfect for modern loading states, creating placeholder
|
|
|
667
779
|
|
|
668
780
|
```jsx
|
|
669
781
|
import { SkeletonLoader } from "react-loadly";
|
|
782
|
+
import "react-loadly/styles.css";
|
|
670
783
|
|
|
671
784
|
function App() {
|
|
672
785
|
return (
|
|
@@ -757,125 +870,6 @@ The SkeletonLoader supports multiple variants to match different content types:
|
|
|
757
870
|
|
|
758
871
|
---
|
|
759
872
|
|
|
760
|
-
## 🤖 AutoSkeletonLoader Component
|
|
761
|
-
|
|
762
|
-
The `AutoSkeletonLoader` is an advanced component that automatically generates skeleton loaders based on the structure of your actual components. It analyzes your component's JSX tree and creates matching skeleton placeholders.
|
|
763
|
-
|
|
764
|
-
### Key Features
|
|
765
|
-
|
|
766
|
-
- **Automatic Generation**: Scans the JSX tree of your component and replaces each element with a matching skeleton placeholder
|
|
767
|
-
- **Smart Dimension Estimation**: Automatically estimates skeleton dimensions based on element types and content
|
|
768
|
-
- **Style Inheritance**: Optionally inherits inline styles and props directly from the original element
|
|
769
|
-
- **Customizable Class Names**: Override styles per element type for fine-grained control
|
|
770
|
-
- **Variant Support**: Automatically selects appropriate skeleton variants (rect, circle, text) based on element type
|
|
771
|
-
- **Consistent Animations**: Shimmer effect support that's consistent with SkeletonLoader
|
|
772
|
-
- **Smooth Transitions**: Built-in fade-in/out animations between skeleton and real component
|
|
773
|
-
- **Accessibility**: Full ARIA support and screen reader compatibility
|
|
774
|
-
- **Performance Optimized**: Uses React.memo and useMemo for optimal re-rendering
|
|
775
|
-
|
|
776
|
-
### Basic Usage
|
|
777
|
-
|
|
778
|
-
```jsx
|
|
779
|
-
import { AutoSkeletonLoader } from "react-loadly";
|
|
780
|
-
|
|
781
|
-
function UserProfile({ user, loading }) {
|
|
782
|
-
return <AutoSkeletonLoader loading={loading} component={<UserProfileCard user={user} />} />;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function UserProfileCard({ user }) {
|
|
786
|
-
return (
|
|
787
|
-
<div>
|
|
788
|
-
<img src={user.avatar} alt={user.name} width="100" height="100" />
|
|
789
|
-
<h3>{user.name}</h3>
|
|
790
|
-
<p>{user.bio}</p>
|
|
791
|
-
<button>Follow</button>
|
|
792
|
-
</div>
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
```
|
|
796
|
-
|
|
797
|
-
### Advanced Usage Examples
|
|
798
|
-
|
|
799
|
-
#### Style Inheritance
|
|
800
|
-
|
|
801
|
-
```jsx
|
|
802
|
-
// Inherits styles from original elements for more accurate skeletons
|
|
803
|
-
<AutoSkeletonLoader loading={loading} component={<UserProfileCard user={user} />} inheritStyles={true} />
|
|
804
|
-
```
|
|
805
|
-
|
|
806
|
-
#### Custom Class Names
|
|
807
|
-
|
|
808
|
-
```jsx
|
|
809
|
-
// Customize skeleton appearance per element type
|
|
810
|
-
<AutoSkeleton
|
|
811
|
-
loading={loading}
|
|
812
|
-
component={<Card data={data} />}
|
|
813
|
-
styless={{
|
|
814
|
-
p: { height: "0.8em", width: "80%" },
|
|
815
|
-
h3: { height: "1.2em", width: "60%", borderRadius: "8px" },
|
|
816
|
-
img: { borderRadius: "12px" },
|
|
817
|
-
button: { width: "150px", height: "50px" },
|
|
818
|
-
}}
|
|
819
|
-
/>
|
|
820
|
-
```
|
|
821
|
-
|
|
822
|
-
#### With Shimmer Effects
|
|
823
|
-
|
|
824
|
-
```jsx
|
|
825
|
-
// Enable shimmer animations for a more polished look
|
|
826
|
-
<AutoSkeleton
|
|
827
|
-
loading={loading}
|
|
828
|
-
component={<ProductCard product={product} />}
|
|
829
|
-
shimmer={true}
|
|
830
|
-
shimmerColor="rgba(255, 255, 255, 0.8)"
|
|
831
|
-
highlightColor="#f8fafc"
|
|
832
|
-
/>
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
#### Complex Component Example
|
|
836
|
-
|
|
837
|
-
```jsx
|
|
838
|
-
function DashboardCard({ title, metrics, loading }) {
|
|
839
|
-
return (
|
|
840
|
-
<AutoSkeletonLoader
|
|
841
|
-
loading={loading}
|
|
842
|
-
component={
|
|
843
|
-
<div className="dashboard-card">
|
|
844
|
-
<h2>{title}</h2>
|
|
845
|
-
<div className="metrics">
|
|
846
|
-
{metrics.map((metric, index) => (
|
|
847
|
-
<div key={index} className="metric">
|
|
848
|
-
<span className="value">{metric.value}</span>
|
|
849
|
-
<span className="label">{metric.label}</span>
|
|
850
|
-
</div>
|
|
851
|
-
))}
|
|
852
|
-
</div>
|
|
853
|
-
</div>
|
|
854
|
-
}
|
|
855
|
-
inheritStyles={true}
|
|
856
|
-
shimmer={true}
|
|
857
|
-
/>
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
```
|
|
861
|
-
|
|
862
|
-
### AutoSkeleton Props
|
|
863
|
-
|
|
864
|
-
| Prop | Type | Default | Description |
|
|
865
|
-
| ---------------- | ------------------------------------------------------------------------ | ----------------------- | ------------------------------------------------ |
|
|
866
|
-
| `component` | ReactElement | - | The component to render or analyze for skeletons |
|
|
867
|
-
| `inheritStyles` | boolean | false | Whether to inherit styles from original elements |
|
|
868
|
-
| `styless` | object | {} | Custom styles for different element types |
|
|
869
|
-
| `shimmer` | boolean | true | Whether to show shimmer animation |
|
|
870
|
-
| `shimmerColor` | string | "rgba(255,255,255,0.6)" | Shimmer effect color |
|
|
871
|
-
| `highlightColor` | string | "#f1f5f9" | Highlight color for shimmer effect |
|
|
872
|
-
| `waveWidth` | number \| string | "200px" | Shimmer wave width |
|
|
873
|
-
| `waveDirection` | "left-to-right" \| "right-to-left" \| "top-to-bottom" \| "bottom-to-top" | "left-to-right" | Direction of shimmer animation |
|
|
874
|
-
|
|
875
|
-
All other props are inherited from `IBaseLoaderProps`.
|
|
876
|
-
|
|
877
|
-
---
|
|
878
|
-
|
|
879
873
|
## 🎨 New Loaders Feature Guide
|
|
880
874
|
|
|
881
875
|
### Using Multiple New Loaders Together
|
|
@@ -884,6 +878,7 @@ Here's how to combine the new loaders for amazing visual effects:
|
|
|
884
878
|
|
|
885
879
|
```jsx
|
|
886
880
|
import { OrbitLoader, PlaneLoader, RippleLoader, SquaresLoader, StairLoader, HashtagLoader, SnakeLoader } from "react-loadly";
|
|
881
|
+
import "react-loadly/styles.css";
|
|
887
882
|
|
|
888
883
|
function MultiLoaderShowcase() {
|
|
889
884
|
return (
|