@lyeve-labs/ui-kit 0.16.0 → 0.17.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.
@@ -21,7 +21,7 @@
21
21
  * reader announce the page name twice.
22
22
  */
23
23
  import type { Snippet } from 'svelte';
24
- import { Menu } from '@lucide/svelte';
24
+ import { Menu, PanelLeft, PanelLeftClose } from '@lucide/svelte';
25
25
  import { overlay } from '../internal/overlay.js';
26
26
  import { APP_BRAND, APP_HEADER, APP_SIDEBAR, APP_SIDEBAR_BAND } from '../internal/layout.js';
27
27
 
@@ -30,6 +30,23 @@
30
30
  section?: string;
31
31
  /** Bindable so the app router can close the drawer after a navigation. */
32
32
  navOpen?: boolean;
33
+ /**
34
+ * Offers a control that puts the sidebar away above md:.
35
+ *
36
+ * Opt-in rather than on by default: three products render this shell, and
37
+ * a new button in every header of all three is a decision each of them
38
+ * makes for itself. A page that owns the viewport - a canvas, a split pane
39
+ * - is the case it exists for.
40
+ */
41
+ collapsible?: boolean;
42
+ /**
43
+ * Whether the sidebar is put away. Desktop only: below md: the sidebar is
44
+ * already a drawer and `navOpen` is the state that governs it.
45
+ *
46
+ * Bindable and not held here, because where it is remembered is the app's
47
+ * decision. Nothing persists it for you.
48
+ */
49
+ collapsed?: boolean;
33
50
  /** The sidebar landmark's accessible name. */
34
51
  sidebarLabel?: string;
35
52
  /** The drawer's accessible name, below md: where the sidebar is a dialog. */
@@ -49,6 +66,8 @@
49
66
  let {
50
67
  section = undefined,
51
68
  navOpen = $bindable(false),
69
+ collapsible = false,
70
+ collapsed = $bindable(false),
52
71
  sidebarLabel = 'Sidebar',
53
72
  drawerLabel = 'Navigation',
54
73
  brand,
@@ -66,6 +85,11 @@
66
85
  */
67
86
  let isMobile = $state(false);
68
87
  const drawerOpen = $derived(isMobile && navOpen);
88
+ /**
89
+ * Collapsing is a desktop gesture. Below md: the same aside is the drawer,
90
+ * so honouring `collapsed` there would leave the hamburger opening nothing.
91
+ */
92
+ const railHidden = $derived(collapsible && collapsed && !isMobile);
69
93
 
70
94
  $effect(() => {
71
95
  const mq = window.matchMedia('(max-width: 767px)');
@@ -129,7 +153,7 @@
129
153
  >
130
154
  {@render sidebar(false)}
131
155
  </div>
132
- {:else}
156
+ {:else if !railHidden}
133
157
  <div class="hidden md:flex">
134
158
  {@render sidebar(isMobile)}
135
159
  </div>
@@ -151,6 +175,19 @@
151
175
  >
152
176
  <Menu size={20} />
153
177
  </button>
178
+ {:else if collapsible}
179
+ <!-- Same square and the same gutter as the hamburger it replaces, so
180
+ the header's first control sits in one place at every width. -->
181
+ <button
182
+ type="button"
183
+ data-testid="app-sidebar-toggle"
184
+ class="-ms-2 flex h-11 w-11 shrink-0 items-center justify-center rounded-lg text-muted outline-none transition-colors duration-150 hover:bg-surface-2 hover:text-fg focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand"
185
+ aria-label={collapsed ? 'Show sidebar' : 'Hide sidebar'}
186
+ aria-expanded={!collapsed}
187
+ onclick={() => (collapsed = !collapsed)}
188
+ >
189
+ {#if collapsed}<PanelLeft size={18} />{:else}<PanelLeftClose size={18} />{/if}
190
+ </button>
154
191
  {/if}
155
192
  {#if section}
156
193
  <span data-testid="app-section" class="truncate text-sm font-semibold text-fg">
@@ -25,6 +25,23 @@ interface Props {
25
25
  section?: string;
26
26
  /** Bindable so the app router can close the drawer after a navigation. */
27
27
  navOpen?: boolean;
28
+ /**
29
+ * Offers a control that puts the sidebar away above md:.
30
+ *
31
+ * Opt-in rather than on by default: three products render this shell, and
32
+ * a new button in every header of all three is a decision each of them
33
+ * makes for itself. A page that owns the viewport - a canvas, a split pane
34
+ * - is the case it exists for.
35
+ */
36
+ collapsible?: boolean;
37
+ /**
38
+ * Whether the sidebar is put away. Desktop only: below md: the sidebar is
39
+ * already a drawer and `navOpen` is the state that governs it.
40
+ *
41
+ * Bindable and not held here, because where it is remembered is the app's
42
+ * decision. Nothing persists it for you.
43
+ */
44
+ collapsed?: boolean;
28
45
  /** The sidebar landmark's accessible name. */
29
46
  sidebarLabel?: string;
30
47
  /** The drawer's accessible name, below md: where the sidebar is a dialog. */
@@ -40,6 +57,6 @@ interface Props {
40
57
  class?: string;
41
58
  children: Snippet;
42
59
  }
43
- declare const AppShell: import("svelte").Component<Props, {}, "navOpen">;
60
+ declare const AppShell: import("svelte").Component<Props, {}, "navOpen" | "collapsed">;
44
61
  type AppShell = ReturnType<typeof AppShell>;
45
62
  export default AppShell;
@@ -72,9 +72,7 @@
72
72
  aria-label={title ? undefined : 'Dialog'}
73
73
  >
74
74
  {#if title}
75
- <div
76
- class="flex shrink-0 items-start justify-between gap-4 px-5 py-4 border-b border-line"
77
- >
75
+ <div class="flex shrink-0 items-start justify-between gap-4 px-5 py-4 border-b border-line">
78
76
  <div>
79
77
  <h2 id={headingId} class="font-semibold text-fg">{title}</h2>
80
78
  {#if description}
@@ -26,13 +26,7 @@
26
26
  children: Snippet;
27
27
  }
28
28
 
29
- let {
30
- level = 2,
31
- variant = 'default',
32
- actions,
33
- class: klass = '',
34
- children,
35
- }: Props = $props();
29
+ let { level = 2, variant = 'default', actions, class: klass = '', children }: Props = $props();
36
30
  </script>
37
31
 
38
32
  <div class="flex flex-wrap items-center justify-between gap-4 {klass}">
@@ -14,10 +14,7 @@
14
14
  import type { DialogEntry } from './types.js';
15
15
  import { sizeClass } from './types.js';
16
16
  import { overlay } from '../../internal/overlay.js';
17
- import {
18
- closeDialog,
19
- dismissDialog,
20
- } from './dialog-manager.svelte';
17
+ import { closeDialog, dismissDialog } from './dialog-manager.svelte';
21
18
  import ConfirmDialog from './ConfirmDialog.svelte';
22
19
 
23
20
  let {
package/dist/index.d.ts CHANGED
@@ -86,4 +86,4 @@ export { openDialog, closeDialog, dismissDialog, dismissAllDialogs, confirm, set
86
86
  export type { DialogOptions, DialogEntry, DialogSize } from './components/dialog/types.js';
87
87
  export { cn, type ClassValue } from './utils/cn.js';
88
88
  export { getTheme, setTheme, toggleTheme, themeBootScript, type Theme } from './utils/theme.js';
89
- export declare const VERSION = "0.16.0";
89
+ export declare const VERSION = "0.17.1";
package/dist/index.js CHANGED
@@ -92,4 +92,4 @@ export { getTheme, setTheme, toggleTheme, themeBootScript } from './utils/theme.
92
92
  // ── Version ────────────────────────────────────────────────────────────────
93
93
  // Generated from package.json by `pnpm version:sync`. Bump package.json, never
94
94
  // this line; the build and the test suite fail when the two disagree.
95
- export const VERSION = '0.16.0';
95
+ export const VERSION = '0.17.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lyeve-labs/ui-kit",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "A clean, accessible, palette-aware Svelte 5 component library. The design system behind LyEve.",
5
5
  "license": "MIT",
6
6
  "author": "LyEve Labs <hello@lyeve.com>",