@placeholderco/placeholder-ui 1.0.6 → 1.0.7

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.
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { LinkNavbarItem } from '../models/NavbarItem.js';
3
- import { Link, ThemeSwitcher, ActionIcon, iconHamburger, type Hyperlink } from '../index.js';
3
+ import type { SidenavSection } from './Sidenav.svelte';
4
+ import { Link, ThemeSwitcher, ActionIcon, Drawer, iconHamburger, type Hyperlink } from '../index.js';
4
5
  import type { Snippet } from 'svelte';
5
6
 
6
7
  interface Props {
@@ -12,6 +13,10 @@
12
13
  inContainer?: boolean;
13
14
  middleSection?: Snippet;
14
15
  rightSection?: Snippet;
16
+ /** Enable responsive collapse to burger menu on small screens (default: true) */
17
+ responsive?: boolean;
18
+ /** Breakpoint in pixels at which the navbar collapses (default: 768) */
19
+ responsiveBreakpoint?: number;
15
20
  // Drawer button options
16
21
  showDrawerButton?: boolean;
17
22
  drawerButtonPosition?: 'left' | 'right';
@@ -28,14 +33,49 @@
28
33
  inContainer = false,
29
34
  middleSection = undefined,
30
35
  rightSection = undefined,
36
+ responsive = true,
37
+ responsiveBreakpoint = 768,
31
38
  // Drawer button options
32
39
  showDrawerButton = false,
33
40
  drawerButtonPosition = 'right',
34
41
  drawerButtonIcon = iconHamburger,
35
42
  onDrawerButtonClick = undefined
36
43
  }: Props = $props();
44
+
45
+ let drawerOpen = $state(false);
46
+ let windowWidth = $state(typeof window !== 'undefined' ? window.innerWidth : 1024);
47
+ const isCollapsed = $derived(responsive && windowWidth <= responsiveBreakpoint);
48
+
49
+ const drawerSections = $derived.by((): SidenavSection[] => {
50
+ const sections: SidenavSection[] = [];
51
+ if (items.length > 0) {
52
+ sections.push({
53
+ items: items.map((item) => ({
54
+ label: item.label,
55
+ href: item.href,
56
+ iconSvg: item.iconSvg
57
+ }))
58
+ });
59
+ }
60
+ if (secondaryItems.length > 0) {
61
+ sections.push({
62
+ items: secondaryItems.map((item) => ({
63
+ label: item.label,
64
+ href: item.href,
65
+ iconSvg: item.iconSvg
66
+ }))
67
+ });
68
+ }
69
+ return sections;
70
+ });
71
+
72
+ function handleBurgerClick() {
73
+ drawerOpen = true;
74
+ }
37
75
  </script>
38
76
 
77
+ <svelte:window onresize={() => (windowWidth = window.innerWidth)} />
78
+
39
79
  <header>
40
80
  <div class="inner-navbar {className} {inContainer ? 'container' : ''}">
41
81
  {#if showDrawerButton && drawerButtonPosition === 'left'}
@@ -52,52 +92,41 @@
52
92
  <Link class="text-xl" href={appNav.href}>{appNav.text}</Link>
53
93
  </div>
54
94
  {/if}
55
- <!-- {#each items.filter((x) => !x.showInOnly || x.showInOnly === 'Navbar') as item}
56
- {#if 'subItems' in item && item.subItems && item.subItems.length > 0}
57
- <div class:hidden-when-small={!item.alwaysShow} class={item.class}>
58
- <Dropdown let:toggleShow>
59
- <button onclick={toggleShow} class="cursor-pointer"
60
- >{item.label}
61
- <Icon svg={iconChevronRight} size="0.35em" class="ml-1 rotate-90" /></button
62
- >
63
- <div slot="dropdown">
64
- {#each item.subItems as subItem}
65
- <NavbarItemDisplay item={subItem} isInDrawer={false} />
66
- {/each}
67
- </div>
68
- </Dropdown>
69
- </div>
70
- {:else}
71
- <NavbarItemDisplay {item} isInDrawer={false} />
72
- {/if}
73
- {/each} -->
74
-
75
- <div class="primary links-container">
76
- {#each items as item}
77
- <Link href={item.href}>{item.label}</Link>
78
- {/each}
79
- </div>
95
+
96
+ {#if !isCollapsed}
97
+ <div class="primary links-container">
98
+ {#each items as item}
99
+ <Link href={item.href}>{item.label}</Link>
100
+ {/each}
101
+ </div>
102
+ {/if}
80
103
 
81
104
  <div class="middle">
82
105
  {@render middleSection?.()}
83
106
  </div>
84
- <!-- <ActionIcon
85
- class="hamburger"
86
- variant={$isDarkMode ? 'auto-subtle' : 'primary'}
87
- onclick={toggleDrawer}
88
- svg={iconHamburger}
89
- /> -->
90
- <div class="secondary links-container">
91
- {#each secondaryItems as item}
92
- {#if item.href.indexOf('/') === 0}
93
- <Link href={item.href}>{item.label}</Link>
94
- {:else}
95
- <a href={item.href}>{item.label}</a>
96
- {/if}
97
- {/each}
98
- </div>
99
- {@render rightSection?.()}
107
+
108
+ {#if !isCollapsed}
109
+ <div class="secondary links-container">
110
+ {#each secondaryItems as item}
111
+ {#if item.href.indexOf('/') === 0}
112
+ <Link href={item.href}>{item.label}</Link>
113
+ {:else}
114
+ <a href={item.href}>{item.label}</a>
115
+ {/if}
116
+ {/each}
117
+ </div>
118
+ {@render rightSection?.()}
119
+ {/if}
100
120
  <ThemeSwitcher darkVariant="secondary-subtle" lightVariant="primary-subtle" />
121
+ {#if isCollapsed}
122
+ <ActionIcon
123
+ svg={iconHamburger}
124
+ variant="secondary-subtle"
125
+ size="1.25rem"
126
+ onclick={handleBurgerClick}
127
+ class="responsive-burger"
128
+ />
129
+ {/if}
101
130
  {#if showDrawerButton && drawerButtonPosition === 'right'}
102
131
  <ActionIcon
103
132
  svg={drawerButtonIcon}
@@ -110,21 +139,15 @@
110
139
  </div>
111
140
  </header>
112
141
 
113
- <!-- <Drawer bind:open={showDrawer}>
114
- <div class="header top-header">{drawerHeader ?? ''}</div>
115
- {#each items.filter((x) => !x.showInOnly || x.showInOnly === 'Drawer') as item}
116
- {#if 'subItems' in item && item.subItems && item.subItems.length > 0}
117
- <div class="header">{item.label}</div>
118
- <div class="collection">
119
- {#each item.subItems as subItem}
120
- <NavbarItemDisplay item={subItem} isInDrawer={true} />
121
- {/each}
122
- </div>
123
- {:else}
124
- <div class="collection"><NavbarItemDisplay {item} isInDrawer={true} /></div>
125
- {/if}
126
- {/each}
127
- </Drawer> -->
142
+ {#if responsive}
143
+ <Drawer
144
+ bind:open={drawerOpen}
145
+ title={drawerHeader ?? appNav?.text ?? ''}
146
+ sections={drawerSections}
147
+ closeForLargeScreens={true}
148
+ largeScreenBreakpoint={responsiveBreakpoint}
149
+ />
150
+ {/if}
128
151
 
129
152
  <style>
130
153
  header {
@@ -163,29 +186,8 @@
163
186
  flex-grow: 1;
164
187
  }
165
188
 
166
- .hidden-when-small {
167
- display: none;
168
- }
169
-
170
- .inner-navbar :global(.hamburger) {
171
- margin-left: auto;
172
- }
173
-
174
- @media (max-width: 640px) {
175
- .hidden-when-small {
176
- display: block;
177
- }
178
-
179
- .inner-navbar :global(.hamburger) {
180
- display: none;
181
- }
182
- }
183
-
184
- .top-header {
185
- border-bottom-width: 1px;
186
- border-bottom-style: solid;
187
- border-color: var(--pui-border-default);
188
- height: var(--pui-spacing-6);
189
+ .inner-navbar :global(.responsive-burger) {
190
+ flex-shrink: 0;
189
191
  }
190
192
 
191
193
  .inner-navbar :global(.cu-icon) {
@@ -10,6 +10,10 @@ interface Props {
10
10
  inContainer?: boolean;
11
11
  middleSection?: Snippet;
12
12
  rightSection?: Snippet;
13
+ /** Enable responsive collapse to burger menu on small screens (default: true) */
14
+ responsive?: boolean;
15
+ /** Breakpoint in pixels at which the navbar collapses (default: 768) */
16
+ responsiveBreakpoint?: number;
13
17
  showDrawerButton?: boolean;
14
18
  drawerButtonPosition?: 'left' | 'right';
15
19
  drawerButtonIcon?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@placeholderco/placeholder-ui",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A modern, customizable Svelte 5 UI component library with comprehensive theming support",
5
5
  "license": "MIT",
6
6
  "repository": {