@coyalabs/bts-style 1.3.7 → 1.3.9
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 +1454 -0
- package/dist/Base/BaseContainer.svelte +1 -1
- package/dist/Components/Button.svelte +12 -4
- package/dist/Components/Button.svelte.d.ts +2 -0
- package/dist/Components/ContextMenu.svelte +4 -2
- package/dist/Components/Dropdown.svelte +79 -30
- package/dist/Components/LinearList.svelte +104 -0
- package/dist/Components/LinearList.svelte.d.ts +146 -0
- package/dist/Components/Popup/AlertPopup.svelte +2 -1
- package/dist/Components/Popup/ConfirmPopup.svelte +2 -1
- package/dist/Components/Popup/Popup.svelte +50 -23
- package/dist/Components/Popup/PromptPopup.svelte +2 -1
- package/dist/Components/Popup/popupStore.d.ts +41 -0
- package/dist/Components/{popupStore.js → Popup/popupStore.js} +59 -32
- package/dist/Components/Toast/Toast.svelte +57 -0
- package/dist/Components/Toast/Toast.svelte.d.ts +26 -0
- package/dist/Components/Toast/toastStore.d.ts +35 -0
- package/dist/Components/Toast/toastStore.js +53 -0
- package/dist/Components/Tooltip.svelte +1 -1
- package/dist/Components/TreeDirectory.svelte +20 -1
- package/dist/Components/TreeDirectory.svelte.d.ts +2 -0
- package/dist/icons.d.ts +3 -0
- package/dist/icons.js +3 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/package.json +5 -3
- package/dist/Components/popupStore.d.ts +0 -31
package/README.md
ADDED
|
@@ -0,0 +1,1454 @@
|
|
|
1
|
+
# @coyalabs/bts-style
|
|
2
|
+
|
|
3
|
+
**Coya Behind the Scenes** - A Svelte 4 component library with a serif 1920s aesthetic.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @coyalabs/bts-style
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```svelte
|
|
14
|
+
<script>
|
|
15
|
+
import { BasePage, Button, BaseText } from '@coyalabs/bts-style';
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<BasePage>
|
|
19
|
+
<BaseText variant="title">Hello BTS Theme!</BaseText>
|
|
20
|
+
<Button theme="primary">Click Me</Button>
|
|
21
|
+
</BasePage>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Design System
|
|
25
|
+
|
|
26
|
+
### Colors
|
|
27
|
+
|
|
28
|
+
- **Background:** `#0B070D` - Deep dark page background
|
|
29
|
+
- **Default Text:** `#E3D8D8` - Light text and icons
|
|
30
|
+
- **Toned:** `#A18F8F` - Muted icons and accents
|
|
31
|
+
- **Accent:** `#FFEFF6` - Interactive elements
|
|
32
|
+
|
|
33
|
+
### Fonts
|
|
34
|
+
|
|
35
|
+
The package uses fonts from Google Fonts and Fontshare (loaded via CDN):
|
|
36
|
+
- **Noto Serif KR (900)** - Titles and button text
|
|
37
|
+
- **Satoshi (400, 500, 700, 900)** - Content and UI text
|
|
38
|
+
|
|
39
|
+
### Theme Variants
|
|
40
|
+
|
|
41
|
+
All container-based components support these themes:
|
|
42
|
+
|
|
43
|
+
- **`full`** - Standard glass container with subtle background
|
|
44
|
+
- **`primary`** - Slightly more prominent glass effect
|
|
45
|
+
- **`secondary`** - Alternative glass styling
|
|
46
|
+
- **`vague`** - Subtle thing
|
|
47
|
+
- **`filled`** - Use for solid container sections (customizable)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Components
|
|
52
|
+
|
|
53
|
+
### Base Components
|
|
54
|
+
|
|
55
|
+
#### BasePage
|
|
56
|
+
|
|
57
|
+
Root page component with automatic favicon setup, dark background, and decorative background elements.
|
|
58
|
+
|
|
59
|
+
**Props:**
|
|
60
|
+
- `favicon?: string | null` - Custom favicon URL (defaults to BTS theme icon)
|
|
61
|
+
- `showBGDetails?: boolean` - Show/hide decorative background elements (default: `true`)
|
|
62
|
+
- `chainColor?: string` - Color of the animated chain (default: `"#130F15"`)
|
|
63
|
+
|
|
64
|
+
**Example:**
|
|
65
|
+
```svelte
|
|
66
|
+
<BasePage favicon="/custom-icon.svg">
|
|
67
|
+
<!-- Your app content -->
|
|
68
|
+
</BasePage>
|
|
69
|
+
|
|
70
|
+
<!-- Without background decorations -->
|
|
71
|
+
<BasePage showBGDetails={false}>
|
|
72
|
+
<!-- Clean page without gears/chains -->
|
|
73
|
+
</BasePage>
|
|
74
|
+
|
|
75
|
+
<!-- Custom chain color -->
|
|
76
|
+
<BasePage chainColor="#1a1520">
|
|
77
|
+
<!-- Content with custom chain color -->
|
|
78
|
+
</BasePage>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Features:**
|
|
82
|
+
- Sets min-height: 100vh
|
|
83
|
+
- Applies #0B070D background
|
|
84
|
+
- Automatically injects BTS favicon unless overridden
|
|
85
|
+
- Resets body/html margins
|
|
86
|
+
- Decorative BGDetails layer with animated gears and swinging chain
|
|
87
|
+
|
|
88
|
+
**Background Details (BGDetails):**
|
|
89
|
+
|
|
90
|
+
The `BGDetails` component renders decorative 1920s-style animated elements:
|
|
91
|
+
- **Chain** - A physics-simulated swinging chain in the top-left corner
|
|
92
|
+
- **Gears** - Rotating interlocked gears in the top-right corner
|
|
93
|
+
- **Single Gear** - A slowly rotating gear in the bottom-left corner
|
|
94
|
+
|
|
95
|
+
These elements are purely decorative (pointer-events: none) and sit behind your content.
|
|
96
|
+
Disable with showBGDetails={false}.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
#### BaseContainer
|
|
101
|
+
|
|
102
|
+
Container with customizable corners and themes.
|
|
103
|
+
|
|
104
|
+
**Props:**
|
|
105
|
+
- `theme?: 'full' | 'primary' | 'secondary' | 'filled'` - Visual theme (default: `'full'`)
|
|
106
|
+
- `padding?: string` - CSS padding value (default: `'1.5rem'`)
|
|
107
|
+
- `borderRadiusTopLeft?: string` - Top-left corner radius (default: `'25px'`)
|
|
108
|
+
- `borderRadiusTopRight?: string` - Top-right corner radius (default: `'25px'`)
|
|
109
|
+
- `borderRadiusBottomLeft?: string` - Bottom-left corner radius (default: `'25px'`)
|
|
110
|
+
- `borderRadiusBottomRight?: string` - Bottom-right corner radius (default: `'25px'`)
|
|
111
|
+
|
|
112
|
+
**Example:**
|
|
113
|
+
```svelte
|
|
114
|
+
<BaseContainer theme="primary" borderRadiusTopLeft="35px">
|
|
115
|
+
<slot />
|
|
116
|
+
</BaseContainer>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
#### BaseText
|
|
122
|
+
|
|
123
|
+
Typography component with three text variants.
|
|
124
|
+
|
|
125
|
+
**Props:**
|
|
126
|
+
- `variant: 'title' | 'content' | 'button'` - Text style variant
|
|
127
|
+
|
|
128
|
+
**Variants:**
|
|
129
|
+
- **`title`** - Noto Serif KR 900, 30px - For headings
|
|
130
|
+
- **`content`** - Satoshi 700, 18px - For body text
|
|
131
|
+
- **`button`** - Noto Serif KR 900, 17px - For button labels
|
|
132
|
+
|
|
133
|
+
**Example:**
|
|
134
|
+
```svelte
|
|
135
|
+
<BaseText variant="title">Page Title</BaseText>
|
|
136
|
+
<BaseText variant="content">This is content text.</BaseText>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
#### BaseIcon
|
|
142
|
+
|
|
143
|
+
SVG icon wrapper with color variants.
|
|
144
|
+
|
|
145
|
+
**Props:**
|
|
146
|
+
- `svg: string` - SVG markup string
|
|
147
|
+
- `variant?: 'default' | 'toned'` - Color variant (default: `'default'`)
|
|
148
|
+
- `size?: string` - Icon size (default: `'18px'`)
|
|
149
|
+
|
|
150
|
+
**Example:**
|
|
151
|
+
```svelte
|
|
152
|
+
<script>
|
|
153
|
+
import { BaseIcon, icons } from '@coyalabs/bts-style';
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<BaseIcon svg={icons.folder} variant="toned" size="24px" />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### Structure Components
|
|
162
|
+
|
|
163
|
+
#### TextHeader
|
|
164
|
+
|
|
165
|
+
Header component with title and optional subtitle.
|
|
166
|
+
|
|
167
|
+
**Props:**
|
|
168
|
+
- `title: string` - Main heading text
|
|
169
|
+
- `subtitle?: string` - Optional subheading
|
|
170
|
+
|
|
171
|
+
**Example:**
|
|
172
|
+
```svelte
|
|
173
|
+
<TextHeader
|
|
174
|
+
title="Welcome"
|
|
175
|
+
subtitle="Get started with BTS Theme"
|
|
176
|
+
/>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### Interactive Components
|
|
182
|
+
|
|
183
|
+
#### Button
|
|
184
|
+
|
|
185
|
+
Clickable button extending BaseContainer with icon support.
|
|
186
|
+
|
|
187
|
+
**Props:**
|
|
188
|
+
- `theme?: 'full' | 'primary' | 'secondary' | 'filled'` - Button theme
|
|
189
|
+
- `icon?: string` - Left icon SVG
|
|
190
|
+
- `actionIcon?: string | null` - Right icon SVG (default: arrow, null to hide)
|
|
191
|
+
- `iconRotation?: number` - Left icon rotation in degrees
|
|
192
|
+
- `actionIconRotation?: number` - Right icon rotation in degrees
|
|
193
|
+
- `iconSize?: string` - Left icon size (default: `'18px'`)
|
|
194
|
+
- `actionIconSize?: string` - Right icon size (default: `'18px'`)
|
|
195
|
+
- All BaseContainer corner radius props
|
|
196
|
+
|
|
197
|
+
**Events:**
|
|
198
|
+
- Forwards all events including `on:click`
|
|
199
|
+
|
|
200
|
+
**Example:**
|
|
201
|
+
```svelte
|
|
202
|
+
<script>
|
|
203
|
+
import { Button, icons } from '@coyalabs/bts-style';
|
|
204
|
+
</script>
|
|
205
|
+
|
|
206
|
+
<Button
|
|
207
|
+
theme="primary"
|
|
208
|
+
icon={icons.folder}
|
|
209
|
+
actionIconRotation={-45}
|
|
210
|
+
on:click={() => console.log('Clicked!')}
|
|
211
|
+
>
|
|
212
|
+
Open Folder
|
|
213
|
+
</Button>
|
|
214
|
+
|
|
215
|
+
<!-- Button without action icon -->
|
|
216
|
+
<Button actionIcon={null} theme="secondary">
|
|
217
|
+
Simple Button
|
|
218
|
+
</Button>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**States:**
|
|
222
|
+
- Hover: Subtle background lightening
|
|
223
|
+
- Active/Pressed: Visual feedback
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
#### IconButton
|
|
228
|
+
|
|
229
|
+
Circular icon-only button with hover effects.
|
|
230
|
+
|
|
231
|
+
**Props:**
|
|
232
|
+
- `svg: string` - Icon SVG markup
|
|
233
|
+
- `variant?: 'default' | 'toned'` - Icon color variant
|
|
234
|
+
- `size?: string` - Button size (default: `'20px'`)
|
|
235
|
+
|
|
236
|
+
**Events:**
|
|
237
|
+
- Forwards all events including `on:click`
|
|
238
|
+
|
|
239
|
+
**Example:**
|
|
240
|
+
```svelte
|
|
241
|
+
<script>
|
|
242
|
+
import { IconButton, icons } from '@coyalabs/bts-style';
|
|
243
|
+
</script>
|
|
244
|
+
|
|
245
|
+
<IconButton
|
|
246
|
+
svg={icons.cross}
|
|
247
|
+
variant="toned"
|
|
248
|
+
on:click={() => closeModal()}
|
|
249
|
+
/>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Effects:**
|
|
253
|
+
- Hover: Scale 1.1, subtle background
|
|
254
|
+
- Active: Scale 0.95
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
#### InputBox
|
|
259
|
+
|
|
260
|
+
Text input field with icon support and theme matching.
|
|
261
|
+
|
|
262
|
+
**Props:**
|
|
263
|
+
- `value?: string` - Input value (bindable)
|
|
264
|
+
- `placeholder?: string` - Placeholder text
|
|
265
|
+
- `type?: string` - Input type (default: `'text'`)
|
|
266
|
+
- `theme?: 'full' | 'primary' | 'secondary' | 'filled'` - Visual theme
|
|
267
|
+
- `icon?: string` - Left icon SVG
|
|
268
|
+
- All BaseContainer corner radius props
|
|
269
|
+
|
|
270
|
+
**Example:**
|
|
271
|
+
```svelte
|
|
272
|
+
<script>
|
|
273
|
+
import { InputBox, icons } from '@coyalabs/bts-style';
|
|
274
|
+
let username = '';
|
|
275
|
+
</script>
|
|
276
|
+
|
|
277
|
+
<InputBox
|
|
278
|
+
bind:value={username}
|
|
279
|
+
placeholder="Enter username..."
|
|
280
|
+
icon={icons.pen}
|
|
281
|
+
theme="primary"
|
|
282
|
+
/>
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
#### Toggle
|
|
289
|
+
|
|
290
|
+
Animated toggle switch with on/off states.
|
|
291
|
+
|
|
292
|
+
**Props:**
|
|
293
|
+
- `checked?: boolean` - Toggle state (bindable)
|
|
294
|
+
|
|
295
|
+
**Example:**
|
|
296
|
+
```svelte
|
|
297
|
+
<script>
|
|
298
|
+
import { Toggle } from '@coyalabs/bts-style';
|
|
299
|
+
let enabled = false;
|
|
300
|
+
</script>
|
|
301
|
+
|
|
302
|
+
<Toggle bind:checked={enabled} />
|
|
303
|
+
<p>State: {enabled ? 'On' : 'Off'}</p>
|
|
304
|
+
```
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
#### Tooltip
|
|
308
|
+
|
|
309
|
+
Hover tooltip that displays above or below an icon.
|
|
310
|
+
|
|
311
|
+
**Props:**
|
|
312
|
+
- `icon: string` - Icon SVG markup
|
|
313
|
+
- `text: string` - Tooltip text content
|
|
314
|
+
- `iconSize?: string` - Icon size (default: `'18px'`)
|
|
315
|
+
|
|
316
|
+
**Example:**
|
|
317
|
+
```svelte
|
|
318
|
+
<script>
|
|
319
|
+
import { Tooltip, icons } from '@coyalabs/bts-style';
|
|
320
|
+
</script>
|
|
321
|
+
|
|
322
|
+
<Tooltip
|
|
323
|
+
icon={icons.folder}
|
|
324
|
+
text="This is a helpful explanation"
|
|
325
|
+
iconSize="20px"
|
|
326
|
+
/>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Features:**
|
|
330
|
+
- Automatic positioning to fit on screen
|
|
331
|
+
- Min width 150px, max width 300px
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
#### TabBar
|
|
336
|
+
|
|
337
|
+
Vertical tab navigation with support for categorized groups.
|
|
338
|
+
|
|
339
|
+
**Props:**
|
|
340
|
+
- `tabs: Array<TabItem>` - Array of tabs and separators
|
|
341
|
+
- `activeTab: string` - Currently active tab ID (bindable)
|
|
342
|
+
- `onTabChange?: (tabId: string) => void` - Callback when tab changes
|
|
343
|
+
|
|
344
|
+
**TabItem Type:**
|
|
345
|
+
```typescript
|
|
346
|
+
type TabItem = {
|
|
347
|
+
id?: string; // Required for tabs, omit for separators
|
|
348
|
+
label: string; // Tab/separator text
|
|
349
|
+
icon?: string; // Optional icon SVG
|
|
350
|
+
type?: 'tab' | 'separator'; // Default: 'tab'
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Example:**
|
|
355
|
+
```svelte
|
|
356
|
+
<script>
|
|
357
|
+
import { TabBar, icons } from '@coyalabs/bts-style';
|
|
358
|
+
|
|
359
|
+
let currentTab = 'home';
|
|
360
|
+
|
|
361
|
+
const tabs = [
|
|
362
|
+
{ type: 'separator', label: 'Navigation' },
|
|
363
|
+
{ id: 'home', label: 'Home', icon: icons.folder },
|
|
364
|
+
{ id: 'about', label: 'About' },
|
|
365
|
+
{ type: 'separator', label: 'Account' },
|
|
366
|
+
{ id: 'settings', label: 'Settings' },
|
|
367
|
+
{ id: 'profile', label: 'Profile' }
|
|
368
|
+
];
|
|
369
|
+
</script>
|
|
370
|
+
|
|
371
|
+
<TabBar
|
|
372
|
+
{tabs}
|
|
373
|
+
bind:activeTab={currentTab}
|
|
374
|
+
onTabChange={(id) => console.log('Tab:', id)}
|
|
375
|
+
/>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Features:**
|
|
379
|
+
- Vertical layout, 175px fixed width
|
|
380
|
+
- Active tab uses `primary` theme, inactive uses `secondary`
|
|
381
|
+
- Adaptive corner radius: 25px at group boundaries, 18px for middle tabs
|
|
382
|
+
- Text separators for grouping tabs into categories
|
|
383
|
+
- Categories have 1rem spacing between them
|
|
384
|
+
- Buttons within categories have 4px spacing
|
|
385
|
+
|
|
386
|
+
**Corner Radius Behavior:**
|
|
387
|
+
- First tab in a group: top corners 25px
|
|
388
|
+
- Last tab in a group: bottom corners 25px
|
|
389
|
+
- Middle tabs: all corners 18px
|
|
390
|
+
- Separators create group boundaries
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
#### TreeDirectory
|
|
395
|
+
|
|
396
|
+
Expandable file/folder tree with recursive structure.
|
|
397
|
+
|
|
398
|
+
**Props:**
|
|
399
|
+
- `items: Array<TreeItem>` - Tree data structure
|
|
400
|
+
- `showCount?: boolean` - Show item counts in folders (default: `false`)
|
|
401
|
+
- `itemIcon?: string` - Custom icon for items
|
|
402
|
+
- `topFoldersExpanded?: boolean` - Expand top-level folders initially (default: `false`)
|
|
403
|
+
- `draggable?: boolean` - Enable drag and drop functionality (default: `false`)
|
|
404
|
+
- `currentPath?: string` - Current folder path for nested items (default: `''`)
|
|
405
|
+
- `level?: number` - Internal: Current nesting level (default: `0`)
|
|
406
|
+
|
|
407
|
+
**TreeItem Type:**
|
|
408
|
+
```typescript
|
|
409
|
+
type TreeItem = {
|
|
410
|
+
name: string;
|
|
411
|
+
type: 'file' | 'folder';
|
|
412
|
+
children?: TreeItem[];
|
|
413
|
+
data?: any; // Custom data attached to item
|
|
414
|
+
variant?: 'full' | 'primary' | 'secondary' | 'filled' | 'special-filled'; // Theme variant
|
|
415
|
+
suffixIcon?: string; // SVG icon displayed on the right
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Events:**
|
|
420
|
+
- `on:itemClick` - Fired when any item is clicked
|
|
421
|
+
- `event.detail.item` - The clicked item object
|
|
422
|
+
- `event.detail.type` - Either `'file'` or `'folder'`
|
|
423
|
+
- `on:dragStart` - Fired when dragging a file starts (requires `draggable={true}`)
|
|
424
|
+
- `event.detail.item` - The dragged item object
|
|
425
|
+
- `event.detail.sourcePath` - Path of the source folder
|
|
426
|
+
- `on:itemDrop` - Fired when a file is dropped (requires `draggable={true}`)
|
|
427
|
+
- `event.detail.item` - The dropped item data
|
|
428
|
+
- `event.detail.sourcePath` - Original folder path
|
|
429
|
+
- `event.detail.targetPath` - Destination folder path
|
|
430
|
+
|
|
431
|
+
**Example:**
|
|
432
|
+
```svelte
|
|
433
|
+
<script>
|
|
434
|
+
import { TreeDirectory, icons } from '@coyalabs/bts-style';
|
|
435
|
+
|
|
436
|
+
const fileTree = [
|
|
437
|
+
{
|
|
438
|
+
name: 'src',
|
|
439
|
+
type: 'folder',
|
|
440
|
+
variant: 'primary',
|
|
441
|
+
children: [
|
|
442
|
+
{
|
|
443
|
+
name: 'App.svelte',
|
|
444
|
+
type: 'file',
|
|
445
|
+
variant: 'secondary',
|
|
446
|
+
suffixIcon: icons.pen
|
|
447
|
+
},
|
|
448
|
+
{ name: 'main.js', type: 'file' }
|
|
449
|
+
]
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: 'README.md',
|
|
453
|
+
type: 'file',
|
|
454
|
+
suffixIcon: '<svg>...</svg>'
|
|
455
|
+
}
|
|
456
|
+
];
|
|
457
|
+
|
|
458
|
+
function handleItemClick(event) {
|
|
459
|
+
const { item, type } = event.detail;
|
|
460
|
+
console.log(`Clicked ${type}:`, item.name);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function handleDrop(event) {
|
|
464
|
+
const { item, sourcePath, targetPath } = event.detail;
|
|
465
|
+
console.log(`Moved ${item.name} from ${sourcePath} to ${targetPath}`);
|
|
466
|
+
}
|
|
467
|
+
</script>
|
|
468
|
+
|
|
469
|
+
<TreeDirectory
|
|
470
|
+
items={fileTree}
|
|
471
|
+
showCount={true}
|
|
472
|
+
topFoldersExpanded={true}
|
|
473
|
+
draggable={true}
|
|
474
|
+
on:itemClick={handleItemClick}
|
|
475
|
+
on:itemDrop={handleDrop}
|
|
476
|
+
/>
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Methods:**
|
|
480
|
+
```svelte
|
|
481
|
+
<script>
|
|
482
|
+
let tree;
|
|
483
|
+
|
|
484
|
+
// Get current state
|
|
485
|
+
const state = tree.getState();
|
|
486
|
+
|
|
487
|
+
// Restore state
|
|
488
|
+
tree.setState(state);
|
|
489
|
+
|
|
490
|
+
// Expand all folders
|
|
491
|
+
tree.expandAll();
|
|
492
|
+
|
|
493
|
+
// Collapse all folders
|
|
494
|
+
tree.collapseAll();
|
|
495
|
+
</script>
|
|
496
|
+
|
|
497
|
+
<TreeDirectory bind:this={tree} items={fileTree} />
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**Features:**
|
|
501
|
+
- Slide animations (150ms, skipped on initial render)
|
|
502
|
+
- Recursive item counting
|
|
503
|
+
- State persistence
|
|
504
|
+
- Event forwarding for click handling
|
|
505
|
+
- Initial expansion option for top-level folders
|
|
506
|
+
- Adaptive corner radius based on nesting
|
|
507
|
+
- Padding increases with depth
|
|
508
|
+
- Per-item theme variants (override default folder/file themes)
|
|
509
|
+
- Suffix icon support for displaying icons on the right side
|
|
510
|
+
- Optional drag and drop functionality for files
|
|
511
|
+
- Visual feedback during drag operations (folder highlight)
|
|
512
|
+
- Path tracking for nested folder structures
|
|
513
|
+
|
|
514
|
+
**Drag and Drop:**
|
|
515
|
+
|
|
516
|
+
When `draggable={true}`:
|
|
517
|
+
- Only files can be dragged (folders are drop targets)
|
|
518
|
+
- Dragged files show reduced opacity
|
|
519
|
+
- Folders highlight when dragged over
|
|
520
|
+
- Drop on folders to move files into them
|
|
521
|
+
- Drop on empty space to move to current level
|
|
522
|
+
- `on:itemDrop` event provides source and target paths for handling moves
|
|
523
|
+
|
|
524
|
+
**Customization:**
|
|
525
|
+
|
|
526
|
+
**Item Variants:**
|
|
527
|
+
Each item can have a custom `variant` to override the default theme:
|
|
528
|
+
- Folders default to `'primary'`
|
|
529
|
+
- Files default to `'secondary'`
|
|
530
|
+
- Set `variant` on any item to use a different theme
|
|
531
|
+
|
|
532
|
+
**Suffix Icons:**
|
|
533
|
+
Display additional icons on the right side of items:
|
|
534
|
+
```svelte
|
|
535
|
+
{
|
|
536
|
+
name: 'config.json',
|
|
537
|
+
type: 'file',
|
|
538
|
+
suffixIcon: '<svg>...</svg>' // Any SVG string
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
### Popup System
|
|
545
|
+
|
|
546
|
+
#### Popup
|
|
547
|
+
|
|
548
|
+
Global modal popup overlay (singleton).
|
|
549
|
+
|
|
550
|
+
**Usage:**
|
|
551
|
+
```svelte
|
|
552
|
+
<script>
|
|
553
|
+
import { Popup, popupStore } from '@coyalabs/bts-style';
|
|
554
|
+
</script>
|
|
555
|
+
|
|
556
|
+
<!-- Place once at app root -->
|
|
557
|
+
<Popup />
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Features:**
|
|
561
|
+
- Fade overlay (250ms)
|
|
562
|
+
- Fly dialog animation (300ms, backOut easing)
|
|
563
|
+
- Close button with toned icon
|
|
564
|
+
- ESC key support (built-in)
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
#### popupStore
|
|
569
|
+
|
|
570
|
+
Writable store for controlling the popup.
|
|
571
|
+
|
|
572
|
+
**Methods:**
|
|
573
|
+
|
|
574
|
+
##### open()
|
|
575
|
+
```typescript
|
|
576
|
+
popupStore.open(
|
|
577
|
+
title: string,
|
|
578
|
+
component: SvelteComponent,
|
|
579
|
+
props?: object,
|
|
580
|
+
subtitle?: string
|
|
581
|
+
)
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Opens popup with custom component.
|
|
585
|
+
|
|
586
|
+
**Example:**
|
|
587
|
+
```svelte
|
|
588
|
+
<script>
|
|
589
|
+
import { popupStore } from '@coyalabs/bts-style';
|
|
590
|
+
import MyCustomPopup from './MyCustomPopup.svelte';
|
|
591
|
+
</script>
|
|
592
|
+
|
|
593
|
+
<button on:click={() =>
|
|
594
|
+
popupStore.open(
|
|
595
|
+
'Settings',
|
|
596
|
+
MyCustomPopup,
|
|
597
|
+
{ userId: 123 },
|
|
598
|
+
'Configure your preferences'
|
|
599
|
+
)
|
|
600
|
+
}>
|
|
601
|
+
Open Settings
|
|
602
|
+
</button>
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
##### confirm()
|
|
606
|
+
```typescript
|
|
607
|
+
popupStore.confirm(
|
|
608
|
+
title: string,
|
|
609
|
+
message: string,
|
|
610
|
+
options?: {
|
|
611
|
+
onConfirm?: () => void,
|
|
612
|
+
onCancel?: () => void,
|
|
613
|
+
confirmText?: string,
|
|
614
|
+
cancelText?: string
|
|
615
|
+
}
|
|
616
|
+
)
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
Shows confirmation dialog.
|
|
620
|
+
|
|
621
|
+
**Example:**
|
|
622
|
+
```svelte
|
|
623
|
+
<button on:click={() =>
|
|
624
|
+
popupStore.confirm(
|
|
625
|
+
'Delete Item',
|
|
626
|
+
'Are you sure? This cannot be undone.',
|
|
627
|
+
{
|
|
628
|
+
onConfirm: () => deleteItem(),
|
|
629
|
+
onCancel: () => console.log('Cancelled'),
|
|
630
|
+
confirmText: 'Delete',
|
|
631
|
+
cancelText: 'Keep'
|
|
632
|
+
}
|
|
633
|
+
)
|
|
634
|
+
}>
|
|
635
|
+
Delete
|
|
636
|
+
</button>
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
##### alert()
|
|
640
|
+
```typescript
|
|
641
|
+
popupStore.alert(
|
|
642
|
+
title: string,
|
|
643
|
+
message: string,
|
|
644
|
+
options?: {
|
|
645
|
+
onOk?: () => void,
|
|
646
|
+
okText?: string
|
|
647
|
+
}
|
|
648
|
+
)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Shows alert dialog.
|
|
652
|
+
|
|
653
|
+
**Example:**
|
|
654
|
+
```svelte
|
|
655
|
+
popupStore.alert(
|
|
656
|
+
'Success',
|
|
657
|
+
'Your changes have been saved!',
|
|
658
|
+
{
|
|
659
|
+
onOk: () => navigateToHome(),
|
|
660
|
+
okText: 'Got it'
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
##### prompt()
|
|
666
|
+
```typescript
|
|
667
|
+
popupStore.prompt(
|
|
668
|
+
title: string,
|
|
669
|
+
message: string,
|
|
670
|
+
options?: {
|
|
671
|
+
onSubmit?: (value: string) => void,
|
|
672
|
+
onCancel?: () => void,
|
|
673
|
+
placeholder?: string,
|
|
674
|
+
submitText?: string,
|
|
675
|
+
cancelText?: string
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
Shows input prompt dialog.
|
|
681
|
+
|
|
682
|
+
**Example:**
|
|
683
|
+
```svelte
|
|
684
|
+
popupStore.prompt(
|
|
685
|
+
'Enter Name',
|
|
686
|
+
'Please provide your display name:',
|
|
687
|
+
{
|
|
688
|
+
onSubmit: (name) => updateProfile(name),
|
|
689
|
+
placeholder: 'Your name...',
|
|
690
|
+
submitText: 'Save',
|
|
691
|
+
cancelText: 'Skip'
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
##### close()
|
|
697
|
+
```typescript
|
|
698
|
+
popupStore.close()
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Closes the current popup.
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
#### Popup Presets
|
|
706
|
+
|
|
707
|
+
The popup system includes three pre-built popup components that can be triggered via `popupStore` methods.
|
|
708
|
+
|
|
709
|
+
##### ConfirmPopup
|
|
710
|
+
|
|
711
|
+
A two-button confirmation dialog with confirm and cancel actions.
|
|
712
|
+
|
|
713
|
+
**Features:**
|
|
714
|
+
- Primary theme confirm button (left)
|
|
715
|
+
- Secondary theme cancel button (right)
|
|
716
|
+
- Callbacks: `onConfirm`, `onCancel`
|
|
717
|
+
- Customizable button text
|
|
718
|
+
|
|
719
|
+
**Triggered via:**
|
|
720
|
+
```svelte
|
|
721
|
+
popupStore.confirm(title, message, options)
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
**Visual Layout:**
|
|
725
|
+
- Message text displayed as subtitle
|
|
726
|
+
- Two buttons side-by-side
|
|
727
|
+
- Confirm button uses primary theme
|
|
728
|
+
- Cancel button uses secondary theme
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
##### AlertPopup
|
|
733
|
+
|
|
734
|
+
A single-button alert dialog for notifications.
|
|
735
|
+
|
|
736
|
+
**Features:**
|
|
737
|
+
- Single OK button with primary theme
|
|
738
|
+
- Callback: `onOk`
|
|
739
|
+
- Customizable button text
|
|
740
|
+
- Auto-closes on OK click
|
|
741
|
+
|
|
742
|
+
**Triggered via:**
|
|
743
|
+
```svelte
|
|
744
|
+
popupStore.alert(title, message, options)
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
**Visual Layout:**
|
|
748
|
+
- Message text displayed as subtitle
|
|
749
|
+
- Single centered OK button
|
|
750
|
+
- Primary theme button
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
##### PromptPopup
|
|
755
|
+
|
|
756
|
+
An input dialog that prompts the user for text input.
|
|
757
|
+
|
|
758
|
+
**Features:**
|
|
759
|
+
- InputBox component for text entry
|
|
760
|
+
- Submit button (primary theme)
|
|
761
|
+
- Cancel button (secondary theme)
|
|
762
|
+
- Callbacks: `onSubmit(value)`, `onCancel`
|
|
763
|
+
- Customizable placeholder and button text
|
|
764
|
+
|
|
765
|
+
**Triggered via:**
|
|
766
|
+
```svelte
|
|
767
|
+
popupStore.prompt(title, message, options)
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
**Visual Layout:**
|
|
771
|
+
- Message text displayed as subtitle
|
|
772
|
+
- InputBox with customizable placeholder
|
|
773
|
+
- Two buttons: Submit (primary) and Cancel (secondary)
|
|
774
|
+
- Submit returns the input value to callback
|
|
775
|
+
|
|
776
|
+
**Example with all options:**
|
|
777
|
+
```svelte
|
|
778
|
+
popupStore.prompt(
|
|
779
|
+
'Rename File',
|
|
780
|
+
'Enter a new name for this file:',
|
|
781
|
+
{
|
|
782
|
+
placeholder: 'filename.txt',
|
|
783
|
+
submitText: 'Rename',
|
|
784
|
+
cancelText: 'Cancel',
|
|
785
|
+
onSubmit: (newName) => {
|
|
786
|
+
console.log('New name:', newName);
|
|
787
|
+
renameFile(newName);
|
|
788
|
+
},
|
|
789
|
+
onCancel: () => console.log('Rename cancelled')
|
|
790
|
+
}
|
|
791
|
+
);
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
795
|
+
|
|
796
|
+
### Toast System
|
|
797
|
+
|
|
798
|
+
#### Toast
|
|
799
|
+
|
|
800
|
+
Global toast notification container (singleton).
|
|
801
|
+
|
|
802
|
+
**Usage:**
|
|
803
|
+
```svelte
|
|
804
|
+
<script>
|
|
805
|
+
import { Toast, toastStore } from '@coyalabs/bts-style';
|
|
806
|
+
</script>
|
|
807
|
+
|
|
808
|
+
<!-- Place once at app root -->
|
|
809
|
+
<Toast />
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
**Features:**
|
|
813
|
+
- Bottom-right stacking
|
|
814
|
+
- Smooth slide animations (300ms in, 200ms out)
|
|
815
|
+
- Auto-dismiss with configurable duration
|
|
816
|
+
- Manual dismiss via close button
|
|
817
|
+
- Non-blocking, less intrusive than popups
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
#### toastStore
|
|
822
|
+
|
|
823
|
+
Writable store for controlling toast notifications.
|
|
824
|
+
|
|
825
|
+
**Methods:**
|
|
826
|
+
|
|
827
|
+
##### show()
|
|
828
|
+
```typescript
|
|
829
|
+
toastStore.show(
|
|
830
|
+
message: string,
|
|
831
|
+
duration?: number // Default: 4000ms, 0 = no auto-dismiss
|
|
832
|
+
): string // Returns toast ID
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
Shows a toast notification.
|
|
836
|
+
|
|
837
|
+
**Example:**
|
|
838
|
+
```svelte
|
|
839
|
+
<script>
|
|
840
|
+
import { toastStore } from '@coyalabs/bts-style';
|
|
841
|
+
</script>
|
|
842
|
+
|
|
843
|
+
<button on:click={() => toastStore.show('File saved successfully!')}>
|
|
844
|
+
Save
|
|
845
|
+
</button>
|
|
846
|
+
|
|
847
|
+
<!-- Custom duration -->
|
|
848
|
+
<button on:click={() => toastStore.show('Processing...', 2000)}>
|
|
849
|
+
Process
|
|
850
|
+
</button>
|
|
851
|
+
|
|
852
|
+
<!-- No auto-dismiss -->
|
|
853
|
+
<button on:click={() => toastStore.show('Important message', 0)}>
|
|
854
|
+
Important
|
|
855
|
+
</button>
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
##### dismiss()
|
|
859
|
+
```typescript
|
|
860
|
+
toastStore.dismiss(id: string)
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Dismisses a specific toast by ID.
|
|
864
|
+
|
|
865
|
+
**Example:**
|
|
866
|
+
```svelte
|
|
867
|
+
<script>
|
|
868
|
+
const toastId = toastStore.show('Click to dismiss', 0);
|
|
869
|
+
</script>
|
|
870
|
+
|
|
871
|
+
<button on:click={() => toastStore.dismiss(toastId)}>
|
|
872
|
+
Dismiss Toast
|
|
873
|
+
</button>
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
##### dismissAll()
|
|
877
|
+
```typescript
|
|
878
|
+
toastStore.dismissAll()
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
Dismisses all active toasts.
|
|
882
|
+
|
|
883
|
+
**Example:**
|
|
884
|
+
```svelte
|
|
885
|
+
<button on:click={() => toastStore.dismissAll()}>
|
|
886
|
+
Clear All Toasts
|
|
887
|
+
</button>
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
**Visual Design:**
|
|
891
|
+
- Uses Button component with secondary theme
|
|
892
|
+
- Help icon on the left (info indicator)
|
|
893
|
+
- Close icon (crossb) on the right
|
|
894
|
+
- Stacks vertically with 0.75rem gap
|
|
895
|
+
- Slides in from right, slides out to right
|
|
896
|
+
- Fixed position at bottom-right (2rem from edges)
|
|
897
|
+
- Z-index 10000 (above popups)
|
|
898
|
+
|
|
899
|
+
**Best Practices:**
|
|
900
|
+
|
|
901
|
+
Place `<Toast />` once at your app root alongside `<Popup />`:
|
|
902
|
+
|
|
903
|
+
```svelte
|
|
904
|
+
<!-- App.svelte -->
|
|
905
|
+
<script>
|
|
906
|
+
import { BasePage, Popup, Toast } from '@coyalabs/bts-style';
|
|
907
|
+
</script>
|
|
908
|
+
|
|
909
|
+
<BasePage>
|
|
910
|
+
<!-- App content -->
|
|
911
|
+
</BasePage>
|
|
912
|
+
|
|
913
|
+
<Popup />
|
|
914
|
+
<Toast />
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
### Special Components
|
|
920
|
+
|
|
921
|
+
Glowy components with unique styling and animations.
|
|
922
|
+
|
|
923
|
+
#### SpecialAction
|
|
924
|
+
|
|
925
|
+
A special-themed button with gradient background and optional tooltip.
|
|
926
|
+
|
|
927
|
+
**Props:**
|
|
928
|
+
- `label: string` - Button text
|
|
929
|
+
- `tooltipText?: string` - Optional tooltip text displayed on the right
|
|
930
|
+
|
|
931
|
+
**Example:**
|
|
932
|
+
```svelte
|
|
933
|
+
<script>
|
|
934
|
+
import { SpecialAction } from '@coyalabs/bts-style';
|
|
935
|
+
</script>
|
|
936
|
+
|
|
937
|
+
<SpecialAction
|
|
938
|
+
label="AI Generate"
|
|
939
|
+
tooltipText="Uses AI to generate content"
|
|
940
|
+
/>
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**Features:**
|
|
944
|
+
- Purple gradient background (`special-filled` theme)
|
|
945
|
+
- AI icon on the left
|
|
946
|
+
- Tooltip positioned at far right (when provided)
|
|
947
|
+
- Enhanced hover effect with brighter gradient
|
|
948
|
+
- Pressed state with darker gradient
|
|
949
|
+
|
|
950
|
+
**For?**
|
|
951
|
+
- AI actions.
|
|
952
|
+
|
|
953
|
+
**Styling:**
|
|
954
|
+
- Background: Linear gradient purple tones
|
|
955
|
+
- Enhanced glow effects on hover
|
|
956
|
+
- User-select disabled for better UX
|
|
957
|
+
|
|
958
|
+
---
|
|
959
|
+
|
|
960
|
+
#### SpecialParagraph
|
|
961
|
+
|
|
962
|
+
Animated text component that reveals words sequentially with fade and blur effects.
|
|
963
|
+
|
|
964
|
+
**Props:**
|
|
965
|
+
- `text: string` - The text content to animate
|
|
966
|
+
- `wordDelay?: number` - Delay between each word appearing in ms (default: `50`)
|
|
967
|
+
- `startDelay?: number` - Initial delay before animation starts in ms (default: `0`)
|
|
968
|
+
- `animationDuration?: number` - Animation duration for each word in ms (default: `300`)
|
|
969
|
+
- `variant?: 'title' | 'content' | 'button'` - Text styling variant (default: `'content'`)
|
|
970
|
+
- `textModifier?: string` - Font size adjustment (default: `'0px'`)
|
|
971
|
+
- `autoPlay?: boolean` - Start animation automatically on mount (default: `true`)
|
|
972
|
+
|
|
973
|
+
**Methods:**
|
|
974
|
+
- `play()` - Start/restart the animation
|
|
975
|
+
- `reset()` - Reset to hidden state
|
|
976
|
+
- `showAll()` - Show all words immediately
|
|
977
|
+
|
|
978
|
+
**Example:**
|
|
979
|
+
```svelte
|
|
980
|
+
<script>
|
|
981
|
+
import { SpecialParagraph } from '@coyalabs/bts-style';
|
|
982
|
+
let paragraph;
|
|
983
|
+
</script>
|
|
984
|
+
|
|
985
|
+
<!-- Auto-playing paragraph -->
|
|
986
|
+
<SpecialParagraph
|
|
987
|
+
text="This text will animate in word by word with smooth effects"
|
|
988
|
+
wordDelay={80}
|
|
989
|
+
variant="content"
|
|
990
|
+
/>
|
|
991
|
+
|
|
992
|
+
<!-- Manual control -->
|
|
993
|
+
<SpecialParagraph
|
|
994
|
+
bind:this={paragraph}
|
|
995
|
+
text="Click the button to animate!"
|
|
996
|
+
autoPlay={false}
|
|
997
|
+
/>
|
|
998
|
+
<button on:click={() => paragraph.play()}>Play Animation</button>
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
**Animation Effects:**
|
|
1002
|
+
- Each word fades in from opacity 0 to 1
|
|
1003
|
+
- Slides up from 8px translateY offset
|
|
1004
|
+
- Blur transitions from 4px to 0
|
|
1005
|
+
- Smooth easing transitions
|
|
1006
|
+
|
|
1007
|
+
**For?**
|
|
1008
|
+
- Fancier AI.
|
|
1009
|
+
|
|
1010
|
+
**Use Cases:**
|
|
1011
|
+
- Intro text animations
|
|
1012
|
+
- Loading state messages
|
|
1013
|
+
- Drawing attention to important content
|
|
1014
|
+
- Storytelling and narrative interfaces
|
|
1015
|
+
|
|
1016
|
+
---
|
|
1017
|
+
|
|
1018
|
+
#### ContextMenu
|
|
1019
|
+
|
|
1020
|
+
Categorized menu component with separator support, similar to TabBar layout.
|
|
1021
|
+
|
|
1022
|
+
**Props:**
|
|
1023
|
+
- `items: Array<ContextMenuItem>` - Array of menu items and separators
|
|
1024
|
+
- `selectedValue?: any` - Currently selected item value
|
|
1025
|
+
- `onSelect?: (value: any) => void` - Callback when item is selected
|
|
1026
|
+
|
|
1027
|
+
**ContextMenuItem Type:**
|
|
1028
|
+
```typescript
|
|
1029
|
+
type ContextMenuItem = {
|
|
1030
|
+
label: string;
|
|
1031
|
+
value?: any; // Required for items, omit for separators
|
|
1032
|
+
disabled?: boolean;
|
|
1033
|
+
type?: 'item' | 'separator'; // Default: 'item'
|
|
1034
|
+
}
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
**Example:**
|
|
1038
|
+
```svelte
|
|
1039
|
+
<script>
|
|
1040
|
+
import { ContextMenu } from '@coyalabs/bts-style';
|
|
1041
|
+
|
|
1042
|
+
let selectedValue = 'cut';
|
|
1043
|
+
|
|
1044
|
+
const menuItems = [
|
|
1045
|
+
{ label: 'Edit', type: 'separator' },
|
|
1046
|
+
{ label: 'Cut', value: 'cut' },
|
|
1047
|
+
{ label: 'Copy', value: 'copy' },
|
|
1048
|
+
{ label: 'Paste', value: 'paste', disabled: true },
|
|
1049
|
+
{ label: 'View', type: 'separator' },
|
|
1050
|
+
{ label: 'Zoom In', value: 'zoomIn' },
|
|
1051
|
+
{ label: 'Zoom Out', value: 'zoomOut' }
|
|
1052
|
+
];
|
|
1053
|
+
</script>
|
|
1054
|
+
|
|
1055
|
+
<ContextMenu
|
|
1056
|
+
items={menuItems}
|
|
1057
|
+
{selectedValue}
|
|
1058
|
+
onSelect={(val) => handleAction(val)}
|
|
1059
|
+
/>
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**Features:**
|
|
1063
|
+
- Category grouping with separator labels
|
|
1064
|
+
- Selected item highlighting
|
|
1065
|
+
- Disabled item support with reduced opacity
|
|
1066
|
+
- Hover effects on enabled items
|
|
1067
|
+
- Filled theme container
|
|
1068
|
+
- Automatic category spacing and borders
|
|
1069
|
+
- Text ellipsis for long labels
|
|
1070
|
+
|
|
1071
|
+
**Visual Layout:**
|
|
1072
|
+
- Categories separated by labeled dividers
|
|
1073
|
+
- First category has no top border
|
|
1074
|
+
- Subsequent categories have subtle top border
|
|
1075
|
+
- 0.5rem padding around separators
|
|
1076
|
+
- 4px spacing between items
|
|
1077
|
+
|
|
1078
|
+
---
|
|
1079
|
+
|
|
1080
|
+
#### Dropdown
|
|
1081
|
+
|
|
1082
|
+
Select dropdown with collapsible options menu.
|
|
1083
|
+
|
|
1084
|
+
**Props:**
|
|
1085
|
+
- `label: string` - Default button text before selection
|
|
1086
|
+
- `icon?: string` - Optional left icon SVG
|
|
1087
|
+
- `theme?: 'full' | 'primary' | 'secondary'` - Button theme (default: `'full'`)
|
|
1088
|
+
- `width?: string` - Fixed width for dropdown (default: `'200px'`)
|
|
1089
|
+
- `options: Array<DropdownOption>` - Array of selectable options
|
|
1090
|
+
- `value?: any` - Currently selected value (bindable)
|
|
1091
|
+
- `onChange?: (value: any) => void` - Callback when selection changes
|
|
1092
|
+
- All BaseContainer corner radius props
|
|
1093
|
+
|
|
1094
|
+
**DropdownOption Type:**
|
|
1095
|
+
```typescript
|
|
1096
|
+
type DropdownOption = {
|
|
1097
|
+
label: string;
|
|
1098
|
+
value: any;
|
|
1099
|
+
disabled?: boolean;
|
|
1100
|
+
type?: 'item' | 'separator'; // Optional: use for category separation
|
|
1101
|
+
}
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
**Example:**
|
|
1105
|
+
```svelte
|
|
1106
|
+
<script>
|
|
1107
|
+
import { Dropdown, icons } from '@coyalabs/bts-style';
|
|
1108
|
+
|
|
1109
|
+
let selectedValue = 'option1';
|
|
1110
|
+
|
|
1111
|
+
const options = [
|
|
1112
|
+
{ label: 'Basic', type: 'separator' },
|
|
1113
|
+
{ label: 'Option 1', value: 'option1' },
|
|
1114
|
+
{ label: 'Option 2', value: 'option2' },
|
|
1115
|
+
{ label: 'Advanced', type: 'separator' },
|
|
1116
|
+
{ label: 'Option 3', value: 'option3' },
|
|
1117
|
+
{ label: 'Disabled', value: 'option4', disabled: true }
|
|
1118
|
+
];
|
|
1119
|
+
</script>
|
|
1120
|
+
|
|
1121
|
+
<Dropdown
|
|
1122
|
+
label="Select an option"
|
|
1123
|
+
icon={icons.folder}
|
|
1124
|
+
theme="primary"
|
|
1125
|
+
width="250px"
|
|
1126
|
+
{options}
|
|
1127
|
+
bind:value={selectedValue}
|
|
1128
|
+
onChange={(val) => console.log('Selected:', val)}
|
|
1129
|
+
/>
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
**Features:**
|
|
1133
|
+
- Fixed width with text truncation (ellipsis)
|
|
1134
|
+
- Expand icon rotates 180° when open
|
|
1135
|
+
- Slide animation for menu (150ms)
|
|
1136
|
+
- Click outside to close
|
|
1137
|
+
- Uses ContextMenu component internally
|
|
1138
|
+
- Support for category separators
|
|
1139
|
+
- Selected item highlighted
|
|
1140
|
+
- Disabled items shown with reduced opacity
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
|
|
1144
|
+
#### LinearList
|
|
1145
|
+
|
|
1146
|
+
Vertical list component with customizable actions for each item.
|
|
1147
|
+
|
|
1148
|
+
**Props:**
|
|
1149
|
+
- `items: Array<ListItem>` - Array of list items
|
|
1150
|
+
|
|
1151
|
+
**ListItem Type:**
|
|
1152
|
+
```typescript
|
|
1153
|
+
type CustomAction = {
|
|
1154
|
+
label: string;
|
|
1155
|
+
actionIcon?: string;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
type ListItem = {
|
|
1159
|
+
data?: any; // Custom data attached to item
|
|
1160
|
+
customActions?: CustomAction[];
|
|
1161
|
+
removeButton?: boolean;
|
|
1162
|
+
}
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
**Events:**
|
|
1166
|
+
- `on:action` - Fired when any custom action is clicked
|
|
1167
|
+
- `event.detail.index` - Item index
|
|
1168
|
+
- `event.detail.actionLabel` - Action label that was clicked
|
|
1169
|
+
- `event.detail.item` - The item object
|
|
1170
|
+
- `on:remove` - Fired when remove button is clicked
|
|
1171
|
+
- `event.detail.index` - Item index
|
|
1172
|
+
- `event.detail.item` - The item object
|
|
1173
|
+
|
|
1174
|
+
**Example:**
|
|
1175
|
+
```svelte
|
|
1176
|
+
<script>
|
|
1177
|
+
import { LinearList, icons } from '@coyalabs/bts-style';
|
|
1178
|
+
|
|
1179
|
+
const items = [
|
|
1180
|
+
{
|
|
1181
|
+
data: { id: 1, name: 'First Item' },
|
|
1182
|
+
customActions: [
|
|
1183
|
+
{ label: 'Edit', actionIcon: icons.pen },
|
|
1184
|
+
{ label: 'View' }
|
|
1185
|
+
],
|
|
1186
|
+
removeButton: true
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
data: { id: 2, name: 'Second Item' },
|
|
1190
|
+
customActions: [
|
|
1191
|
+
{ label: 'Download' }
|
|
1192
|
+
],
|
|
1193
|
+
removeButton: true
|
|
1194
|
+
}
|
|
1195
|
+
];
|
|
1196
|
+
|
|
1197
|
+
function handleAction(event) {
|
|
1198
|
+
const { index, actionLabel, item } = event.detail;
|
|
1199
|
+
console.log(`${actionLabel} clicked on item ${index}`, item);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
function handleRemove(event) {
|
|
1203
|
+
const { index, item } = event.detail;
|
|
1204
|
+
console.log(`Remove item ${index}`, item);
|
|
1205
|
+
}
|
|
1206
|
+
</script>
|
|
1207
|
+
|
|
1208
|
+
<LinearList
|
|
1209
|
+
{items}
|
|
1210
|
+
on:action={handleAction}
|
|
1211
|
+
on:remove={handleRemove}
|
|
1212
|
+
>
|
|
1213
|
+
{#snippet children({ item, index })}
|
|
1214
|
+
<div>{item.data.name}</div>
|
|
1215
|
+
{/snippet}
|
|
1216
|
+
</LinearList>
|
|
1217
|
+
```
|
|
1218
|
+
|
|
1219
|
+
**Features:**
|
|
1220
|
+
- Left-aligned content slot with `item` and `index` props
|
|
1221
|
+
- Right-aligned action buttons (horizontal)
|
|
1222
|
+
- Optional remove button (icon-only, toned variant)
|
|
1223
|
+
- 1px bottom border separator (rgba(161, 143, 143, 0.24))
|
|
1224
|
+
- No border on last item
|
|
1225
|
+
- 10px vertical padding per item
|
|
1226
|
+
- No horizontal padding
|
|
1227
|
+
- No spacing between items
|
|
1228
|
+
- Event-based action handling
|
|
1229
|
+
|
|
1230
|
+
**Visual Layout:**
|
|
1231
|
+
- Each item is a flex row with space-between
|
|
1232
|
+
- Content on the left, actions on the right
|
|
1233
|
+
- Actions have 0.5rem gap between them
|
|
1234
|
+
- Remove button appears at the end of actions
|
|
1235
|
+
- Borders are internal strokes (bottom edge only)
|
|
1236
|
+
|
|
1237
|
+
---
|
|
1238
|
+
|
|
1239
|
+
#### Separator
|
|
1240
|
+
|
|
1241
|
+
Decorative SVG separator with tiled middle section.
|
|
1242
|
+
|
|
1243
|
+
**Props:**
|
|
1244
|
+
- `height?: string` - Separator height (default: `'12px'`)
|
|
1245
|
+
- `width?: string` - Separator width (default: `'100%'`)
|
|
1246
|
+
- `margin?: string` - CSS margin (default: `'2rem 0'`)
|
|
1247
|
+
|
|
1248
|
+
**Example:**
|
|
1249
|
+
```svelte
|
|
1250
|
+
<script>
|
|
1251
|
+
import { Separator } from '@coyalabs/bts-style';
|
|
1252
|
+
</script>
|
|
1253
|
+
|
|
1254
|
+
<Separator />
|
|
1255
|
+
<Separator height="20px" margin="3rem 0" />
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
**Features:**
|
|
1259
|
+
- Three-part design: left piece, tiled middle, right piece
|
|
1260
|
+
- Inline SVG data URLs for performance
|
|
1261
|
+
- Scales to container width
|
|
1262
|
+
- Elegant visual break between sections
|
|
1263
|
+
|
|
1264
|
+
---
|
|
1265
|
+
|
|
1266
|
+
### Icons
|
|
1267
|
+
|
|
1268
|
+
The package exports a collection of built-in SVG icons.
|
|
1269
|
+
|
|
1270
|
+
**Usage:**
|
|
1271
|
+
```svelte
|
|
1272
|
+
<script>
|
|
1273
|
+
import { icons, BaseIcon } from '@coyalabs/bts-style';
|
|
1274
|
+
</script>
|
|
1275
|
+
|
|
1276
|
+
<BaseIcon svg={icons.arrow} />
|
|
1277
|
+
<BaseIcon svg={icons.folder} />
|
|
1278
|
+
<BaseIcon svg={icons.icon_expand} />
|
|
1279
|
+
<BaseIcon svg={icons.cross} />
|
|
1280
|
+
<BaseIcon svg={icons.pen} />
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
**Available Icons:**
|
|
1284
|
+
- `arrow` - Right arrow navigation
|
|
1285
|
+
- `folder` - Folder icon
|
|
1286
|
+
- `icon_expand` - Expand/collapse chevron
|
|
1287
|
+
- `cross` - Close/dismiss X
|
|
1288
|
+
- `pen` - Edit/write pen
|
|
1289
|
+
|
|
1290
|
+
**Custom Icons:**
|
|
1291
|
+
|
|
1292
|
+
You can use any SVG string with icon-supporting components:
|
|
1293
|
+
|
|
1294
|
+
```svelte
|
|
1295
|
+
<script>
|
|
1296
|
+
const myIcon = '<svg>...</svg>';
|
|
1297
|
+
</script>
|
|
1298
|
+
|
|
1299
|
+
<Button icon={myIcon}>Custom Icon</Button>
|
|
1300
|
+
<BaseIcon svg={myIcon} />
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
---
|
|
1304
|
+
|
|
1305
|
+
## Styling
|
|
1306
|
+
|
|
1307
|
+
### Corner Radius Customization
|
|
1308
|
+
|
|
1309
|
+
All components extending BaseContainer support individual corner radius props:
|
|
1310
|
+
|
|
1311
|
+
```svelte
|
|
1312
|
+
<Button
|
|
1313
|
+
borderRadiusTopLeft="35px"
|
|
1314
|
+
borderRadiusTopRight="35px"
|
|
1315
|
+
borderRadiusBottomLeft="15px"
|
|
1316
|
+
borderRadiusBottomRight="15px"
|
|
1317
|
+
theme="primary"
|
|
1318
|
+
>
|
|
1319
|
+
Asymmetric Button
|
|
1320
|
+
</Button>
|
|
1321
|
+
```
|
|
1322
|
+
|
|
1323
|
+
### Theme Customization
|
|
1324
|
+
|
|
1325
|
+
You can customize filled theme backgrounds by targeting CSS variables or extending components.
|
|
1326
|
+
|
|
1327
|
+
---
|
|
1328
|
+
|
|
1329
|
+
## Best Practices
|
|
1330
|
+
|
|
1331
|
+
### Layout
|
|
1332
|
+
|
|
1333
|
+
```svelte
|
|
1334
|
+
<BasePage>
|
|
1335
|
+
<main style="max-width: 900px; margin: 0 auto; padding: 3rem 2rem;">
|
|
1336
|
+
<TextHeader title="My App" subtitle="Welcome" />
|
|
1337
|
+
|
|
1338
|
+
<!-- Your content -->
|
|
1339
|
+
</main>
|
|
1340
|
+
</BasePage>
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
### Popup Management
|
|
1344
|
+
|
|
1345
|
+
Place `<Popup />` once at your app root:
|
|
1346
|
+
|
|
1347
|
+
```svelte
|
|
1348
|
+
<!-- App.svelte -->
|
|
1349
|
+
<script>
|
|
1350
|
+
import { BasePage, Popup } from '@coyalabs/bts-style';
|
|
1351
|
+
</script>
|
|
1352
|
+
|
|
1353
|
+
<BasePage>
|
|
1354
|
+
<!-- App content -->
|
|
1355
|
+
</BasePage>
|
|
1356
|
+
|
|
1357
|
+
<Popup />
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
### Icons
|
|
1361
|
+
|
|
1362
|
+
For consistent styling, prefer using `BaseIcon` over raw SVG:
|
|
1363
|
+
|
|
1364
|
+
```svelte
|
|
1365
|
+
<!-- Good -->
|
|
1366
|
+
<BaseIcon svg={myIcon} variant="toned" />
|
|
1367
|
+
|
|
1368
|
+
<!-- Less ideal -->
|
|
1369
|
+
{@html myIcon}
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
---
|
|
1373
|
+
|
|
1374
|
+
## Development
|
|
1375
|
+
|
|
1376
|
+
### Local Development
|
|
1377
|
+
|
|
1378
|
+
Link the package locally for testing:
|
|
1379
|
+
|
|
1380
|
+
```bash
|
|
1381
|
+
# In the package directory
|
|
1382
|
+
cd @bts-theme/bts-theme
|
|
1383
|
+
npm run package
|
|
1384
|
+
npm link
|
|
1385
|
+
|
|
1386
|
+
# In your project
|
|
1387
|
+
npm link @coyalabs/bts-style
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
After making changes:
|
|
1391
|
+
|
|
1392
|
+
```bash
|
|
1393
|
+
npm run package
|
|
1394
|
+
```
|
|
1395
|
+
|
|
1396
|
+
**Note:** You may need to clear Vite cache after rebuilding:
|
|
1397
|
+
```bash
|
|
1398
|
+
rm -rf node_modules/.vite
|
|
1399
|
+
```
|
|
1400
|
+
|
|
1401
|
+
### Publishing
|
|
1402
|
+
|
|
1403
|
+
```bash
|
|
1404
|
+
npm run release # Bumps version, packages, and publishes
|
|
1405
|
+
```
|
|
1406
|
+
|
|
1407
|
+
---
|
|
1408
|
+
|
|
1409
|
+
## TypeScript Support
|
|
1410
|
+
|
|
1411
|
+
All components include TypeScript definitions. Import types as needed:
|
|
1412
|
+
|
|
1413
|
+
```typescript
|
|
1414
|
+
import type { TreeItem } from '@coyalabs/bts-style';
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
---
|
|
1418
|
+
|
|
1419
|
+
## Package Structure
|
|
1420
|
+
|
|
1421
|
+
```
|
|
1422
|
+
@coyalabs/bts-style/
|
|
1423
|
+
├── dist/ # Compiled package
|
|
1424
|
+
├── public/ # Static assets
|
|
1425
|
+
│ ├── favicon.svg # Default BTS favicon
|
|
1426
|
+
│ └── PLACE_YOUR_IMAGES_HERE.txt
|
|
1427
|
+
├── src/
|
|
1428
|
+
│ ├── Base/ # Base components
|
|
1429
|
+
│ ├── Components/ # Interactive components
|
|
1430
|
+
│ ├── Structure/ # Layout components
|
|
1431
|
+
│ ├── icons.js # Icon definitions
|
|
1432
|
+
│ └── index.ts # Main export
|
|
1433
|
+
└── package.json
|
|
1434
|
+
```
|
|
1435
|
+
|
|
1436
|
+
---
|
|
1437
|
+
|
|
1438
|
+
## License
|
|
1439
|
+
|
|
1440
|
+
MIT
|
|
1441
|
+
|
|
1442
|
+
---
|
|
1443
|
+
|
|
1444
|
+
## Repository
|
|
1445
|
+
|
|
1446
|
+
[github.com/sparklescoya/svelte-bts-theme](https://github.com/sparklescoya/svelte-bts-theme)
|
|
1447
|
+
|
|
1448
|
+
---
|
|
1449
|
+
|
|
1450
|
+
## Credits
|
|
1451
|
+
|
|
1452
|
+
Created with ❤️ using Svelte 5
|
|
1453
|
+
|
|
1454
|
+
Fonts: [Google Fonts (Noto Serif KR)](https://fonts.google.com/), [Fontshare (Satoshi)](https://www.fontshare.com/)
|