@ainsleydev/sveltekit-helper 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -1
- package/dist/components/Hamburger.svelte +62 -0
- package/dist/components/Hamburger.svelte.d.ts +27 -0
- package/dist/components/Hamburger.svelte.d.ts.map +1 -0
- package/dist/components/Sidebar.svelte +274 -0
- package/dist/components/Sidebar.svelte.d.ts +41 -0
- package/dist/components/Sidebar.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ pnpm add @ainsleydev/sveltekit-helper
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- **Grid System**: Responsive Container, Row, and Column components with CSS variables
|
|
14
|
+
- **Navigation Components**: Mobile-first Sidebar and Hamburger menu components
|
|
14
15
|
- **Form Utilities**: Schema generation and error helpers for Zod validation
|
|
15
16
|
- **Payload CMS Integration**: Ready-to-use components for Payload CMS forms and media
|
|
16
17
|
- **SCSS with BEM**: All components use SCSS with BEM naming convention
|
|
@@ -29,7 +30,7 @@ All Grid components use CSS variables with fallback values, allowing flexible cu
|
|
|
29
30
|
|
|
30
31
|
**Responsive Variables:**
|
|
31
32
|
|
|
32
|
-
Row and Column components include mobile-specific overrides (< 568px). You can
|
|
33
|
+
Row and Column components include mobile-specific overrides (< 568px). You can customise responsive behaviour using:
|
|
33
34
|
|
|
34
35
|
```css
|
|
35
36
|
:root {
|
|
@@ -157,6 +158,128 @@ Base column component with customisable gap. Consumers should define their own g
|
|
|
157
158
|
}
|
|
158
159
|
```
|
|
159
160
|
|
|
161
|
+
## Navigation Components
|
|
162
|
+
|
|
163
|
+
### Sidebar
|
|
164
|
+
|
|
165
|
+
Mobile-first sidebar navigation component with toggle and hamburger display modes. Automatically collapses on mobile and remains visible on desktop.
|
|
166
|
+
|
|
167
|
+
```svelte
|
|
168
|
+
<script>
|
|
169
|
+
import { Sidebar } from '@ainsleydev/sveltekit-helper/components'
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<Sidebar bind:isOpen>
|
|
173
|
+
<nav>
|
|
174
|
+
<a href="/">Home</a>
|
|
175
|
+
<a href="/about">About</a>
|
|
176
|
+
<a href="/contact">Contact</a>
|
|
177
|
+
</nav>
|
|
178
|
+
</Sidebar>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Props
|
|
182
|
+
|
|
183
|
+
- `menuLabel?: string` - Label for toggle button (default: 'Menu')
|
|
184
|
+
- `isOpen?: boolean` - Bindable open/closed state
|
|
185
|
+
- `position?: 'left' | 'right'` - Sidebar position (default: 'left')
|
|
186
|
+
- `width?: string` - Sidebar width on mobile (default: '50vw')
|
|
187
|
+
- `top?: number` - Sticky position offset on desktop (default: 160)
|
|
188
|
+
- `closeOnOverlayClick?: boolean` - Close when overlay is clicked (default: true)
|
|
189
|
+
- `overlayOpacity?: number` - Overlay opacity when open (default: 0.3)
|
|
190
|
+
- `toggleStyle?: 'toggle' | 'hamburger'` - Toggle display mode (default: 'toggle')
|
|
191
|
+
- `class?: string` - Additional CSS classes
|
|
192
|
+
- `onOpen?: () => void` - Callback when sidebar opens
|
|
193
|
+
- `onClose?: () => void` - Callback when sidebar closes
|
|
194
|
+
- `onToggle?: (isOpen: boolean) => void` - Callback when sidebar toggles
|
|
195
|
+
|
|
196
|
+
#### Examples
|
|
197
|
+
|
|
198
|
+
With hamburger menu:
|
|
199
|
+
|
|
200
|
+
```svelte
|
|
201
|
+
<Sidebar toggleStyle="hamburger" bind:isOpen>
|
|
202
|
+
<nav>...</nav>
|
|
203
|
+
</Sidebar>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Right-side with custom width:
|
|
207
|
+
|
|
208
|
+
```svelte
|
|
209
|
+
<Sidebar position="right" width="300px">
|
|
210
|
+
<nav>...</nav>
|
|
211
|
+
</Sidebar>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### Customisation
|
|
215
|
+
|
|
216
|
+
Override CSS variables globally from `:root`:
|
|
217
|
+
|
|
218
|
+
```css
|
|
219
|
+
:root {
|
|
220
|
+
--sidebar-width: 400px;
|
|
221
|
+
--sidebar-min-width: 300px;
|
|
222
|
+
--sidebar-background: #1a1a1a;
|
|
223
|
+
--sidebar-border-colour: rgba(255, 255, 255, 0.2);
|
|
224
|
+
--sidebar-overlay-colour: #000;
|
|
225
|
+
--sidebar-overlay-opacity: 0.5;
|
|
226
|
+
|
|
227
|
+
/* Toggle button */
|
|
228
|
+
--sidebar-toggle-background: #2a2a2a;
|
|
229
|
+
--sidebar-toggle-colour: #fff;
|
|
230
|
+
--sidebar-toggle-padding: 0.5rem 1.5rem;
|
|
231
|
+
--sidebar-toggle-radius: 8px;
|
|
232
|
+
--sidebar-toggle-font-size: 1rem;
|
|
233
|
+
|
|
234
|
+
/* Inner spacing */
|
|
235
|
+
--sidebar-inner-padding: 2rem 2rem 0 2rem;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Or use inline styles:
|
|
240
|
+
|
|
241
|
+
```svelte
|
|
242
|
+
<Sidebar style="--sidebar-background: #2a2a2a; --sidebar-width: 400px">
|
|
243
|
+
<nav>...</nav>
|
|
244
|
+
</Sidebar>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Hamburger
|
|
248
|
+
|
|
249
|
+
Hamburger menu icon with animation for mobile navigation. Uses `svelte-hamburgers` under the hood.
|
|
250
|
+
|
|
251
|
+
```svelte
|
|
252
|
+
<script>
|
|
253
|
+
import { Hamburger } from '@ainsleydev/sveltekit-helper/components'
|
|
254
|
+
|
|
255
|
+
let isOpen = $state(false)
|
|
256
|
+
</script>
|
|
257
|
+
|
|
258
|
+
<Hamburger bind:isOpen />
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### Props
|
|
262
|
+
|
|
263
|
+
- `isOpen?: boolean` - Bindable open/closed state
|
|
264
|
+
- `gap?: string` - Distance from top/right edges (default: '0.8rem')
|
|
265
|
+
- `class?: string` - Additional CSS classes
|
|
266
|
+
- `ariaLabel?: string` - Accessibility label (default: 'Toggle menu')
|
|
267
|
+
- `onChange?: (isOpen: boolean) => void` - Callback when state changes
|
|
268
|
+
|
|
269
|
+
#### Customisation
|
|
270
|
+
|
|
271
|
+
```css
|
|
272
|
+
:root {
|
|
273
|
+
--hamburger-gap: 1rem;
|
|
274
|
+
--hamburger-z-index: 10000;
|
|
275
|
+
--hamburger-colour: #fff;
|
|
276
|
+
--hamburger-layer-width: 28px;
|
|
277
|
+
--hamburger-layer-height: 3px;
|
|
278
|
+
--hamburger-layer-spacing: 6px;
|
|
279
|
+
--hamburger-border-radius: 3px;
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
160
283
|
## Form Utilities
|
|
161
284
|
|
|
162
285
|
### generateFormSchema
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { HamburgerType as HamburgerTypeLib } from 'svelte-hamburgers';
|
|
3
|
+
|
|
4
|
+
export type HamburgerType = HamburgerTypeLib;
|
|
5
|
+
|
|
6
|
+
export type HamburgerProps = {
|
|
7
|
+
// See: https://github.com/ghostdevv/svelte-hamburgers/blob/main/types.md
|
|
8
|
+
style?: HamburgerType;
|
|
9
|
+
isOpen?: boolean;
|
|
10
|
+
gap?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
onChange?: (isOpen: boolean) => void;
|
|
13
|
+
};
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<script lang="ts">
|
|
17
|
+
import { Hamburger as SvelteHamburger } from 'svelte-hamburgers';
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
style = 'spin',
|
|
21
|
+
isOpen = $bindable(false),
|
|
22
|
+
gap = '0.8rem',
|
|
23
|
+
class: className = '',
|
|
24
|
+
onChange
|
|
25
|
+
}: HamburgerProps = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<!--
|
|
29
|
+
@component
|
|
30
|
+
|
|
31
|
+
Hamburger menu icon with animation for mobile navigation.
|
|
32
|
+
Uses svelte-hamburgers under the hood.
|
|
33
|
+
|
|
34
|
+
@example
|
|
35
|
+
```svelte
|
|
36
|
+
<Hamburger bind:isOpen />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
@example
|
|
40
|
+
```svelte
|
|
41
|
+
<Hamburger gap="1rem">
|
|
42
|
+
```
|
|
43
|
+
-->
|
|
44
|
+
<div class="hamburger-wrapper {className}" style="--hamburger-gap: {gap}" aria-label="Toggle Menu">
|
|
45
|
+
<SvelteHamburger
|
|
46
|
+
type={style}
|
|
47
|
+
bind:open={isOpen}
|
|
48
|
+
on:change={() => onChange?.(isOpen)}
|
|
49
|
+
--color="var(--hamburger-colour, var(--colour-base-light))"
|
|
50
|
+
--layer-width="var(--hamburger-layer-width, 24px)"
|
|
51
|
+
--layer-height="var(--hamburger-layer-height, 2px)"
|
|
52
|
+
--layer-spacing="var(--hamburger-layer-spacing, 5px)"
|
|
53
|
+
--border-radius="var(--hamburger-border-radius, 2px)"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<style>.hamburger-wrapper {
|
|
58
|
+
position: fixed;
|
|
59
|
+
top: var(--hamburger-gap, 0.8rem);
|
|
60
|
+
right: var(--hamburger-gap, 0.8rem);
|
|
61
|
+
z-index: var(--hamburger-z-index, 10000);
|
|
62
|
+
}</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { HamburgerType as HamburgerTypeLib } from 'svelte-hamburgers';
|
|
2
|
+
export type HamburgerType = HamburgerTypeLib;
|
|
3
|
+
export type HamburgerProps = {
|
|
4
|
+
style?: HamburgerType;
|
|
5
|
+
isOpen?: boolean;
|
|
6
|
+
gap?: string;
|
|
7
|
+
class?: string;
|
|
8
|
+
onChange?: (isOpen: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Hamburger menu icon with animation for mobile navigation.
|
|
12
|
+
* Uses svelte-hamburgers under the hood.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```svelte
|
|
16
|
+
* <Hamburger bind:isOpen />
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```svelte
|
|
21
|
+
* <Hamburger gap="1rem">
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare const Hamburger: import("svelte").Component<HamburgerProps, {}, "isOpen">;
|
|
25
|
+
type Hamburger = ReturnType<typeof Hamburger>;
|
|
26
|
+
export default Hamburger;
|
|
27
|
+
//# sourceMappingURL=Hamburger.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Hamburger.svelte.d.ts","sourceRoot":"","sources":["../../src/components/Hamburger.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG;IAE5B,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,CAAC;AA4BF;;;;;;;;;;;;;GAaG;AACH,QAAA,MAAM,SAAS,0DAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export type SidebarProps = {
|
|
5
|
+
menuLabel?: string;
|
|
6
|
+
children: Snippet;
|
|
7
|
+
isOpen?: boolean;
|
|
8
|
+
position?: 'left' | 'right';
|
|
9
|
+
width?: string;
|
|
10
|
+
top?: number;
|
|
11
|
+
closeOnOverlayClick?: boolean;
|
|
12
|
+
overlayOpacity?: number;
|
|
13
|
+
toggleStyle?: 'toggle' | 'hamburger';
|
|
14
|
+
class?: string;
|
|
15
|
+
onOpen?: () => void;
|
|
16
|
+
onClose?: () => void;
|
|
17
|
+
onToggle?: (isOpen: boolean) => void;
|
|
18
|
+
};
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<script lang="ts">
|
|
22
|
+
import { onMount } from 'svelte';
|
|
23
|
+
import Hamburger from './Hamburger.svelte';
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
menuLabel = 'Menu',
|
|
27
|
+
children,
|
|
28
|
+
isOpen = $bindable(false),
|
|
29
|
+
position = 'left',
|
|
30
|
+
width = '50vw',
|
|
31
|
+
top = 160,
|
|
32
|
+
closeOnOverlayClick = true,
|
|
33
|
+
overlayOpacity = 0.3,
|
|
34
|
+
toggleStyle = 'toggle',
|
|
35
|
+
class: className = '',
|
|
36
|
+
onOpen,
|
|
37
|
+
onClose,
|
|
38
|
+
onToggle
|
|
39
|
+
}: SidebarProps = $props();
|
|
40
|
+
|
|
41
|
+
const uniqueId = `sidebar-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
42
|
+
let checkboxRef = $state<HTMLInputElement>();
|
|
43
|
+
let overlayRef = $state<HTMLLabelElement>();
|
|
44
|
+
let contentRef = $state<HTMLDivElement>();
|
|
45
|
+
let previousActiveElement = $state<HTMLElement>();
|
|
46
|
+
|
|
47
|
+
// Sync checkbox with isOpen state.
|
|
48
|
+
$effect(() => {
|
|
49
|
+
if (checkboxRef && checkboxRef.checked !== isOpen) {
|
|
50
|
+
checkboxRef.checked = isOpen;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Watch for changes to isOpen and call callbacks.
|
|
55
|
+
$effect(() => {
|
|
56
|
+
if (isOpen) {
|
|
57
|
+
onOpen?.();
|
|
58
|
+
} else {
|
|
59
|
+
onClose?.();
|
|
60
|
+
}
|
|
61
|
+
onToggle?.(isOpen);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Focus management.
|
|
65
|
+
$effect(() => {
|
|
66
|
+
if (isOpen && contentRef) {
|
|
67
|
+
previousActiveElement = document.activeElement as HTMLElement;
|
|
68
|
+
const firstFocusable = contentRef.querySelector<HTMLElement>(
|
|
69
|
+
'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
|
|
70
|
+
);
|
|
71
|
+
firstFocusable?.focus();
|
|
72
|
+
} else if (!isOpen && previousActiveElement) {
|
|
73
|
+
previousActiveElement.focus();
|
|
74
|
+
previousActiveElement = undefined;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
onMount(() => {
|
|
79
|
+
// Capture refs in local variables to ensure proper cleanup.
|
|
80
|
+
const overlay = overlayRef;
|
|
81
|
+
const checkbox = checkboxRef;
|
|
82
|
+
|
|
83
|
+
if (!overlay || !checkbox) return;
|
|
84
|
+
|
|
85
|
+
const handleOverlayClick = (e: Event) => {
|
|
86
|
+
if (!closeOnOverlayClick) return;
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
checkbox.checked = false;
|
|
89
|
+
isOpen = false;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleCheckboxChange = () => {
|
|
93
|
+
isOpen = checkbox.checked;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleKeydown = (e: KeyboardEvent) => {
|
|
97
|
+
if (e.key === 'Escape' && isOpen) {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
checkbox.checked = false;
|
|
100
|
+
isOpen = false;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
overlay.addEventListener('click', handleOverlayClick);
|
|
105
|
+
checkbox.addEventListener('change', handleCheckboxChange);
|
|
106
|
+
document.addEventListener('keydown', handleKeydown);
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
overlay.removeEventListener('click', handleOverlayClick);
|
|
110
|
+
checkbox.removeEventListener('change', handleCheckboxChange);
|
|
111
|
+
document.removeEventListener('keydown', handleKeydown);
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<!--
|
|
117
|
+
@component
|
|
118
|
+
|
|
119
|
+
Mobile-first sidebar navigation component with toggle and hamburger options.
|
|
120
|
+
Automatically collapses on mobile and remains visible on desktop.
|
|
121
|
+
|
|
122
|
+
@example
|
|
123
|
+
```svelte
|
|
124
|
+
<Sidebar bind:isOpen>
|
|
125
|
+
<nav>
|
|
126
|
+
<a href="/">Home</a>
|
|
127
|
+
<a href="/about">About</a>
|
|
128
|
+
</nav>
|
|
129
|
+
</Sidebar>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
@example
|
|
133
|
+
```svelte
|
|
134
|
+
<Sidebar toggleStyle="hamburger" position="right" width="300px">
|
|
135
|
+
<nav>...</nav>
|
|
136
|
+
</Sidebar>
|
|
137
|
+
```
|
|
138
|
+
-->
|
|
139
|
+
<aside
|
|
140
|
+
class="sidebar sidebar--{toggleStyle} sidebar--{position} {className}"
|
|
141
|
+
style="--sidebar-width: {width}; --sidebar-top: {top}px; --sidebar-overlay-opacity: {overlayOpacity}"
|
|
142
|
+
>
|
|
143
|
+
<!-- Click Logic -->
|
|
144
|
+
<input
|
|
145
|
+
bind:this={checkboxRef}
|
|
146
|
+
type="checkbox"
|
|
147
|
+
class="sidebar__checkbox"
|
|
148
|
+
id={uniqueId}
|
|
149
|
+
aria-label={menuLabel}
|
|
150
|
+
/>
|
|
151
|
+
<label bind:this={overlayRef} for={uniqueId} class="sidebar__overlay"></label>
|
|
152
|
+
<!-- Hamburger -->
|
|
153
|
+
{#if toggleStyle === 'hamburger'}
|
|
154
|
+
<Hamburger bind:isOpen />
|
|
155
|
+
{/if}
|
|
156
|
+
<!-- Content -->
|
|
157
|
+
<div bind:this={contentRef} class="sidebar__content" role="navigation" aria-label={menuLabel}>
|
|
158
|
+
{#if toggleStyle === 'toggle'}
|
|
159
|
+
<label for={uniqueId} class="sidebar__toggle">
|
|
160
|
+
{menuLabel}
|
|
161
|
+
</label>
|
|
162
|
+
{/if}
|
|
163
|
+
<div class="sidebar__inner">
|
|
164
|
+
{@render children()}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</aside>
|
|
168
|
+
|
|
169
|
+
<style>.sidebar__toggle {
|
|
170
|
+
position: absolute;
|
|
171
|
+
display: none;
|
|
172
|
+
bottom: 0;
|
|
173
|
+
right: 1px;
|
|
174
|
+
background-color: var(--sidebar-toggle-background, var(--colour-base-black));
|
|
175
|
+
color: var(--sidebar-toggle-colour, var(--colour-base-light));
|
|
176
|
+
padding: var(--sidebar-toggle-padding, 0.25rem 1.5rem);
|
|
177
|
+
border-top-right-radius: var(--sidebar-toggle-radius, 0.375rem);
|
|
178
|
+
border-top-left-radius: var(--sidebar-toggle-radius, 0.375rem);
|
|
179
|
+
font-size: var(--sidebar-toggle-font-size, 0.9rem);
|
|
180
|
+
transform: rotate(90deg) translate(0%, -100%);
|
|
181
|
+
transform-origin: right top;
|
|
182
|
+
cursor: pointer;
|
|
183
|
+
user-select: none;
|
|
184
|
+
transition: box-shadow 200ms linear;
|
|
185
|
+
border: 1px solid var(--sidebar-border-colour, rgba(255, 255, 255, 0.1));
|
|
186
|
+
}
|
|
187
|
+
.sidebar__toggle::before {
|
|
188
|
+
content: "";
|
|
189
|
+
position: absolute;
|
|
190
|
+
top: calc(90% + 2px);
|
|
191
|
+
left: 1px;
|
|
192
|
+
width: calc(100% - 2px);
|
|
193
|
+
height: 10%;
|
|
194
|
+
background: var(--sidebar-toggle-background, var(--colour-base-black));
|
|
195
|
+
}
|
|
196
|
+
.sidebar__overlay {
|
|
197
|
+
position: fixed;
|
|
198
|
+
top: 0;
|
|
199
|
+
left: 0;
|
|
200
|
+
width: 100%;
|
|
201
|
+
height: 100%;
|
|
202
|
+
background-color: var(--sidebar-overlay-colour, var(--colour-grey-900));
|
|
203
|
+
z-index: -100;
|
|
204
|
+
opacity: 0;
|
|
205
|
+
transition: opacity 400ms ease, z-index 400ms step-end;
|
|
206
|
+
}
|
|
207
|
+
.sidebar__checkbox {
|
|
208
|
+
position: fixed;
|
|
209
|
+
top: 0;
|
|
210
|
+
display: none;
|
|
211
|
+
}
|
|
212
|
+
.sidebar__checkbox:checked ~ .sidebar__content {
|
|
213
|
+
translate: 0;
|
|
214
|
+
z-index: 9999999;
|
|
215
|
+
}
|
|
216
|
+
.sidebar__checkbox:checked ~ .sidebar__content .sidebar__toggle {
|
|
217
|
+
box-shadow: none;
|
|
218
|
+
}
|
|
219
|
+
.sidebar__checkbox:checked ~ .sidebar__overlay {
|
|
220
|
+
transition: opacity 600ms ease, z-index 600ms step-start;
|
|
221
|
+
opacity: var(--sidebar-overlay-opacity, 0.3);
|
|
222
|
+
z-index: 999999;
|
|
223
|
+
}
|
|
224
|
+
@media (max-width: 1023px) {
|
|
225
|
+
.sidebar__content {
|
|
226
|
+
position: fixed;
|
|
227
|
+
display: grid;
|
|
228
|
+
top: 0;
|
|
229
|
+
height: 100%;
|
|
230
|
+
width: var(--sidebar-width, 50vw);
|
|
231
|
+
min-width: var(--sidebar-min-width, 270px);
|
|
232
|
+
background-color: var(--sidebar-background, var(--colour-base-black));
|
|
233
|
+
border-color: var(--sidebar-border-colour, rgba(255, 255, 255, 0.1));
|
|
234
|
+
z-index: 1000;
|
|
235
|
+
transition: translate 600ms cubic-bezier(0.1, 0.7, 0.1, 1);
|
|
236
|
+
}
|
|
237
|
+
.sidebar__inner {
|
|
238
|
+
overflow: auto;
|
|
239
|
+
display: flex;
|
|
240
|
+
flex-direction: column;
|
|
241
|
+
padding: var(--sidebar-inner-padding, 2rem 1.8rem 0 1.8rem);
|
|
242
|
+
}
|
|
243
|
+
.sidebar__toggle {
|
|
244
|
+
display: flex;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
@media (min-width: 1024px) {
|
|
248
|
+
.sidebar {
|
|
249
|
+
position: sticky;
|
|
250
|
+
top: var(--sidebar-top, 160px);
|
|
251
|
+
}
|
|
252
|
+
.sidebar__overlay {
|
|
253
|
+
display: none;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
@media (max-width: 1023px) {
|
|
257
|
+
.sidebar--left .sidebar__content {
|
|
258
|
+
left: 0;
|
|
259
|
+
border-right-style: solid;
|
|
260
|
+
border-right-width: 1px;
|
|
261
|
+
translate: -100%;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
@media (max-width: 1023px) {
|
|
265
|
+
.sidebar--right .sidebar__content {
|
|
266
|
+
right: 0;
|
|
267
|
+
border-left-style: solid;
|
|
268
|
+
border-left-width: 1px;
|
|
269
|
+
translate: 100%;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
.sidebar--hamburger .sidebar__toggle {
|
|
273
|
+
display: none;
|
|
274
|
+
}</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type SidebarProps = {
|
|
3
|
+
menuLabel?: string;
|
|
4
|
+
children: Snippet;
|
|
5
|
+
isOpen?: boolean;
|
|
6
|
+
position?: 'left' | 'right';
|
|
7
|
+
width?: string;
|
|
8
|
+
top?: number;
|
|
9
|
+
closeOnOverlayClick?: boolean;
|
|
10
|
+
overlayOpacity?: number;
|
|
11
|
+
toggleStyle?: 'toggle' | 'hamburger';
|
|
12
|
+
class?: string;
|
|
13
|
+
onOpen?: () => void;
|
|
14
|
+
onClose?: () => void;
|
|
15
|
+
onToggle?: (isOpen: boolean) => void;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Mobile-first sidebar navigation component with toggle and hamburger options.
|
|
19
|
+
* Automatically collapses on mobile and remains visible on desktop.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <Sidebar bind:isOpen>
|
|
24
|
+
* <nav>
|
|
25
|
+
* <a href="/">Home</a>
|
|
26
|
+
* <a href="/about">About</a>
|
|
27
|
+
* </nav>
|
|
28
|
+
* </Sidebar>
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```svelte
|
|
33
|
+
* <Sidebar toggleStyle="hamburger" position="right" width="300px">
|
|
34
|
+
* <nav>...</nav>
|
|
35
|
+
* </Sidebar>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare const Sidebar: import("svelte").Component<SidebarProps, {}, "isOpen">;
|
|
39
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
40
|
+
export default Sidebar;
|
|
41
|
+
//# sourceMappingURL=Sidebar.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sidebar.svelte.d.ts","sourceRoot":"","sources":["../../src/components/Sidebar.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,MAAM,YAAY,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,CAAC;AAiIF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,OAAO,wDAAwC,CAAC;AACtD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC;AAC1C,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SvelteKit utilities, components and helpers for ainsley.dev builds.
|
|
5
5
|
* Provides form utilities, grid components, and Payload CMS integrations.
|
|
6
6
|
*/
|
|
7
|
+
export * from './components/index.js';
|
|
7
8
|
export * from './components/Grid/index.js';
|
|
8
9
|
export * from './components/payload/index.js';
|
|
9
10
|
export * from './utils/forms/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Provides form utilities, grid components, and Payload CMS integrations.
|
|
6
6
|
*/
|
|
7
7
|
// Re-export all exports from submodules
|
|
8
|
+
export * from './components/index.js';
|
|
8
9
|
export * from './components/Grid/index.js';
|
|
9
10
|
export * from './components/payload/index.js';
|
|
10
11
|
export * from './utils/forms/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainsleydev/sveltekit-helper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "SvelteKit utilities, components and helpers for ainsley.dev builds",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"svelte": "./dist/index.js",
|
|
30
30
|
"default": "./dist/index.js"
|
|
31
31
|
},
|
|
32
|
+
"./components": {
|
|
33
|
+
"types": "./dist/components/index.d.ts",
|
|
34
|
+
"svelte": "./dist/components/index.js",
|
|
35
|
+
"default": "./dist/components/index.js"
|
|
36
|
+
},
|
|
32
37
|
"./components/Grid": {
|
|
33
38
|
"types": "./dist/components/Grid/index.d.ts",
|
|
34
39
|
"svelte": "./dist/components/Grid/index.js",
|
|
@@ -67,7 +72,9 @@
|
|
|
67
72
|
"optional": true
|
|
68
73
|
}
|
|
69
74
|
},
|
|
70
|
-
"dependencies": {
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"svelte-hamburgers": "^4.1.0"
|
|
77
|
+
},
|
|
71
78
|
"devDependencies": {
|
|
72
79
|
"@sveltejs/kit": "^2.0.0",
|
|
73
80
|
"@sveltejs/package": "^2.0.0",
|