fluid-ui-svelte 0.2.4 → 0.3.1
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/LICENSE +22 -0
- package/README.md +16 -5
- package/dist/app.css +284 -0
- package/dist/base/Button.svelte +35 -0
- package/dist/base/Button.svelte.d.ts +14 -0
- package/dist/base/Container.svelte +34 -0
- package/dist/base/Container.svelte.d.ts +14 -0
- package/dist/base/Image.svelte +21 -0
- package/dist/base/Image.svelte.d.ts +8 -0
- package/dist/base/InputField.svelte +33 -0
- package/dist/base/InputField.svelte.d.ts +10 -0
- package/dist/base/Link.svelte +23 -0
- package/dist/base/Link.svelte.d.ts +10 -0
- package/dist/base/List.svelte +39 -0
- package/dist/base/List.svelte.d.ts +33 -0
- package/dist/base/Table.svelte +80 -0
- package/dist/base/Table.svelte.d.ts +42 -0
- package/dist/base/Text.svelte +45 -0
- package/dist/base/Text.svelte.d.ts +11 -0
- package/dist/base/index.d.ts +8 -0
- package/dist/base/index.js +8 -0
- package/dist/components/Accordion.svelte +44 -1
- package/dist/components/Accordion.svelte.d.ts +14 -25
- package/dist/components/Calendar.svelte +62 -0
- package/dist/components/Calendar.svelte.d.ts +12 -25
- package/dist/components/Carousel.svelte +114 -0
- package/dist/components/Carousel.svelte.d.ts +37 -0
- package/dist/components/CodeBlock.svelte +33 -0
- package/dist/components/CodeBlock.svelte.d.ts +9 -0
- package/dist/components/Drawer.svelte +77 -22
- package/dist/components/Drawer.svelte.d.ts +21 -6
- package/dist/components/{Badge.svelte.d.ts → ImageCrop.svelte.d.ts} +3 -3
- package/dist/components/Page.svelte +26 -0
- package/dist/components/Page.svelte.d.ts +11 -0
- package/dist/components/Switch.svelte +22 -20
- package/dist/components/Switch.svelte.d.ts +9 -4
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +8 -0
- package/dist/index.d.ts +2 -33
- package/dist/index.js +2 -36
- package/dist/{community/components/index.js → utilities/accesibility.js} +1 -0
- package/dist/utilities/applyCharacterFilter.d.ts +1 -0
- package/dist/utilities/applyCharacterFilter.js +6 -0
- package/dist/utilities/calendar.d.ts +10 -0
- package/dist/utilities/calendar.js +112 -0
- package/dist/utilities/carousel.d.ts +2 -0
- package/dist/utilities/carousel.js +27 -0
- package/dist/utilities/codeBlockContents.d.ts +15 -0
- package/dist/utilities/codeBlockContents.js +298 -0
- package/dist/utilities/imageCrop.d.ts +33 -0
- package/dist/utilities/imageCrop.js +217 -0
- package/dist/utilities/mergeClasses.d.ts +1 -0
- package/dist/utilities/mergeClasses.js +3 -0
- package/package.json +50 -33
- package/dist/community/prebuilt/index.d.ts +0 -1
- package/dist/community/prebuilt/index.js +0 -1
- package/dist/components/Breadcrumb.svelte +0 -1
- package/dist/components/Breadcrumb.svelte.d.ts +0 -26
- package/dist/components/Dropdown.svelte +0 -59
- package/dist/components/Dropdown.svelte.d.ts +0 -20
- package/dist/components/Dropzone.svelte +0 -1
- package/dist/components/Dropzone.svelte.d.ts +0 -26
- package/dist/components/InteractiveScrollArea.svelte +0 -1
- package/dist/components/InteractiveScrollArea.svelte.d.ts +0 -26
- package/dist/components/Pagination.svelte +0 -1
- package/dist/components/Pagination.svelte.d.ts +0 -26
- package/dist/components/Progress.svelte +0 -1
- package/dist/components/Progress.svelte.d.ts +0 -26
- package/dist/components/Tabs.svelte +0 -1
- package/dist/components/Tabs.svelte.d.ts +0 -26
- package/dist/components/Tooltip.svelte +0 -1
- package/dist/components/Tooltip.svelte.d.ts +0 -26
- package/dist/components/charts/BatteryChart.svelte +0 -0
- package/dist/components/charts/BatteryChart.svelte.d.ts +0 -26
- package/dist/components/charts/LineChart.svelte +0 -0
- package/dist/components/charts/LineChart.svelte.d.ts +0 -26
- package/dist/components/charts/PieChart.svelte +0 -0
- package/dist/components/charts/PieChart.svelte.d.ts +0 -26
- package/dist/components/notification/NotificationArea.svelte +0 -21
- package/dist/components/notification/NotificationArea.svelte.d.ts +0 -7
- package/dist/prebuilt/FormBuilder.svelte +0 -1
- package/dist/prebuilt/FormBuilder.svelte.d.ts +0 -26
- package/dist/prebuilt/document/Document.svelte +0 -1
- package/dist/prebuilt/document/Document.svelte.d.ts +0 -26
- package/dist/prebuilt/document/DocumentBlock.svelte +0 -1
- package/dist/prebuilt/document/DocumentBlock.svelte.d.ts +0 -26
- package/dist/prebuilt/document/DocumentPage.svelte +0 -1
- package/dist/prebuilt/document/DocumentPage.svelte.d.ts +0 -26
- package/dist/prebuilt/timeline/Timeline.svelte +0 -0
- package/dist/prebuilt/timeline/Timeline.svelte.d.ts +0 -26
- package/dist/prebuilt/timeline/TimelineItem.svelte +0 -0
- package/dist/prebuilt/timeline/TimelineItem.svelte.d.ts +0 -26
- package/dist/primitives/Button.svelte +0 -53
- package/dist/primitives/Button.svelte.d.ts +0 -13
- package/dist/primitives/Container.svelte +0 -51
- package/dist/primitives/Container.svelte.d.ts +0 -10
- package/dist/primitives/Dialog.svelte +0 -39
- package/dist/primitives/Dialog.svelte.d.ts +0 -10
- package/dist/primitives/Divider.svelte +0 -14
- package/dist/primitives/Divider.svelte.d.ts +0 -6
- package/dist/primitives/Form.svelte +0 -18
- package/dist/primitives/Form.svelte.d.ts +0 -8
- package/dist/primitives/Image.svelte +0 -57
- package/dist/primitives/Image.svelte.d.ts +0 -12
- package/dist/primitives/Input.svelte +0 -23
- package/dist/primitives/Input.svelte.d.ts +0 -9
- package/dist/primitives/Label.svelte +0 -18
- package/dist/primitives/Label.svelte.d.ts +0 -8
- package/dist/primitives/Link.svelte +0 -18
- package/dist/primitives/Link.svelte.d.ts +0 -8
- package/dist/primitives/Table.svelte +0 -18
- package/dist/primitives/Table.svelte.d.ts +0 -8
- package/dist/primitives/TableBody.svelte +0 -17
- package/dist/primitives/TableBody.svelte.d.ts +0 -7
- package/dist/primitives/TableData.svelte +0 -18
- package/dist/primitives/TableData.svelte.d.ts +0 -8
- package/dist/primitives/TableFooter.svelte +0 -17
- package/dist/primitives/TableFooter.svelte.d.ts +0 -7
- package/dist/primitives/TableHead.svelte +0 -18
- package/dist/primitives/TableHead.svelte.d.ts +0 -8
- package/dist/primitives/TableHeader.svelte +0 -17
- package/dist/primitives/TableHeader.svelte.d.ts +0 -7
- package/dist/primitives/TableRow.svelte +0 -18
- package/dist/primitives/TableRow.svelte.d.ts +0 -8
- package/dist/primitives/Text.svelte +0 -46
- package/dist/primitives/Text.svelte.d.ts +0 -8
- package/dist/primitives/TextArea.svelte +0 -15
- package/dist/primitives/TextArea.svelte.d.ts +0 -7
- package/dist/types.d.ts +0 -4
- package/dist/types.js +0 -1
- /package/dist/components/{Badge.svelte → ImageCrop.svelte} +0 -0
- /package/dist/{community/components/index.d.ts → utilities/accesibility.d.ts} +0 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Parsing errors happen in code editor when imports and script tags used inside code block component's contents.
|
|
2
|
+
// To prevent this we import usage examples from here.
|
|
3
|
+
const gettingStartedAppCss = `// src/routes/+layout.svelte
|
|
4
|
+
import '../app.css';`;
|
|
5
|
+
const gettingStartedUsage = `<script>
|
|
6
|
+
// Import a base element for custom implementation
|
|
7
|
+
import { Button } from 'fluid-ui-svelte/base';
|
|
8
|
+
|
|
9
|
+
// Import a pre-built component
|
|
10
|
+
import { Accordion } from 'fluid-ui-svelte/components';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Button onclick={handleClick}>Submit</Button>
|
|
14
|
+
|
|
15
|
+
<Accordion>
|
|
16
|
+
<!-- Accordion content -->
|
|
17
|
+
</Accordion>`;
|
|
18
|
+
const drawerBasicUsage = `<script>
|
|
19
|
+
import { Drawer } from 'fluid-ui-svelte/components';
|
|
20
|
+
import { Button, Text } from 'fluid-ui-svelte/base';
|
|
21
|
+
|
|
22
|
+
let isBasicDrawerOpen = $state(false);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<Button onclick={async () => isBasicDrawerOpen = true}>Open Drawer</Button>
|
|
26
|
+
|
|
27
|
+
<Drawer bind:isOpen={isBasicDrawerOpen} position="left">
|
|
28
|
+
<div class="flex flex-col gap-4">
|
|
29
|
+
<Text type="h2">Drawer Content</Text>
|
|
30
|
+
<Text>This is some content inside the drawer.</Text>
|
|
31
|
+
<Button onclick={async () => isBasicDrawerOpen = false}>Close</Button>
|
|
32
|
+
</div>
|
|
33
|
+
</Drawer>`;
|
|
34
|
+
const drawerPositions = `<script>
|
|
35
|
+
import { Drawer } from 'fluid-ui-svelte/components';
|
|
36
|
+
let left = $state(false);
|
|
37
|
+
let right = $state(false);
|
|
38
|
+
let top = $state(false);
|
|
39
|
+
let bottom = $state(false);
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<Button onclick={async () => left = true}>Left</Button>
|
|
43
|
+
<Button onclick={async () => right = true}>Right</Button>
|
|
44
|
+
<Button onclick={async () => top = true}>Top</Button>
|
|
45
|
+
<Button onclick={async () => bottom = true}>Bottom</Button>
|
|
46
|
+
|
|
47
|
+
<Drawer bind:isOpen={left} position="left">Left Drawer</Drawer>
|
|
48
|
+
<Drawer bind:isOpen={right} position="right">Right Drawer</Drawer>
|
|
49
|
+
<Drawer bind:isOpen={top} position="top">Top Drawer</Drawer>
|
|
50
|
+
<Drawer bind:isOpen={bottom} position="bottom">Bottom Drawer</Drawer>`;
|
|
51
|
+
const drawerAnimated = `<script>
|
|
52
|
+
import { Drawer } from 'fluid-ui-svelte/components';
|
|
53
|
+
import { fly, fade } from 'svelte/transition';
|
|
54
|
+
|
|
55
|
+
let isAnimatedDrawerOpen = $state(false);
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<Button onclick={async () => isAnimatedDrawerOpen = true}>Open Animated</Button>
|
|
59
|
+
|
|
60
|
+
<Drawer
|
|
61
|
+
bind:isOpen={isAnimatedDrawerOpen}
|
|
62
|
+
position="right"
|
|
63
|
+
transitionFn={fly}
|
|
64
|
+
transitionParams={{ x: 500, duration: 500 }}
|
|
65
|
+
backdropTransitionFn={fade}
|
|
66
|
+
>
|
|
67
|
+
Animated Content
|
|
68
|
+
</Drawer>`;
|
|
69
|
+
const drawerFlyAnimation = `<script>
|
|
70
|
+
import { Drawer } from 'fluid-ui-svelte/components';
|
|
71
|
+
import { fly } from 'svelte/transition';
|
|
72
|
+
|
|
73
|
+
let isFlyDrawerOpen = $state(false);
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<Button onclick={async () => isFlyDrawerOpen = true}>Open Fly Drawer</Button>
|
|
77
|
+
|
|
78
|
+
<Drawer
|
|
79
|
+
bind:isOpen={isFlyDrawerOpen}
|
|
80
|
+
position="bottom"
|
|
81
|
+
transitionFn={fly}
|
|
82
|
+
transitionParams={{ y: 200, duration: 800 }}
|
|
83
|
+
>
|
|
84
|
+
Fly Content
|
|
85
|
+
</Drawer>`;
|
|
86
|
+
const calendarSingle = `<script>
|
|
87
|
+
import { Calendar } from 'fluid-ui-svelte/components';
|
|
88
|
+
import { Button, Container } from 'fluid-ui-svelte/base';
|
|
89
|
+
|
|
90
|
+
let currentDate = $state(new Date().toISOString());
|
|
91
|
+
|
|
92
|
+
const changeMonth = (increment: number) => {
|
|
93
|
+
const date = new Date(currentDate);
|
|
94
|
+
date.setMonth(date.getMonth() + increment);
|
|
95
|
+
currentDate = date.toISOString();
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<Container class="flex flex-col gap-4">
|
|
100
|
+
<Container class="flex gap-2">
|
|
101
|
+
<Button onclick={() => changeMonth(-1)}>Prev</Button>
|
|
102
|
+
<Button onclick={() => changeMonth(1)}>Next</Button>
|
|
103
|
+
</Container>
|
|
104
|
+
<Calendar bind:currentDate componentId="calendar-single" />
|
|
105
|
+
</Container>`;
|
|
106
|
+
const calendarDual = `<script>
|
|
107
|
+
import { Calendar } from 'fluid-ui-svelte/components';
|
|
108
|
+
import { Button, Container } from 'fluid-ui-svelte/base';
|
|
109
|
+
|
|
110
|
+
let baseDate = $state(new Date().toISOString());
|
|
111
|
+
|
|
112
|
+
// Helper to get next month relative to base
|
|
113
|
+
const getOffsetDate = (offset: number) => {
|
|
114
|
+
const d = new Date(baseDate);
|
|
115
|
+
d.setMonth(d.getMonth() + offset);
|
|
116
|
+
return d.toISOString();
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const changeMonth = (increment: number) => {
|
|
120
|
+
const d = new Date(baseDate);
|
|
121
|
+
d.setMonth(d.getMonth() + increment);
|
|
122
|
+
baseDate = d.toISOString();
|
|
123
|
+
};
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<Container class="flex flex-col gap-4">
|
|
127
|
+
<Container class="flex gap-2">
|
|
128
|
+
<Button onclick={() => changeMonth(-1)}>Prev</Button>
|
|
129
|
+
<Button onclick={() => changeMonth(1)}>Next</Button>
|
|
130
|
+
</Container>
|
|
131
|
+
<Container class="flex gap-8 flex-wrap">
|
|
132
|
+
<Calendar currentDate={baseDate} componentId="cal-1" />
|
|
133
|
+
<Calendar currentDate={getOffsetDate(1)} componentId="cal-2" />
|
|
134
|
+
</Container>
|
|
135
|
+
</Container>`;
|
|
136
|
+
const calendarSixMonth = `<script>
|
|
137
|
+
import { Calendar } from 'fluid-ui-svelte/components';
|
|
138
|
+
import { Button, Container } from 'fluid-ui-svelte/base';
|
|
139
|
+
|
|
140
|
+
let baseDate = $state(new Date().toISOString());
|
|
141
|
+
|
|
142
|
+
const getOffsetDate = (offset: number) => {
|
|
143
|
+
const d = new Date(baseDate);
|
|
144
|
+
d.setMonth(d.getMonth() + offset);
|
|
145
|
+
return d.toISOString();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const changeMonth = (increment: number) => {
|
|
149
|
+
const d = new Date(baseDate);
|
|
150
|
+
d.setMonth(d.getMonth() + increment);
|
|
151
|
+
baseDate = d.toISOString();
|
|
152
|
+
};
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<Container class="flex flex-col gap-4">
|
|
156
|
+
<Container class="flex gap-2">
|
|
157
|
+
<Button onclick={() => changeMonth(-1)}>Prev</Button>
|
|
158
|
+
<Button onclick={() => changeMonth(1)}>Next</Button>
|
|
159
|
+
</Container>
|
|
160
|
+
<Container class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
161
|
+
{#each Array(6) as _, i}
|
|
162
|
+
<Calendar currentDate={getOffsetDate(i)} componentId="cal-{i}" />
|
|
163
|
+
{/each}
|
|
164
|
+
</Container>
|
|
165
|
+
</Container>`;
|
|
166
|
+
const calendarRange = `<script>
|
|
167
|
+
import { Calendar } from 'fluid-ui-svelte/components';
|
|
168
|
+
import { Button, Container, Text } from 'fluid-ui-svelte/base';
|
|
169
|
+
|
|
170
|
+
let baseDate = $state(new Date().toISOString());
|
|
171
|
+
let startDate = $state<string>();
|
|
172
|
+
let endDate = $state<string>();
|
|
173
|
+
|
|
174
|
+
const getOffsetDate = (offset: number) => {
|
|
175
|
+
const d = new Date(baseDate);
|
|
176
|
+
d.setMonth(d.getMonth() + offset);
|
|
177
|
+
return d.toISOString();
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const changeMonth = (inc: number) => {
|
|
181
|
+
const d = new Date(baseDate);
|
|
182
|
+
d.setMonth(d.getMonth() + inc);
|
|
183
|
+
baseDate = d.toISOString();
|
|
184
|
+
};
|
|
185
|
+
</script>
|
|
186
|
+
|
|
187
|
+
<Container class="flex flex-col gap-4">
|
|
188
|
+
<Container class="flex justify-between items-center">
|
|
189
|
+
<Container class="flex gap-2">
|
|
190
|
+
<Button onclick={() => changeMonth(-1)}>Prev</Button>
|
|
191
|
+
<Button onclick={() => changeMonth(1)}>Next</Button>
|
|
192
|
+
</Container>
|
|
193
|
+
<Text class="text-sm font-mono">
|
|
194
|
+
{startDate || '...'} to {endDate || '...'}
|
|
195
|
+
</Text>
|
|
196
|
+
</Container>
|
|
197
|
+
|
|
198
|
+
<Container class="flex gap-8 flex-wrap">
|
|
199
|
+
<Calendar
|
|
200
|
+
currentDate={baseDate}
|
|
201
|
+
bind:startDate
|
|
202
|
+
bind:endDate
|
|
203
|
+
componentId="cal-range-1"
|
|
204
|
+
/>
|
|
205
|
+
<Calendar
|
|
206
|
+
currentDate={getOffsetDate(1)}
|
|
207
|
+
bind:startDate
|
|
208
|
+
bind:endDate
|
|
209
|
+
componentId="cal-range-2"
|
|
210
|
+
/>
|
|
211
|
+
</Container>
|
|
212
|
+
</Container>`;
|
|
213
|
+
const calendarMulti = `<script>
|
|
214
|
+
import { Calendar } from 'fluid-ui-svelte/components';
|
|
215
|
+
import { Button, Container } from 'fluid-ui-svelte/base';
|
|
216
|
+
|
|
217
|
+
const multiCalendarState = $state({
|
|
218
|
+
currentDate: new Date().toISOString(),
|
|
219
|
+
startDate: '',
|
|
220
|
+
endDate: ''
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const changeMonthMulti = (inc: number) => {
|
|
224
|
+
const d = new Date(multiCalendarState.currentDate);
|
|
225
|
+
d.setMonth(d.getMonth() + inc);
|
|
226
|
+
multiCalendarState.currentDate = d.toISOString();
|
|
227
|
+
};
|
|
228
|
+
</script>
|
|
229
|
+
|
|
230
|
+
<Container class="flex flex-col gap-4">
|
|
231
|
+
<Container class="flex gap-2">
|
|
232
|
+
<Button onclick={() => changeMonthMulti(-1)}>Prev</Button>
|
|
233
|
+
<Button onclick={() => changeMonthMulti(1)}>Next</Button>
|
|
234
|
+
</Container>
|
|
235
|
+
|
|
236
|
+
<Container class="flex gap-8 flex-wrap">
|
|
237
|
+
<Calendar
|
|
238
|
+
bind:currentDate={multiCalendarState.currentDate}
|
|
239
|
+
bind:startDate={multiCalendarState.startDate}
|
|
240
|
+
bind:endDate={multiCalendarState.endDate}
|
|
241
|
+
/>
|
|
242
|
+
<Calendar
|
|
243
|
+
currentDate={new Date(new Date(multiCalendarState.currentDate).setMonth(new Date(multiCalendarState.currentDate).getMonth() + 1)).toISOString()}
|
|
244
|
+
bind:startDate={multiCalendarState.startDate}
|
|
245
|
+
bind:endDate={multiCalendarState.endDate}
|
|
246
|
+
/>
|
|
247
|
+
</Container>
|
|
248
|
+
</Container>`;
|
|
249
|
+
const carouselSlide = `<script>
|
|
250
|
+
import { Carousel } from 'fluid-ui-svelte/components';
|
|
251
|
+
import { Image } from 'fluid-ui-svelte/base';
|
|
252
|
+
|
|
253
|
+
const items = [
|
|
254
|
+
{ src: 'https://via.placeholder.com/800x400?text=Slide+1', alt: 'Slide 1' },
|
|
255
|
+
{ src: 'https://via.placeholder.com/800x400?text=Slide+2', alt: 'Slide 2' },
|
|
256
|
+
{ src: 'https://via.placeholder.com/800x400?text=Slide+3', alt: 'Slide 3' }
|
|
257
|
+
];
|
|
258
|
+
</script>
|
|
259
|
+
|
|
260
|
+
<Carousel {items} type="slide" loop>
|
|
261
|
+
{#snippet children(item)}
|
|
262
|
+
<Image src={item.src} alt={item.alt} class="w-full h-64 object-cover" />
|
|
263
|
+
{/snippet}
|
|
264
|
+
</Carousel>`;
|
|
265
|
+
const carouselFade = `<script>
|
|
266
|
+
import { Carousel } from 'fluid-ui-svelte/components';
|
|
267
|
+
import { Text } from 'fluid-ui-svelte/base';
|
|
268
|
+
|
|
269
|
+
const quotes = [
|
|
270
|
+
{ text: "Innovation is key.", author: "Tech CEO" },
|
|
271
|
+
{ text: "Design is intelligence made visible.", author: "Designer" },
|
|
272
|
+
{ text: "Simplicity is the ultimate sophistication.", author: "Da Vinci" }
|
|
273
|
+
];
|
|
274
|
+
</script>
|
|
275
|
+
|
|
276
|
+
<Carousel {items: quotes} type="fade" autoplay autoplayInterval={4000}>
|
|
277
|
+
{#snippet children(quote)}
|
|
278
|
+
<div class="h-64 flex flex-col items-center justify-center bg-neutral-100 dark:bg-neutral-800 p-8 text-center">
|
|
279
|
+
<Text type="h3" class="text-2xl font-bold mb-4">"{quote.text}"</Text>
|
|
280
|
+
<Text class="text-neutral-500">- {quote.author}</Text>
|
|
281
|
+
</div>
|
|
282
|
+
{/snippet}
|
|
283
|
+
</Carousel>`;
|
|
284
|
+
export const codeBlockContents = {
|
|
285
|
+
gettingStartedAppCss,
|
|
286
|
+
gettingStartedUsage,
|
|
287
|
+
drawerBasicUsage,
|
|
288
|
+
drawerPositions,
|
|
289
|
+
drawerAnimated,
|
|
290
|
+
drawerFlyAnimation,
|
|
291
|
+
calendarSingle,
|
|
292
|
+
calendarDual,
|
|
293
|
+
calendarSixMonth,
|
|
294
|
+
calendarRange,
|
|
295
|
+
calendarMulti,
|
|
296
|
+
carouselSlide,
|
|
297
|
+
carouselFade
|
|
298
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare function generateCropAreaState(shape?: 'circle' | 'rectangle', aspectRatio?: {
|
|
2
|
+
w: number;
|
|
3
|
+
h: number;
|
|
4
|
+
}): {
|
|
5
|
+
cropArea: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
shape: "circle" | "rectangle";
|
|
12
|
+
aspectRatio: {
|
|
13
|
+
w: number;
|
|
14
|
+
h: number;
|
|
15
|
+
};
|
|
16
|
+
activeAction: string;
|
|
17
|
+
lastMousePosition: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export declare function draw(canvasElement: HTMLCanvasElement, image: HTMLImageElement, cropAreaState: ReturnType<typeof generateCropAreaState>): void;
|
|
23
|
+
export declare function handleMouseDown(event: PointerEvent | MouseEvent, cropAreaState: ReturnType<typeof generateCropAreaState>, canvasElement?: HTMLCanvasElement): void;
|
|
24
|
+
export declare function handleMouseMove(event: PointerEvent | MouseEvent, cropAreaState: ReturnType<typeof generateCropAreaState>, canvasElement?: HTMLCanvasElement): void;
|
|
25
|
+
export declare function handleMouseUp(cropAreaState: ReturnType<typeof generateCropAreaState>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Export the currently selected crop as a PNG Blob.
|
|
28
|
+
* - If shape === 'rectangle' exports the rectangular region.
|
|
29
|
+
* - If shape === 'circle' exports a square canvas with a circular alpha mask (transparent outside circle).
|
|
30
|
+
*
|
|
31
|
+
* Returns Promise<Blob | null>.
|
|
32
|
+
*/
|
|
33
|
+
export declare function exportSelectionAsPNG(canvasElement: HTMLCanvasElement, image: HTMLImageElement, cropAreaState: ReturnType<typeof generateCropAreaState>): Promise<Blob | null>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
export function generateCropAreaState(shape = 'rectangle', aspectRatio = { w: 1, h: 1 }) {
|
|
2
|
+
return {
|
|
3
|
+
cropArea: { x: 0, y: 0, width: 0, height: 0 },
|
|
4
|
+
shape,
|
|
5
|
+
aspectRatio,
|
|
6
|
+
activeAction: 'idle',
|
|
7
|
+
lastMousePosition: { x: 0, y: 0 }
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function draw(canvasElement, image, cropAreaState) {
|
|
11
|
+
if (!image || !canvasElement)
|
|
12
|
+
return;
|
|
13
|
+
const context = canvasElement.getContext('2d');
|
|
14
|
+
// 1. Draw the base image
|
|
15
|
+
context.drawImage(image, 0, 0);
|
|
16
|
+
// 2. Draw the semi-transparent mask
|
|
17
|
+
context.fillStyle = 'rgba(0, 0, 0, 0.2)';
|
|
18
|
+
context.fillRect(0, 0, canvasElement.width, canvasElement.height);
|
|
19
|
+
// 3. Cut a "hole" for the crop area
|
|
20
|
+
drawCropArea(context, cropAreaState);
|
|
21
|
+
// 4. Draw the resize handles
|
|
22
|
+
drawHandles(context, cropAreaState);
|
|
23
|
+
}
|
|
24
|
+
function drawCropArea(context, cropAreaState) {
|
|
25
|
+
context.save(); // Save the current state
|
|
26
|
+
context.globalCompositeOperation = 'destination-out';
|
|
27
|
+
if (cropAreaState.shape === 'circle') {
|
|
28
|
+
const { x, y, width, height } = cropAreaState.cropArea;
|
|
29
|
+
// Use the smaller dimension so the circle always fits inside the bounding box
|
|
30
|
+
const radius = Math.min(width, height) / 2;
|
|
31
|
+
const centerX = x + width / 2;
|
|
32
|
+
const centerY = y + height / 2;
|
|
33
|
+
context.beginPath();
|
|
34
|
+
context.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
35
|
+
context.fill();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Default to square/rectangle
|
|
39
|
+
context.fillRect(cropAreaState.cropArea.x, cropAreaState.cropArea.y, cropAreaState.cropArea.width, cropAreaState.cropArea.height);
|
|
40
|
+
}
|
|
41
|
+
context.restore(); // Restore to the previous state (resets globalCompositeOperation)
|
|
42
|
+
}
|
|
43
|
+
const HANDLE_SIZE = 24;
|
|
44
|
+
function getHandles(cropAreaState) {
|
|
45
|
+
const { x, y, width, height } = cropAreaState.cropArea;
|
|
46
|
+
// If the crop shape is a circle, place handles around the actual circle (inscribed),
|
|
47
|
+
// with a small padding so they sit slightly outside the circle's edge.
|
|
48
|
+
if (cropAreaState.shape === 'circle') {
|
|
49
|
+
const centerX = x + width / 2;
|
|
50
|
+
const centerY = y + height / 2;
|
|
51
|
+
const radius = Math.min(width, height) / 2;
|
|
52
|
+
const padding = radius * 0.05; // 5% padding
|
|
53
|
+
const r = radius + padding;
|
|
54
|
+
const diagOffset = r / Math.sqrt(2); // 45° corner offset
|
|
55
|
+
return [
|
|
56
|
+
// corners (45° around circle)
|
|
57
|
+
{ x: centerX - diagOffset, y: centerY - diagOffset, name: 'tl' },
|
|
58
|
+
{ x: centerX + diagOffset, y: centerY - diagOffset, name: 'tr' },
|
|
59
|
+
{ x: centerX + diagOffset, y: centerY + diagOffset, name: 'br' },
|
|
60
|
+
{ x: centerX - diagOffset, y: centerY + diagOffset, name: 'bl' },
|
|
61
|
+
// sides (cardinal points on circle)
|
|
62
|
+
{ x: centerX, y: centerY - r, name: 't' },
|
|
63
|
+
{ x: centerX + r, y: centerY, name: 'r' },
|
|
64
|
+
{ x: centerX, y: centerY + r, name: 'b' },
|
|
65
|
+
{ x: centerX - r, y: centerY, name: 'l' }
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
// Default rectangle handles (unchanged)
|
|
69
|
+
return [
|
|
70
|
+
// corners
|
|
71
|
+
{ x: x, y: y, name: 'tl' }, // top-left
|
|
72
|
+
{ x: x + width, y: y, name: 'tr' }, // top-right
|
|
73
|
+
{ x: x + width, y: y + height, name: 'br' }, // bottom-right
|
|
74
|
+
{ x: x, y: y + height, name: 'bl' }, // bottom-left
|
|
75
|
+
// sides
|
|
76
|
+
{ x: x + width / 2, y: y, name: 't' }, // top
|
|
77
|
+
{ x: x + width, y: y + height / 2, name: 'r' }, // right
|
|
78
|
+
{ x: x + width / 2, y: y + height, name: 'b' }, // bottom
|
|
79
|
+
{ x: x, y: y + height / 2, name: 'l' } // left
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
function drawHandles(context, cropAreaState) {
|
|
83
|
+
context.fillStyle = 'white';
|
|
84
|
+
const handles = getHandles(cropAreaState);
|
|
85
|
+
for (const handle of handles) {
|
|
86
|
+
context.fillRect(handle.x - HANDLE_SIZE / 2, handle.y - HANDLE_SIZE / 2, HANDLE_SIZE, HANDLE_SIZE);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Map a PointerEvent/MouseEvent to canvas coordinate space (accounts for CSS scaling).
|
|
91
|
+
*/
|
|
92
|
+
function getCanvasCoords(event, canvas) {
|
|
93
|
+
// Use client coordinates and canvas bounding box to map to drawing buffer pixels
|
|
94
|
+
const rect = canvas.getBoundingClientRect();
|
|
95
|
+
const clientX = event.clientX ?? event.clientX;
|
|
96
|
+
const clientY = event.clientY ?? event.clientY;
|
|
97
|
+
const scaleX = canvas.width / rect.width;
|
|
98
|
+
const scaleY = canvas.height / rect.height;
|
|
99
|
+
return {
|
|
100
|
+
x: (clientX - rect.left) * scaleX,
|
|
101
|
+
y: (clientY - rect.top) * scaleY
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function clampCropToCanvas(cropArea, canvas, shape = 'rectangle', minSize = 20) {
|
|
105
|
+
// Ensure positive sizes
|
|
106
|
+
cropArea.width = Math.max(minSize, cropArea.width);
|
|
107
|
+
cropArea.height = Math.max(minSize, cropArea.height);
|
|
108
|
+
if (shape === 'circle') {
|
|
109
|
+
// Force a square bounding box that stays centered on the current center.
|
|
110
|
+
const centerX = cropArea.x + cropArea.width / 2;
|
|
111
|
+
const centerY = cropArea.y + cropArea.height / 2;
|
|
112
|
+
const size = Math.max(minSize, Math.min(cropArea.width, cropArea.height));
|
|
113
|
+
cropArea.width = size;
|
|
114
|
+
cropArea.height = size;
|
|
115
|
+
cropArea.x = centerX - size / 2;
|
|
116
|
+
cropArea.y = centerY - size / 2;
|
|
117
|
+
}
|
|
118
|
+
// Keep position inside canvas
|
|
119
|
+
cropArea.x = Math.max(0, Math.min(cropArea.x, canvas.width - cropArea.width));
|
|
120
|
+
cropArea.y = Math.max(0, Math.min(cropArea.y, canvas.height - cropArea.height));
|
|
121
|
+
}
|
|
122
|
+
export function handleMouseDown(event, cropAreaState, canvasElement) {
|
|
123
|
+
if (!canvasElement)
|
|
124
|
+
return;
|
|
125
|
+
const { x: offsetX, y: offsetY } = getCanvasCoords(event, canvasElement);
|
|
126
|
+
const handles = getHandles(cropAreaState);
|
|
127
|
+
// Check if a handle was clicked (handles are in canvas coordinates)
|
|
128
|
+
for (const handle of handles) {
|
|
129
|
+
const handleX = handle.x - HANDLE_SIZE / 2;
|
|
130
|
+
const handleY = handle.y - HANDLE_SIZE / 2;
|
|
131
|
+
if (offsetX >= handleX &&
|
|
132
|
+
offsetX <= handleX + HANDLE_SIZE &&
|
|
133
|
+
offsetY >= handleY &&
|
|
134
|
+
offsetY <= handleY + HANDLE_SIZE) {
|
|
135
|
+
cropAreaState.activeAction = `resizing-${handle.name}`;
|
|
136
|
+
cropAreaState.lastMousePosition = { x: offsetX, y: offsetY };
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// If no handle was clicked, check if the main rectangle was clicked
|
|
141
|
+
const { x, y, width, height } = cropAreaState.cropArea;
|
|
142
|
+
if (offsetX >= x && offsetX <= x + width && offsetY >= y && offsetY <= y + height) {
|
|
143
|
+
cropAreaState.activeAction = 'moving';
|
|
144
|
+
cropAreaState.lastMousePosition = { x: offsetX, y: offsetY };
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
export function handleMouseMove(event, cropAreaState, canvasElement) {
|
|
149
|
+
if (cropAreaState.activeAction === 'idle')
|
|
150
|
+
return;
|
|
151
|
+
if (!canvasElement)
|
|
152
|
+
return;
|
|
153
|
+
const { x: offsetX, y: offsetY } = getCanvasCoords(event, canvasElement);
|
|
154
|
+
const deltaX = offsetX - cropAreaState.lastMousePosition.x;
|
|
155
|
+
const deltaY = offsetY - cropAreaState.lastMousePosition.y;
|
|
156
|
+
const { cropArea } = cropAreaState;
|
|
157
|
+
if (cropAreaState.activeAction === 'moving') {
|
|
158
|
+
cropArea.x += deltaX;
|
|
159
|
+
cropArea.y += deltaY;
|
|
160
|
+
}
|
|
161
|
+
else if (cropAreaState.activeAction.startsWith('resizing-')) {
|
|
162
|
+
const handleName = cropAreaState.activeAction.split('-')[1];
|
|
163
|
+
if (handleName.includes('r'))
|
|
164
|
+
cropArea.width += deltaX;
|
|
165
|
+
if (handleName.includes('l')) {
|
|
166
|
+
cropArea.x += deltaX;
|
|
167
|
+
cropArea.width -= deltaX;
|
|
168
|
+
}
|
|
169
|
+
if (handleName.includes('b'))
|
|
170
|
+
cropArea.height += deltaY;
|
|
171
|
+
if (handleName.includes('t')) {
|
|
172
|
+
cropArea.y += deltaY;
|
|
173
|
+
cropArea.height -= deltaY;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Prevent negative/too-small sizes and keep crop within canvas
|
|
177
|
+
// pass the current shape so a circle forces a square bounding box
|
|
178
|
+
clampCropToCanvas(cropArea, canvasElement, cropAreaState.shape, 20);
|
|
179
|
+
cropAreaState.lastMousePosition = { x: offsetX, y: offsetY };
|
|
180
|
+
}
|
|
181
|
+
export function handleMouseUp(cropAreaState) {
|
|
182
|
+
cropAreaState.activeAction = 'idle';
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Export the currently selected crop as a PNG Blob.
|
|
186
|
+
* - If shape === 'rectangle' exports the rectangular region.
|
|
187
|
+
* - If shape === 'circle' exports a square canvas with a circular alpha mask (transparent outside circle).
|
|
188
|
+
*
|
|
189
|
+
* Returns Promise<Blob | null>.
|
|
190
|
+
*/
|
|
191
|
+
export async function exportSelectionAsPNG(canvasElement, image, cropAreaState) {
|
|
192
|
+
if (!canvasElement || !image)
|
|
193
|
+
return null;
|
|
194
|
+
const { x, y, width, height } = cropAreaState.cropArea;
|
|
195
|
+
if (width <= 0 || height <= 0)
|
|
196
|
+
return null;
|
|
197
|
+
// Create an offscreen canvas sized to the crop bounding box (square for circle)
|
|
198
|
+
const outCanvas = document.createElement('canvas');
|
|
199
|
+
outCanvas.width = Math.round(width);
|
|
200
|
+
outCanvas.height = Math.round(height);
|
|
201
|
+
const ctx = outCanvas.getContext('2d');
|
|
202
|
+
if (!ctx)
|
|
203
|
+
return null;
|
|
204
|
+
// Draw the corresponding portion of the source image
|
|
205
|
+
// coords are in canvas/image pixel space because the component sets canvas size to image.naturalWidth/Height
|
|
206
|
+
ctx.drawImage(image, x, y, width, height, 0, 0, outCanvas.width, outCanvas.height);
|
|
207
|
+
if (cropAreaState.shape === 'circle') {
|
|
208
|
+
// Mask to a circle (destination-in keeps the circular area)
|
|
209
|
+
ctx.globalCompositeOperation = 'destination-in';
|
|
210
|
+
ctx.beginPath();
|
|
211
|
+
const r = Math.min(outCanvas.width, outCanvas.height) / 2;
|
|
212
|
+
ctx.arc(outCanvas.width / 2, outCanvas.height / 2, r, 0, Math.PI * 2);
|
|
213
|
+
ctx.fill();
|
|
214
|
+
}
|
|
215
|
+
// Return a PNG Blob
|
|
216
|
+
return await new Promise((resolve) => outCanvas.toBlob((b) => resolve(b), 'image/png'));
|
|
217
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mergeClasses(classA: string, classB: string): string;
|
package/package.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluid-ui-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Emre Ayaz",
|
|
6
|
-
"email": "
|
|
7
|
-
"url": "https://emreayaz.com"
|
|
6
|
+
"email": "ayazthemre@gmail.com"
|
|
8
7
|
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/ayazemre/fluid-ui-svelte",
|
|
9
10
|
"repository": {
|
|
10
|
-
"url": "https://github.com/ayazemre/fluid-ui"
|
|
11
|
+
"url": "git+https://github.com/ayazemre/fluid-ui-svelte.git"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
13
14
|
"dev": "vite dev",
|
|
14
|
-
"build": "vite build && npm run
|
|
15
|
+
"build": "vite build && npm run prepack",
|
|
15
16
|
"preview": "vite preview",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
17
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
18
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
18
19
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
19
20
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
20
21
|
"format": "prettier --write .",
|
|
21
22
|
"lint": "prettier --check . && eslint .",
|
|
22
23
|
"test:unit": "vitest",
|
|
23
|
-
"test": "npm run test:unit -- --run
|
|
24
|
-
"test:e2e": "playwright test"
|
|
24
|
+
"test": "npm run test:unit -- --run"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
@@ -38,34 +38,51 @@
|
|
|
38
38
|
".": {
|
|
39
39
|
"types": "./dist/index.d.ts",
|
|
40
40
|
"svelte": "./dist/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./base": {
|
|
43
|
+
"types": "./dist/base/index.d.ts",
|
|
44
|
+
"svelte": "./dist/base/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./components": {
|
|
47
|
+
"types": "./dist/components/index.d.ts",
|
|
48
|
+
"svelte": "./dist/components/index.js"
|
|
41
49
|
}
|
|
42
50
|
},
|
|
43
51
|
"peerDependencies": {
|
|
44
52
|
"svelte": "^5.0.0"
|
|
45
53
|
},
|
|
46
54
|
"devDependencies": {
|
|
47
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"@sveltejs/
|
|
51
|
-
"@sveltejs/
|
|
52
|
-
"@
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"svelte
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
55
|
+
"@eslint/compat": "^1.4.0",
|
|
56
|
+
"@eslint/js": "^9.38.0",
|
|
57
|
+
"@iconify/svelte": "^5.1.0",
|
|
58
|
+
"@sveltejs/adapter-auto": "^7.0.0",
|
|
59
|
+
"@sveltejs/adapter-node": "^5.4.0",
|
|
60
|
+
"@sveltejs/kit": "^2.47.1",
|
|
61
|
+
"@sveltejs/package": "^2.5.4",
|
|
62
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
63
|
+
"@tailwindcss/vite": "^4.1.14",
|
|
64
|
+
"@types/node": "^22",
|
|
65
|
+
"@vitest/browser": "^3.2.4",
|
|
66
|
+
"@vitest/ui": "^3.2.4",
|
|
67
|
+
"eslint": "^9.38.0",
|
|
68
|
+
"eslint-config-prettier": "^10.1.8",
|
|
69
|
+
"eslint-plugin-svelte": "^3.12.4",
|
|
70
|
+
"globals": "^16.4.0",
|
|
71
|
+
"playwright": "^1.56.1",
|
|
72
|
+
"prettier": "^3.6.2",
|
|
73
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
74
|
+
"prettier-plugin-tailwindcss": "^0.7.1",
|
|
75
|
+
"publint": "^0.3.14",
|
|
76
|
+
"svelte": "^5.41.0",
|
|
77
|
+
"svelte-check": "^4.3.3",
|
|
78
|
+
"tailwindcss": "^4.1.14",
|
|
79
|
+
"typescript": "^5.9.3",
|
|
80
|
+
"typescript-eslint": "^8.46.1",
|
|
81
|
+
"vite": "^7.1.10",
|
|
82
|
+
"vitest": "^3.2.4",
|
|
83
|
+
"vitest-browser-svelte": "^1.1.0"
|
|
66
84
|
},
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
85
|
+
"keywords": [
|
|
86
|
+
"svelte"
|
|
87
|
+
]
|
|
88
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<!-- TODO -->
|