@aspect-ops/exon-ui 0.0.2 → 0.1.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 +793 -43
- package/dist/components/Accordion/Accordion.svelte +79 -0
- package/dist/components/Accordion/Accordion.svelte.d.ts +10 -0
- package/dist/components/Accordion/AccordionItem.svelte +198 -0
- package/dist/components/Accordion/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/Accordion/index.d.ts +2 -0
- package/dist/components/Accordion/index.js +2 -0
- package/dist/components/ActionSheet/ActionSheet.svelte +270 -0
- package/dist/components/ActionSheet/ActionSheet.svelte.d.ts +12 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte +151 -0
- package/dist/components/ActionSheet/ActionSheetItem.svelte.d.ts +10 -0
- package/dist/components/ActionSheet/index.d.ts +3 -0
- package/dist/components/ActionSheet/index.js +2 -0
- package/dist/components/Alert/Alert.svelte +165 -0
- package/dist/components/Alert/Alert.svelte.d.ts +11 -0
- package/dist/components/Alert/index.d.ts +1 -0
- package/dist/components/Alert/index.js +1 -0
- package/dist/components/AspectRatio/AspectRatio.svelte +42 -0
- package/dist/components/AspectRatio/AspectRatio.svelte.d.ts +9 -0
- package/dist/components/AspectRatio/index.d.ts +1 -0
- package/dist/components/AspectRatio/index.js +1 -0
- package/dist/components/Avatar/Avatar.svelte +147 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +12 -0
- package/dist/components/Avatar/AvatarGroup.svelte +153 -0
- package/dist/components/Avatar/AvatarGroup.svelte.d.ts +12 -0
- package/dist/components/Avatar/index.d.ts +2 -0
- package/dist/components/Avatar/index.js +2 -0
- package/dist/components/BottomSheet/BottomSheet.svelte +230 -0
- package/dist/components/BottomSheet/BottomSheet.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte +20 -0
- package/dist/components/BottomSheet/BottomSheetBody.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte +27 -0
- package/dist/components/BottomSheet/BottomSheetHeader.svelte.d.ts +7 -0
- package/dist/components/BottomSheet/index.d.ts +3 -0
- package/dist/components/BottomSheet/index.js +3 -0
- package/dist/components/Box/Box.svelte +41 -0
- package/dist/components/Box/Box.svelte.d.ts +7 -0
- package/dist/components/Box/index.d.ts +1 -0
- package/dist/components/Box/index.js +1 -0
- package/dist/components/Card/Card.svelte +95 -0
- package/dist/components/Card/Card.svelte.d.ts +10 -0
- package/dist/components/Card/CardBody.svelte +32 -0
- package/dist/components/Card/CardBody.svelte.d.ts +7 -0
- package/dist/components/Card/CardFooter.svelte +34 -0
- package/dist/components/Card/CardFooter.svelte.d.ts +7 -0
- package/dist/components/Card/CardHeader.svelte +67 -0
- package/dist/components/Card/CardHeader.svelte.d.ts +9 -0
- package/dist/components/Card/index.d.ts +4 -0
- package/dist/components/Card/index.js +4 -0
- package/dist/components/Carousel/Carousel.svelte +454 -0
- package/dist/components/Carousel/Carousel.svelte.d.ts +14 -0
- package/dist/components/Carousel/CarouselSlide.svelte +22 -0
- package/dist/components/Carousel/CarouselSlide.svelte.d.ts +7 -0
- package/dist/components/Carousel/index.d.ts +2 -0
- package/dist/components/Carousel/index.js +2 -0
- package/dist/components/Center/Center.svelte +28 -0
- package/dist/components/Center/Center.svelte.d.ts +8 -0
- package/dist/components/Center/index.d.ts +1 -0
- package/dist/components/Center/index.js +1 -0
- package/dist/components/Chip/Chip.svelte +461 -0
- package/dist/components/Chip/Chip.svelte.d.ts +17 -0
- package/dist/components/Chip/ChipGroup.svelte +76 -0
- package/dist/components/Chip/ChipGroup.svelte.d.ts +9 -0
- package/dist/components/Chip/index.d.ts +2 -0
- package/dist/components/Chip/index.js +2 -0
- package/dist/components/Container/Container.svelte +58 -0
- package/dist/components/Container/Container.svelte.d.ts +10 -0
- package/dist/components/Container/index.d.ts +1 -0
- package/dist/components/Container/index.js +1 -0
- package/dist/components/DatePicker/DatePicker.svelte +746 -0
- package/dist/components/DatePicker/DatePicker.svelte.d.ts +19 -0
- package/dist/components/DatePicker/index.d.ts +1 -0
- package/dist/components/DatePicker/index.js +1 -0
- package/dist/components/Divider/Divider.svelte +38 -0
- package/dist/components/Divider/Divider.svelte.d.ts +9 -0
- package/dist/components/Divider/index.d.ts +1 -0
- package/dist/components/Divider/index.js +1 -0
- package/dist/components/EmptyState/EmptyState.svelte +164 -0
- package/dist/components/EmptyState/EmptyState.svelte.d.ts +12 -0
- package/dist/components/EmptyState/index.d.ts +1 -0
- package/dist/components/EmptyState/index.js +1 -0
- package/dist/components/FAB/FAB.svelte +242 -0
- package/dist/components/FAB/FAB.svelte.d.ts +9 -0
- package/dist/components/FAB/FABGroup.svelte +449 -0
- package/dist/components/FAB/FABGroup.svelte.d.ts +9 -0
- package/dist/components/FAB/index.d.ts +3 -0
- package/dist/components/FAB/index.js +2 -0
- package/dist/components/FileUpload/FileUpload.svelte +484 -0
- package/dist/components/FileUpload/FileUpload.svelte.d.ts +16 -0
- package/dist/components/FileUpload/index.d.ts +1 -0
- package/dist/components/FileUpload/index.js +1 -0
- package/dist/components/Grid/Grid.svelte +136 -0
- package/dist/components/Grid/Grid.svelte.d.ts +12 -0
- package/dist/components/Grid/GridItem.svelte +21 -0
- package/dist/components/Grid/GridItem.svelte.d.ts +7 -0
- package/dist/components/Grid/index.d.ts +2 -0
- package/dist/components/Grid/index.js +2 -0
- package/dist/components/Image/Image.svelte +223 -0
- package/dist/components/Image/Image.svelte.d.ts +19 -0
- package/dist/components/Image/index.d.ts +1 -0
- package/dist/components/Image/index.js +1 -0
- package/dist/components/List/List.svelte +42 -0
- package/dist/components/List/List.svelte.d.ts +18 -0
- package/dist/components/List/ListItem.svelte +139 -0
- package/dist/components/List/ListItem.svelte.d.ts +36 -0
- package/dist/components/List/index.d.ts +2 -0
- package/dist/components/List/index.js +2 -0
- package/dist/components/Modal/Modal.svelte +204 -0
- package/dist/components/Modal/Modal.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalBody.svelte +50 -0
- package/dist/components/Modal/ModalBody.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalFooter.svelte +37 -0
- package/dist/components/Modal/ModalFooter.svelte.d.ts +7 -0
- package/dist/components/Modal/ModalHeader.svelte +73 -0
- package/dist/components/Modal/ModalHeader.svelte.d.ts +7 -0
- package/dist/components/Modal/index.d.ts +4 -0
- package/dist/components/Modal/index.js +4 -0
- package/dist/components/OTPInput/OTPInput.svelte +312 -0
- package/dist/components/OTPInput/OTPInput.svelte.d.ts +57 -0
- package/dist/components/OTPInput/index.d.ts +1 -0
- package/dist/components/OTPInput/index.js +1 -0
- package/dist/components/Popover/Popover.svelte +14 -0
- package/dist/components/Popover/Popover.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverContent.svelte +63 -0
- package/dist/components/Popover/PopoverContent.svelte.d.ts +7 -0
- package/dist/components/Popover/PopoverTrigger.svelte +14 -0
- package/dist/components/Popover/PopoverTrigger.svelte.d.ts +7 -0
- package/dist/components/Popover/index.d.ts +3 -0
- package/dist/components/Popover/index.js +3 -0
- package/dist/components/Progress/ProgressBar.svelte +86 -0
- package/dist/components/Progress/ProgressBar.svelte.d.ts +11 -0
- package/dist/components/Progress/ProgressCircle.svelte +134 -0
- package/dist/components/Progress/ProgressCircle.svelte.d.ts +12 -0
- package/dist/components/Progress/Spinner.svelte +68 -0
- package/dist/components/Progress/Spinner.svelte.d.ts +8 -0
- package/dist/components/Progress/index.d.ts +3 -0
- package/dist/components/Progress/index.js +3 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte +304 -0
- package/dist/components/PullToRefresh/PullToRefresh.svelte.d.ts +20 -0
- package/dist/components/PullToRefresh/index.d.ts +1 -0
- package/dist/components/PullToRefresh/index.js +1 -0
- package/dist/components/Rating/Rating.svelte +316 -0
- package/dist/components/Rating/Rating.svelte.d.ts +16 -0
- package/dist/components/Rating/index.d.ts +1 -0
- package/dist/components/Rating/index.js +1 -0
- package/dist/components/SafeArea/SafeArea.svelte +33 -0
- package/dist/components/SafeArea/SafeArea.svelte.d.ts +7 -0
- package/dist/components/SearchInput/SearchInput.svelte +480 -0
- package/dist/components/SearchInput/SearchInput.svelte.d.ts +22 -0
- package/dist/components/SearchInput/index.d.ts +1 -0
- package/dist/components/SearchInput/index.js +1 -0
- package/dist/components/Select/Select.svelte +55 -12
- package/dist/components/Skeleton/Skeleton.svelte +59 -0
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +10 -0
- package/dist/components/Skeleton/index.d.ts +1 -0
- package/dist/components/Skeleton/index.js +1 -0
- package/dist/components/Slider/Slider.svelte +324 -0
- package/dist/components/Slider/Slider.svelte.d.ts +14 -0
- package/dist/components/Slider/index.d.ts +1 -0
- package/dist/components/Slider/index.js +1 -0
- package/dist/components/Spacer/Spacer.svelte +56 -0
- package/dist/components/Spacer/Spacer.svelte.d.ts +6 -0
- package/dist/components/Spacer/index.d.ts +1 -0
- package/dist/components/Spacer/index.js +1 -0
- package/dist/components/Stack/Stack.svelte +117 -0
- package/dist/components/Stack/Stack.svelte.d.ts +13 -0
- package/dist/components/Stack/index.d.ts +1 -0
- package/dist/components/Stack/index.js +1 -0
- package/dist/components/Stepper/Stepper.svelte +100 -0
- package/dist/components/Stepper/Stepper.svelte.d.ts +11 -0
- package/dist/components/Stepper/StepperStep.svelte +391 -0
- package/dist/components/Stepper/StepperStep.svelte.d.ts +13 -0
- package/dist/components/Stepper/index.d.ts +2 -0
- package/dist/components/Stepper/index.js +2 -0
- package/dist/components/SwipeActions/SwipeAction.svelte +43 -0
- package/dist/components/SwipeActions/SwipeAction.svelte.d.ts +8 -0
- package/dist/components/SwipeActions/SwipeActions.svelte +193 -0
- package/dist/components/SwipeActions/SwipeActions.svelte.d.ts +9 -0
- package/dist/components/SwipeActions/index.d.ts +2 -0
- package/dist/components/SwipeActions/index.js +2 -0
- package/dist/components/Switch/Switch.svelte +29 -9
- package/dist/components/Table/Table.svelte +175 -0
- package/dist/components/Table/Table.svelte.d.ts +38 -0
- package/dist/components/Table/TableBody.svelte +26 -0
- package/dist/components/Table/TableBody.svelte.d.ts +13 -0
- package/dist/components/Table/TableCell.svelte +85 -0
- package/dist/components/Table/TableCell.svelte.d.ts +28 -0
- package/dist/components/Table/TableHead.svelte +36 -0
- package/dist/components/Table/TableHead.svelte.d.ts +13 -0
- package/dist/components/Table/TableHeader.svelte +217 -0
- package/dist/components/Table/TableHeader.svelte.d.ts +32 -0
- package/dist/components/Table/TableRow.svelte +92 -0
- package/dist/components/Table/TableRow.svelte.d.ts +28 -0
- package/dist/components/Table/index.d.ts +6 -0
- package/dist/components/Table/index.js +6 -0
- package/dist/components/Tag/Tag.svelte +189 -0
- package/dist/components/Tag/Tag.svelte.d.ts +13 -0
- package/dist/components/Tag/index.d.ts +1 -0
- package/dist/components/Tag/index.js +1 -0
- package/dist/components/TimePicker/TimePicker.svelte +803 -0
- package/dist/components/TimePicker/TimePicker.svelte.d.ts +17 -0
- package/dist/components/TimePicker/index.d.ts +1 -0
- package/dist/components/TimePicker/index.js +1 -0
- package/dist/components/Toast/Toast.svelte +241 -0
- package/dist/components/Toast/Toast.svelte.d.ts +18 -0
- package/dist/components/Toast/ToastContainer.svelte +110 -0
- package/dist/components/Toast/ToastContainer.svelte.d.ts +8 -0
- package/dist/components/Toast/index.d.ts +3 -0
- package/dist/components/Toast/index.js +3 -0
- package/dist/components/Toast/toast.d.ts +13 -0
- package/dist/components/Toast/toast.js +55 -0
- package/dist/components/Tooltip/Tooltip.svelte +71 -0
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +7 -0
- package/dist/components/Tooltip/index.d.ts +2 -0
- package/dist/components/Tooltip/index.js +1 -0
- package/dist/index.d.ts +38 -1
- package/dist/index.js +41 -0
- package/dist/styles/tokens.css +5 -0
- package/dist/types/data-display.d.ts +161 -0
- package/dist/types/data-display.js +1 -0
- package/dist/types/feedback.d.ts +92 -0
- package/dist/types/feedback.js +1 -0
- package/dist/types/index.d.ts +6 -1
- package/dist/types/input.d.ts +67 -0
- package/dist/types/input.js +2 -0
- package/dist/types/layout.d.ts +57 -0
- package/dist/types/layout.js +1 -0
- package/dist/types/mobile.d.ts +91 -0
- package/dist/types/mobile.js +1 -0
- package/dist/types/navigation.d.ts +15 -0
- package/dist/utils/gestures.d.ts +219 -0
- package/dist/utils/gestures.js +492 -0
- package/dist/utils/haptics.d.ts +89 -0
- package/dist/utils/haptics.js +198 -0
- package/dist/utils/platform.d.ts +47 -0
- package/dist/utils/platform.js +156 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ProgressBarProps } from '../../types/index.js';
|
|
3
|
+
|
|
4
|
+
interface Props extends ProgressBarProps {
|
|
5
|
+
value?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
size?: 'sm' | 'md' | 'lg';
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
value = 0,
|
|
14
|
+
max = 100,
|
|
15
|
+
indeterminate = false,
|
|
16
|
+
size = 'md',
|
|
17
|
+
class: className = ''
|
|
18
|
+
}: Props = $props();
|
|
19
|
+
|
|
20
|
+
// Calculate percentage for determinate mode
|
|
21
|
+
const percentage = $derived(indeterminate ? 0 : Math.min(100, Math.max(0, (value / max) * 100)));
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div
|
|
25
|
+
class="progress-bar progress-bar--{size} {className}"
|
|
26
|
+
role="progressbar"
|
|
27
|
+
aria-valuemin="0"
|
|
28
|
+
aria-valuemax={max}
|
|
29
|
+
aria-valuenow={indeterminate ? undefined : value}
|
|
30
|
+
aria-label={indeterminate ? 'Loading' : `${Math.round(percentage)}% complete`}
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
class="progress-bar__fill"
|
|
34
|
+
class:progress-bar__fill--indeterminate={indeterminate}
|
|
35
|
+
style="width: {indeterminate ? '30%' : `${percentage}%`}"
|
|
36
|
+
></div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.progress-bar {
|
|
41
|
+
position: relative;
|
|
42
|
+
width: 100%;
|
|
43
|
+
overflow: hidden;
|
|
44
|
+
background-color: var(--exon-progress-track, var(--color-neutral-200, #e5e7eb));
|
|
45
|
+
border-radius: var(--exon-radius-full, 9999px);
|
|
46
|
+
font-family: inherit;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Size variants */
|
|
50
|
+
.progress-bar--sm {
|
|
51
|
+
height: 0.25rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.progress-bar--md {
|
|
55
|
+
height: 0.5rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.progress-bar--lg {
|
|
59
|
+
height: 0.75rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Progress fill */
|
|
63
|
+
.progress-bar__fill {
|
|
64
|
+
height: 100%;
|
|
65
|
+
background-color: var(--exon-progress-fill, var(--color-primary, #3b82f6));
|
|
66
|
+
border-radius: var(--exon-radius-full, 9999px);
|
|
67
|
+
transition: width 0.3s ease;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Indeterminate animation */
|
|
71
|
+
.progress-bar__fill--indeterminate {
|
|
72
|
+
animation: progress-indeterminate 1.5s ease-in-out infinite;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@keyframes progress-indeterminate {
|
|
76
|
+
0% {
|
|
77
|
+
transform: translateX(-100%);
|
|
78
|
+
}
|
|
79
|
+
50% {
|
|
80
|
+
transform: translateX(350%);
|
|
81
|
+
}
|
|
82
|
+
100% {
|
|
83
|
+
transform: translateX(-100%);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProgressBarProps } from '../../types/index.js';
|
|
2
|
+
interface Props extends ProgressBarProps {
|
|
3
|
+
value?: number;
|
|
4
|
+
max?: number;
|
|
5
|
+
indeterminate?: boolean;
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const ProgressBar: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type ProgressBar = ReturnType<typeof ProgressBar>;
|
|
11
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ProgressCircleProps } from '../../types/index.js';
|
|
3
|
+
|
|
4
|
+
interface Props extends ProgressCircleProps {
|
|
5
|
+
value?: number;
|
|
6
|
+
max?: number;
|
|
7
|
+
indeterminate?: boolean;
|
|
8
|
+
size?: 'sm' | 'md' | 'lg';
|
|
9
|
+
strokeWidth?: number;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
value = 0,
|
|
15
|
+
max = 100,
|
|
16
|
+
indeterminate = false,
|
|
17
|
+
size = 'md',
|
|
18
|
+
strokeWidth = 4,
|
|
19
|
+
class: className = ''
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
|
|
22
|
+
// SVG constants
|
|
23
|
+
const radius = 45;
|
|
24
|
+
const circumference = 2 * Math.PI * radius;
|
|
25
|
+
|
|
26
|
+
// Calculate percentage and stroke-dashoffset for determinate mode
|
|
27
|
+
const percentage = $derived(indeterminate ? 0 : Math.min(100, Math.max(0, (value / max) * 100)));
|
|
28
|
+
const strokeDashoffset = $derived(circumference * (1 - percentage / 100));
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div
|
|
32
|
+
class="progress-circle progress-circle--{size} {className}"
|
|
33
|
+
role="progressbar"
|
|
34
|
+
aria-valuemin="0"
|
|
35
|
+
aria-valuemax={max}
|
|
36
|
+
aria-valuenow={indeterminate ? undefined : value}
|
|
37
|
+
aria-label={indeterminate ? 'Loading' : `${Math.round(percentage)}% complete`}
|
|
38
|
+
>
|
|
39
|
+
<svg class="progress-circle__svg" viewBox="0 0 100 100">
|
|
40
|
+
<!-- Track circle (background) -->
|
|
41
|
+
<circle
|
|
42
|
+
class="progress-circle__track"
|
|
43
|
+
cx="50"
|
|
44
|
+
cy="50"
|
|
45
|
+
r={radius}
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke-width={strokeWidth}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<!-- Progress circle (foreground) -->
|
|
51
|
+
<circle
|
|
52
|
+
class="progress-circle__fill"
|
|
53
|
+
class:progress-circle__fill--indeterminate={indeterminate}
|
|
54
|
+
cx="50"
|
|
55
|
+
cy="50"
|
|
56
|
+
r={radius}
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke-width={strokeWidth}
|
|
59
|
+
stroke-dasharray={circumference}
|
|
60
|
+
stroke-dashoffset={indeterminate ? circumference * 0.75 : strokeDashoffset}
|
|
61
|
+
stroke-linecap="round"
|
|
62
|
+
/>
|
|
63
|
+
</svg>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
.progress-circle {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
font-family: inherit;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Size variants */
|
|
75
|
+
.progress-circle--sm {
|
|
76
|
+
width: 1.5rem;
|
|
77
|
+
height: 1.5rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.progress-circle--md {
|
|
81
|
+
width: 2.5rem;
|
|
82
|
+
height: 2.5rem;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.progress-circle--lg {
|
|
86
|
+
width: 3.5rem;
|
|
87
|
+
height: 3.5rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.progress-circle__svg {
|
|
91
|
+
width: 100%;
|
|
92
|
+
height: 100%;
|
|
93
|
+
transform: rotate(-90deg);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Track circle (background) */
|
|
97
|
+
.progress-circle__track {
|
|
98
|
+
stroke: var(--exon-progress-track, var(--color-neutral-200, #e5e7eb));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Progress circle (foreground fill) */
|
|
102
|
+
.progress-circle__fill {
|
|
103
|
+
stroke: var(--exon-progress-fill, var(--color-primary, #3b82f6));
|
|
104
|
+
transition: stroke-dashoffset 0.3s ease;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Indeterminate animation */
|
|
108
|
+
.progress-circle__fill--indeterminate {
|
|
109
|
+
animation:
|
|
110
|
+
progress-circle-rotate 1.5s linear infinite,
|
|
111
|
+
progress-circle-dash 1.5s ease-in-out infinite;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@keyframes progress-circle-rotate {
|
|
115
|
+
0% {
|
|
116
|
+
transform: rotate(0deg);
|
|
117
|
+
}
|
|
118
|
+
100% {
|
|
119
|
+
transform: rotate(360deg);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@keyframes progress-circle-dash {
|
|
124
|
+
0% {
|
|
125
|
+
stroke-dashoffset: 282.74;
|
|
126
|
+
}
|
|
127
|
+
50% {
|
|
128
|
+
stroke-dashoffset: 70.685;
|
|
129
|
+
}
|
|
130
|
+
100% {
|
|
131
|
+
stroke-dashoffset: 282.74;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ProgressCircleProps } from '../../types/index.js';
|
|
2
|
+
interface Props extends ProgressCircleProps {
|
|
3
|
+
value?: number;
|
|
4
|
+
max?: number;
|
|
5
|
+
indeterminate?: boolean;
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
strokeWidth?: number;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const ProgressCircle: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type ProgressCircle = ReturnType<typeof ProgressCircle>;
|
|
12
|
+
export default ProgressCircle;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SpinnerProps } from '../../types/index.js';
|
|
3
|
+
|
|
4
|
+
interface Props extends SpinnerProps {
|
|
5
|
+
size?: 'sm' | 'md' | 'lg';
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { size = 'md', class: className = '' }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="spinner spinner--{size} {className}" role="status" aria-label="Loading">
|
|
13
|
+
<span class="spinner__visually-hidden">Loading...</span>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
.spinner {
|
|
18
|
+
display: inline-block;
|
|
19
|
+
border-style: solid;
|
|
20
|
+
border-color: var(--exon-spinner-color, var(--color-primary, #3b82f6)) transparent transparent
|
|
21
|
+
transparent;
|
|
22
|
+
border-radius: 50%;
|
|
23
|
+
animation: spinner-rotate 0.75s linear infinite;
|
|
24
|
+
font-family: inherit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Size variants - diameter and border-width */
|
|
28
|
+
.spinner--sm {
|
|
29
|
+
width: 1rem;
|
|
30
|
+
height: 1rem;
|
|
31
|
+
border-width: 2px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.spinner--md {
|
|
35
|
+
width: 1.5rem;
|
|
36
|
+
height: 1.5rem;
|
|
37
|
+
border-width: 2px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.spinner--lg {
|
|
41
|
+
width: 2rem;
|
|
42
|
+
height: 2rem;
|
|
43
|
+
border-width: 3px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Visually hidden text for screen readers */
|
|
47
|
+
.spinner__visually-hidden {
|
|
48
|
+
position: absolute;
|
|
49
|
+
width: 1px;
|
|
50
|
+
height: 1px;
|
|
51
|
+
padding: 0;
|
|
52
|
+
margin: -1px;
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
clip: rect(0, 0, 0, 0);
|
|
55
|
+
white-space: nowrap;
|
|
56
|
+
border-width: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Rotation animation */
|
|
60
|
+
@keyframes spinner-rotate {
|
|
61
|
+
0% {
|
|
62
|
+
transform: rotate(0deg);
|
|
63
|
+
}
|
|
64
|
+
100% {
|
|
65
|
+
transform: rotate(360deg);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SpinnerProps } from '../../types/index.js';
|
|
2
|
+
interface Props extends SpinnerProps {
|
|
3
|
+
size?: 'sm' | 'md' | 'lg';
|
|
4
|
+
class?: string;
|
|
5
|
+
}
|
|
6
|
+
declare const Spinner: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type Spinner = ReturnType<typeof Spinner>;
|
|
8
|
+
export default Spinner;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { PullToRefreshProps } from '../../types/index.js';
|
|
3
|
+
import { hapticImpact } from '../../utils/haptics.js';
|
|
4
|
+
|
|
5
|
+
interface Props extends PullToRefreshProps {
|
|
6
|
+
/** Controlled loading state (bindable) */
|
|
7
|
+
refreshing?: boolean;
|
|
8
|
+
/** Pull distance in pixels to trigger refresh (default: 80) */
|
|
9
|
+
threshold?: number;
|
|
10
|
+
/** Maximum pull distance in pixels (default: 150) */
|
|
11
|
+
maxPull?: number;
|
|
12
|
+
/** Disable pull-to-refresh functionality */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Additional CSS class */
|
|
15
|
+
class?: string;
|
|
16
|
+
/** Called when threshold reached and released */
|
|
17
|
+
onrefresh?: () => void;
|
|
18
|
+
/** Content to render inside */
|
|
19
|
+
children?: import('svelte').Snippet;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
refreshing = $bindable(false),
|
|
24
|
+
threshold = 80,
|
|
25
|
+
maxPull = 150,
|
|
26
|
+
disabled = false,
|
|
27
|
+
class: className = '',
|
|
28
|
+
onrefresh,
|
|
29
|
+
children
|
|
30
|
+
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
// Touch tracking state
|
|
33
|
+
let pullDistance = $state(0);
|
|
34
|
+
let isPulling = $state(false);
|
|
35
|
+
let startY = $state(0);
|
|
36
|
+
let currentY = $state(0);
|
|
37
|
+
let containerEl: HTMLDivElement | undefined = $state();
|
|
38
|
+
let contentEl: HTMLDivElement | undefined = $state();
|
|
39
|
+
|
|
40
|
+
// Calculated values
|
|
41
|
+
const progress = $derived(Math.min(pullDistance / threshold, 1));
|
|
42
|
+
const indicatorRotation = $derived(progress * 360);
|
|
43
|
+
const indicatorY = $derived(Math.min(pullDistance, maxPull) - 40);
|
|
44
|
+
const indicatorOpacity = $derived(Math.min(progress, 1));
|
|
45
|
+
const isThresholdReached = $derived(pullDistance >= threshold);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if content is scrolled to the top
|
|
49
|
+
*/
|
|
50
|
+
function isAtScrollTop(): boolean {
|
|
51
|
+
if (!contentEl) return true;
|
|
52
|
+
return contentEl.scrollTop <= 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Apply resistance to pull distance for natural feel
|
|
57
|
+
*/
|
|
58
|
+
function applyResistance(distance: number): number {
|
|
59
|
+
if (distance <= 0) return 0;
|
|
60
|
+
// Diminishing returns after threshold
|
|
61
|
+
const resistance = distance > threshold ? 0.4 : 0.6;
|
|
62
|
+
return Math.min(distance * resistance, maxPull);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handle touch start
|
|
67
|
+
*/
|
|
68
|
+
function handleTouchStart(e: TouchEvent) {
|
|
69
|
+
if (disabled || refreshing) return;
|
|
70
|
+
if (!isAtScrollTop()) return;
|
|
71
|
+
if (e.touches.length !== 1) return;
|
|
72
|
+
|
|
73
|
+
startY = e.touches[0].clientY;
|
|
74
|
+
currentY = startY;
|
|
75
|
+
isPulling = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Handle touch move
|
|
80
|
+
*/
|
|
81
|
+
function handleTouchMove(e: TouchEvent) {
|
|
82
|
+
if (disabled || refreshing) return;
|
|
83
|
+
if (e.touches.length !== 1) return;
|
|
84
|
+
|
|
85
|
+
const touchY = e.touches[0].clientY;
|
|
86
|
+
const rawDistance = touchY - startY;
|
|
87
|
+
|
|
88
|
+
// Only start pulling if we're at scroll top and pulling down
|
|
89
|
+
if (!isPulling) {
|
|
90
|
+
if (rawDistance > 0 && isAtScrollTop()) {
|
|
91
|
+
isPulling = true;
|
|
92
|
+
startY = touchY; // Reset start position when pull begins
|
|
93
|
+
} else {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Calculate pull distance with resistance
|
|
99
|
+
currentY = touchY;
|
|
100
|
+
const actualDistance = currentY - startY;
|
|
101
|
+
|
|
102
|
+
if (actualDistance > 0) {
|
|
103
|
+
pullDistance = applyResistance(actualDistance);
|
|
104
|
+
// Prevent scroll while pulling
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
} else {
|
|
107
|
+
// Reset if user pushes back up
|
|
108
|
+
pullDistance = 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handle touch end
|
|
114
|
+
*/
|
|
115
|
+
async function handleTouchEnd() {
|
|
116
|
+
if (disabled) return;
|
|
117
|
+
|
|
118
|
+
if (isPulling && isThresholdReached && !refreshing) {
|
|
119
|
+
// Trigger haptic feedback
|
|
120
|
+
await hapticImpact('medium');
|
|
121
|
+
|
|
122
|
+
// Trigger refresh
|
|
123
|
+
refreshing = true;
|
|
124
|
+
onrefresh?.();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Animate back
|
|
128
|
+
isPulling = false;
|
|
129
|
+
|
|
130
|
+
if (!refreshing) {
|
|
131
|
+
pullDistance = 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Handle touch cancel
|
|
137
|
+
*/
|
|
138
|
+
function handleTouchCancel() {
|
|
139
|
+
isPulling = false;
|
|
140
|
+
if (!refreshing) {
|
|
141
|
+
pullDistance = 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Watch for refreshing state changes to reset pull distance
|
|
146
|
+
$effect(() => {
|
|
147
|
+
if (!refreshing && pullDistance > 0) {
|
|
148
|
+
pullDistance = 0;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<div
|
|
154
|
+
bind:this={containerEl}
|
|
155
|
+
class="ptr-container {className}"
|
|
156
|
+
class:ptr-container--pulling={isPulling}
|
|
157
|
+
class:ptr-container--refreshing={refreshing}
|
|
158
|
+
class:ptr-container--disabled={disabled}
|
|
159
|
+
ontouchstart={handleTouchStart}
|
|
160
|
+
ontouchmove={handleTouchMove}
|
|
161
|
+
ontouchend={handleTouchEnd}
|
|
162
|
+
ontouchcancel={handleTouchCancel}
|
|
163
|
+
>
|
|
164
|
+
<!-- Pull indicator -->
|
|
165
|
+
<div
|
|
166
|
+
class="ptr-indicator"
|
|
167
|
+
class:ptr-indicator--visible={pullDistance > 0 || refreshing}
|
|
168
|
+
class:ptr-indicator--threshold-reached={isThresholdReached}
|
|
169
|
+
class:ptr-indicator--refreshing={refreshing}
|
|
170
|
+
style="transform: translateY({refreshing ? 20 : indicatorY}px); opacity: {refreshing
|
|
171
|
+
? 1
|
|
172
|
+
: indicatorOpacity};"
|
|
173
|
+
aria-hidden="true"
|
|
174
|
+
>
|
|
175
|
+
<div
|
|
176
|
+
class="ptr-spinner"
|
|
177
|
+
class:ptr-spinner--spinning={refreshing}
|
|
178
|
+
style="transform: rotate({refreshing ? 0 : indicatorRotation}deg);"
|
|
179
|
+
>
|
|
180
|
+
<svg viewBox="0 0 24 24" class="ptr-spinner__icon">
|
|
181
|
+
<path
|
|
182
|
+
d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46A7.93 7.93 0 0020 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74A7.93 7.93 0 004 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"
|
|
183
|
+
fill="currentColor"
|
|
184
|
+
/>
|
|
185
|
+
</svg>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<!-- Content wrapper -->
|
|
190
|
+
<div
|
|
191
|
+
bind:this={contentEl}
|
|
192
|
+
class="ptr-content"
|
|
193
|
+
style="transform: translateY({refreshing ? threshold * 0.5 : pullDistance}px);"
|
|
194
|
+
>
|
|
195
|
+
{#if children}
|
|
196
|
+
{@render children()}
|
|
197
|
+
{/if}
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<style>
|
|
202
|
+
.ptr-container {
|
|
203
|
+
position: relative;
|
|
204
|
+
width: 100%;
|
|
205
|
+
height: 100%;
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
touch-action: pan-y;
|
|
208
|
+
-webkit-overflow-scrolling: touch;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ptr-container--disabled {
|
|
212
|
+
pointer-events: none;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.ptr-indicator {
|
|
216
|
+
position: absolute;
|
|
217
|
+
top: 0;
|
|
218
|
+
left: 50%;
|
|
219
|
+
transform: translateX(-50%);
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
justify-content: center;
|
|
223
|
+
width: 40px;
|
|
224
|
+
height: 40px;
|
|
225
|
+
border-radius: 50%;
|
|
226
|
+
background: var(--color-bg, #ffffff);
|
|
227
|
+
box-shadow: var(
|
|
228
|
+
--shadow-md,
|
|
229
|
+
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
230
|
+
0 2px 4px -1px rgba(0, 0, 0, 0.06)
|
|
231
|
+
);
|
|
232
|
+
opacity: 0;
|
|
233
|
+
pointer-events: none;
|
|
234
|
+
z-index: 10;
|
|
235
|
+
margin-left: -20px;
|
|
236
|
+
transition: opacity 0.2s ease;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.ptr-indicator--visible {
|
|
240
|
+
opacity: 1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.ptr-indicator--threshold-reached {
|
|
244
|
+
background: var(--color-primary-light, #dbeafe);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.ptr-indicator--refreshing {
|
|
248
|
+
background: var(--color-bg, #ffffff);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.ptr-spinner {
|
|
252
|
+
display: flex;
|
|
253
|
+
align-items: center;
|
|
254
|
+
justify-content: center;
|
|
255
|
+
width: 24px;
|
|
256
|
+
height: 24px;
|
|
257
|
+
color: var(--color-primary, #3b82f6);
|
|
258
|
+
transition: transform 0.1s linear;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.ptr-spinner--spinning {
|
|
262
|
+
animation: ptr-spin 1s linear infinite;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.ptr-spinner__icon {
|
|
266
|
+
width: 100%;
|
|
267
|
+
height: 100%;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.ptr-content {
|
|
271
|
+
position: relative;
|
|
272
|
+
width: 100%;
|
|
273
|
+
height: 100%;
|
|
274
|
+
overflow-y: auto;
|
|
275
|
+
overflow-x: hidden;
|
|
276
|
+
-webkit-overflow-scrolling: touch;
|
|
277
|
+
will-change: transform;
|
|
278
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.ptr-container--pulling .ptr-content {
|
|
282
|
+
transition: none;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.ptr-container--refreshing .ptr-content {
|
|
286
|
+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Spinning animation */
|
|
290
|
+
@keyframes ptr-spin {
|
|
291
|
+
0% {
|
|
292
|
+
transform: rotate(0deg);
|
|
293
|
+
}
|
|
294
|
+
100% {
|
|
295
|
+
transform: rotate(360deg);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* Ensure minimum touch target size (F20 standard) */
|
|
300
|
+
.ptr-indicator {
|
|
301
|
+
min-width: 44px;
|
|
302
|
+
min-height: 44px;
|
|
303
|
+
}
|
|
304
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PullToRefreshProps } from '../../types/index.js';
|
|
2
|
+
interface Props extends PullToRefreshProps {
|
|
3
|
+
/** Controlled loading state (bindable) */
|
|
4
|
+
refreshing?: boolean;
|
|
5
|
+
/** Pull distance in pixels to trigger refresh (default: 80) */
|
|
6
|
+
threshold?: number;
|
|
7
|
+
/** Maximum pull distance in pixels (default: 150) */
|
|
8
|
+
maxPull?: number;
|
|
9
|
+
/** Disable pull-to-refresh functionality */
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
/** Additional CSS class */
|
|
12
|
+
class?: string;
|
|
13
|
+
/** Called when threshold reached and released */
|
|
14
|
+
onrefresh?: () => void;
|
|
15
|
+
/** Content to render inside */
|
|
16
|
+
children?: import('svelte').Snippet;
|
|
17
|
+
}
|
|
18
|
+
declare const PullToRefresh: import("svelte").Component<Props, {}, "refreshing">;
|
|
19
|
+
type PullToRefresh = ReturnType<typeof PullToRefresh>;
|
|
20
|
+
export default PullToRefresh;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PullToRefresh } from './PullToRefresh.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as PullToRefresh } from './PullToRefresh.svelte';
|