@stacksjs/components 0.2.90 → 0.2.92

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,109 +0,0 @@
1
- <script server>
2
- export const showSettings = $props.showSettings ?? true
3
- export const showThemeToggle = $props.showThemeToggle ?? true
4
- export const settingsHref = $props.settingsHref || '/settings'
5
- export const settingsLabel = $props.settingsLabel || 'Settings'
6
- export const collapsed = $props.collapsed ?? false
7
- export const actions = $props.actions || []
8
- export const variant = $props.variant || 'tahoe'
9
-
10
- const footerVariants = {
11
- workspace: {
12
- container: 'p-5 space-y-2',
13
- action: 'flex-1 flex items-center gap-3 rounded-[18px] px-4 py-3 text-[17px] text-zinc-700 transition-colors hover:bg-[#ececea] hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
14
- icon: 'w-5 h-5 text-zinc-500 dark:text-zinc-400',
15
- },
16
- desktop: {
17
- container: 'p-4 space-y-2',
18
- action: 'flex-1 flex items-center gap-3 rounded-[16px] px-3.5 py-2.5 text-[15px] text-zinc-700 transition-colors hover:bg-white/34 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
19
- icon: 'w-5 h-5 text-zinc-500 dark:text-zinc-400',
20
- },
21
- default: {
22
- container: 'border-t border-black/5 dark:border-white/10 p-3 space-y-2',
23
- action: 'flex-1 flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-black/70 dark:text-white/70 hover:bg-black/5 dark:hover:bg-white/10 transition-colors',
24
- icon: 'w-4 h-4',
25
- },
26
- }
27
-
28
- const footerStyle = footerVariants[variant] || footerVariants.default
29
- export const footerClasses = footerStyle.container
30
- export const actionClasses = footerStyle.action
31
- export const footerIconClasses = footerStyle.icon
32
- export const settingsIconClasses = `i-hugeicons-settings-02 ${footerStyle.icon}`
33
- export const themeButtonClasses = variant === 'workspace' || variant === 'desktop'
34
- ? 'p-3 rounded-[16px] text-zinc-500 hover:text-zinc-900 hover:bg-white/50 dark:text-zinc-400 dark:hover:text-white dark:hover:bg-white/10 transition-colors'
35
- : 'p-2 rounded-lg text-black/50 hover:text-black/80 hover:bg-black/5 dark:text-white/50 dark:hover:text-white/80 dark:hover:bg-white/10 transition-colors'
36
- </script>
37
-
38
- <script client>
39
- const emit = defineEmits()
40
-
41
- function onThemeToggle() {
42
- document.documentElement.classList.toggle('dark')
43
- emit('themeToggle')
44
- }
45
-
46
- function onActionClick(index) {
47
- emit('actionClick', index)
48
- }
49
- </script>
50
-
51
- <div class="{{ footerClasses }}">
52
- <!-- Custom actions slot -->
53
- <slot />
54
-
55
- <!-- Quick actions row -->
56
- @if(!collapsed && actions.length > 0)
57
- <div class="flex items-center gap-2">
58
- @foreach(actions as action)
59
- @if(action.href)
60
- <a
61
- href="{{ action.href }}"
62
- class="{{ actionClasses }}"
63
- >
64
- @if(action.icon)
65
- <div class="{{ action.icon }} {{ footerIconClasses }}"></div>
66
- @endif
67
- <span>{{ action.label }}</span>
68
- </a>
69
- @else
70
- <button
71
- type="button"
72
- class="{{ actionClasses }}"
73
- @click="onActionClick({{ $loop.index }})"
74
- >
75
- @if(action.icon)
76
- <div class="{{ action.icon }} {{ footerIconClasses }}"></div>
77
- @endif
78
- <span>{{ action.label }}</span>
79
- </button>
80
- @endif
81
- @endforeach
82
-
83
- @if(showThemeToggle)
84
- <button
85
- type="button"
86
- class="{{ themeButtonClasses }}"
87
- @click="onThemeToggle()"
88
- aria-label="Toggle dark mode"
89
- >
90
- <div class="i-hugeicons-moon-02 w-4 h-4 dark:hidden"></div>
91
- <div class="i-hugeicons-sun-02 w-4 h-4 hidden dark:block"></div>
92
- </button>
93
- @endif
94
- </div>
95
- @endif
96
-
97
- <!-- Settings link -->
98
- @if(showSettings)
99
- <a
100
- href="{{ settingsHref }}"
101
- class="{{ actionClasses }}"
102
- >
103
- <div class="{{ settingsIconClasses }}"></div>
104
- @if(!collapsed)
105
- <span>{{ settingsLabel }}</span>
106
- @endif
107
- </a>
108
- @endif
109
- </div>
@@ -1,199 +0,0 @@
1
- <script server>
2
- export const title = $props.title || ''
3
- export const subtitle = $props.subtitle || ''
4
- export const logo = $props.logo || ''
5
- export const logoIcon = $props.logoIcon || ''
6
- export const showSearch = $props.showSearch ?? false
7
- export const searchPlaceholder = $props.searchPlaceholder || 'Search...'
8
- export const searchValue = $props.searchValue || ''
9
- export const collapsed = $props.collapsed ?? false
10
- export const variant = $props.variant || 'tahoe'
11
- export const showWindowControls = $props.showWindowControls ?? (variant === 'desktop' ? 'auto' : false)
12
- export const showNavigationControls = $props.showNavigationControls ?? variant === 'desktop'
13
-
14
- const headerVariants = {
15
- workspace: {
16
- titlebar: 'h-18 flex items-center justify-between px-5',
17
- logo: 'w-9 h-9 rounded-[12px] bg-black text-white flex items-center justify-center font-semibold text-sm',
18
- title: 'text-[18px] font-semibold text-zinc-950 dark:text-white truncate',
19
- subtitle: 'text-sm text-zinc-500 dark:text-zinc-400 truncate',
20
- searchWrap: 'px-5 pb-4',
21
- searchInput: 'w-full rounded-[16px] border border-zinc-200/70 bg-white/70 px-10 py-2.5 text-[15px] text-zinc-900 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-zinc-300/80 dark:border-white/10 dark:bg-white/10 dark:text-white',
22
- },
23
- desktop: {
24
- titlebar: 'h-14 flex items-center gap-5 px-5 border-b border-black/5 dark:border-white/10',
25
- chrome: 'flex shrink-0 items-center justify-start gap-5 min-h-7',
26
- trafficLight: 'h-3.5 w-3.5 rounded-full shadow-[inset_0_0_0_1px_rgba(0,0,0,0.08)]',
27
- navButton: 'inline-flex h-7 w-7 items-center justify-center rounded-full text-zinc-500/80 transition-colors hover:bg-white/35 hover:text-zinc-900 disabled:opacity-35 disabled:hover:bg-transparent disabled:hover:text-zinc-500/80 dark:text-zinc-400 dark:hover:bg-white/10 dark:hover:text-white',
28
- logo: 'w-9 h-9 rounded-[12px] bg-black/80 text-white flex items-center justify-center font-semibold text-sm shadow-sm dark:bg-white/14',
29
- title: 'text-[15px] leading-[22px] font-medium text-zinc-950 dark:text-white truncate',
30
- subtitle: 'text-xs text-zinc-500 dark:text-zinc-400 truncate',
31
- searchWrap: 'px-4 pb-4',
32
- searchInput: 'w-full rounded-[16px] border border-white/40 bg-white/45 px-10 py-2.5 text-[15px] text-zinc-900 placeholder:text-zinc-500 shadow-sm focus:outline-none focus:ring-2 focus:ring-white/70 dark:border-white/10 dark:bg-white/10 dark:text-white',
33
- },
34
- default: {
35
- titlebar: 'h-13 flex items-center justify-between px-4 border-b border-black/5 dark:border-white/10',
36
- logo: 'w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center text-white font-bold text-sm',
37
- title: 'text-sm font-semibold text-black/90 dark:text-white truncate',
38
- subtitle: 'text-xs text-black/50 dark:text-white/50 truncate',
39
- searchWrap: 'px-3 py-3',
40
- searchInput: 'w-full pl-9 pr-3 py-2 text-sm bg-black/5 dark:bg-white/10 border border-black/10 dark:border-white/10 rounded-lg text-black/90 dark:text-white placeholder:text-black/40 dark:placeholder:text-white/40 focus:outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-400/50 transition-colors',
41
- },
42
- }
43
-
44
- const headerStyle = headerVariants[variant] || headerVariants.default
45
- export const titlebarClasses = headerStyle.titlebar
46
- export const chromeClasses = headerStyle.chrome || ''
47
- export const trafficLightClasses = headerStyle.trafficLight || ''
48
- export const navButtonClasses = headerStyle.navButton || ''
49
- export const logoClasses = headerStyle.logo
50
- export const titleClasses = headerStyle.title
51
- export const subtitleClasses = headerStyle.subtitle
52
- export const searchWrapClasses = headerStyle.searchWrap
53
- export const searchInputClasses = headerStyle.searchInput
54
- export const hasDesktopChrome = variant === 'desktop' && (showWindowControls || showNavigationControls)
55
- export const windowControlsClasses = showWindowControls === 'auto' ? 'hidden items-center gap-2' : 'flex items-center gap-2'
56
- </script>
57
-
58
- <script client>
59
- const emit = defineEmits()
60
-
61
- function isCraftRuntime() {
62
- return window.__craftNativeSidebar === true
63
- || window.craft?.__craft_bridge_loaded === true
64
- || !!window.craft?.window
65
- || !!window.webkit?.messageHandlers?.craft
66
- }
67
-
68
- function usesCustomWindowControls() {
69
- return window.__craftCustomWindowControls === true
70
- || document.documentElement.classList.contains('has-custom-window-controls')
71
- || document.body?.dataset?.customWindowControls === 'true'
72
- }
73
-
74
- function syncWindowControls() {
75
- document.querySelectorAll('[data-stx-native-window-controls]').forEach((element) => {
76
- const show = element.dataset.stxNativeWindowControls === 'auto'
77
- ? isCraftRuntime() && usesCustomWindowControls()
78
- : true
79
- element.classList.toggle('hidden', !show)
80
- element.classList.toggle('flex', show)
81
- })
82
- }
83
-
84
- function onSearchInput(event) {
85
- emit('search', event.target.value)
86
- }
87
-
88
- function goBack() {
89
- window.history.back()
90
- emit('back')
91
- }
92
-
93
- function goForward() {
94
- window.history.forward()
95
- emit('forward')
96
- }
97
-
98
- onMount(() => {
99
- syncWindowControls()
100
- window.addEventListener('craft:ready', syncWindowControls)
101
- window.addEventListener('stx:load', syncWindowControls)
102
-
103
- return () => {
104
- window.removeEventListener('craft:ready', syncWindowControls)
105
- window.removeEventListener('stx:load', syncWindowControls)
106
- }
107
- })
108
- </script>
109
-
110
- <div class="flex-shrink-0">
111
- <!-- Title bar area -->
112
- <div class="{{ titlebarClasses }}">
113
- @if(hasDesktopChrome)
114
- <div class="{{ chromeClasses }}" data-stx-sidebar-web-chrome>
115
- @if(showWindowControls)
116
- <div class="{{ windowControlsClasses }}" aria-hidden="true" data-stx-native-window-controls="{{ showWindowControls === 'auto' ? 'auto' : 'always' }}">
117
- <span class="{{ trafficLightClasses }} bg-[#ff5f57]"></span>
118
- <span class="{{ trafficLightClasses }} bg-[#ffbd2e]"></span>
119
- <span class="{{ trafficLightClasses }} bg-[#28c840]"></span>
120
- </div>
121
- @endif
122
-
123
- @if(showNavigationControls)
124
- <div class="flex items-center gap-2">
125
- <button type="button" class="{{ navButtonClasses }}" @click="goBack()" aria-label="Go back">
126
- <span class="h-5 w-5 i-hugeicons-arrow-left-02" aria-hidden="true"></span>
127
- </button>
128
- <button type="button" class="{{ navButtonClasses }}" @click="goForward()" aria-label="Go forward">
129
- <span class="h-5 w-5 i-hugeicons-arrow-right-02" aria-hidden="true"></span>
130
- </button>
131
- </div>
132
- @endif
133
- </div>
134
- @endif
135
-
136
- @if(!collapsed)
137
- <div class="flex items-center gap-3 min-w-0">
138
- <!-- Logo -->
139
- @if(logo)
140
- <img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover" />
141
- @elseif(logoIcon)
142
- <div class="{{ logoClasses }}">
143
- <div class="{{ logoIcon }} w-5 h-5 text-white"></div>
144
- </div>
145
- @else
146
- <div class="{{ logoClasses }}">
147
- {{ title.charAt(0) }}
148
- </div>
149
- @endif
150
-
151
- <!-- Title & Subtitle -->
152
- <div class="min-w-0">
153
- @if(title)
154
- <div class="{{ titleClasses }}">
155
- {{ title }}
156
- </div>
157
- @endif
158
- @if(subtitle)
159
- <div class="{{ subtitleClasses }}">
160
- {{ subtitle }}
161
- </div>
162
- @endif
163
- </div>
164
- </div>
165
- @else
166
- <!-- Collapsed state - just logo -->
167
- @if(logo)
168
- <img src="{{ logo }}" alt="{{ title }}" class="w-8 h-8 rounded-lg object-cover mx-auto" />
169
- @elseif(logoIcon)
170
- <div class="{{ logoClasses }} mx-auto">
171
- <div class="{{ logoIcon }} w-5 h-5 text-white"></div>
172
- </div>
173
- @else
174
- <div class="{{ logoClasses }} mx-auto">
175
- {{ title.charAt(0) }}
176
- </div>
177
- @endif
178
- @endif
179
-
180
- <!-- Slot for custom header actions -->
181
- <slot name="actions" />
182
- </div>
183
-
184
- <!-- Search (only when expanded) -->
185
- @if(showSearch && !collapsed)
186
- <div class="{{ searchWrapClasses }}">
187
- <div class="relative">
188
- <div class="i-hugeicons-search-01 absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-black/40 dark:text-white/40"></div>
189
- <input
190
- type="text"
191
- placeholder="{{ searchPlaceholder }}"
192
- value="{{ searchValue }}"
193
- class="{{ searchInputClasses }}"
194
- @input="onSearchInput($event)"
195
- />
196
- </div>
197
- </div>
198
- @endif
199
- </div>
@@ -1,92 +0,0 @@
1
- <script server>
2
- export const id = $props.id || ''
3
- export const label = $props.label || ''
4
- export const icon = $props.icon || ''
5
- export const href = $props.href || ''
6
- export const badge = $props.badge || ''
7
- export const active = $props.active ?? false
8
- export const disabled = $props.disabled ?? false
9
- export const indent = $props.indent ?? false
10
- export const variant = $props.variant || 'tahoe'
11
-
12
- const itemVariants = {
13
- workspace: {
14
- base: 'flex w-full items-center gap-3 rounded-[16px] px-3.5 py-2 text-[14px] leading-tight transition-colors duration-150',
15
- indent: '',
16
- active: 'bg-[#e7e7e5] text-zinc-950',
17
- inactive: 'text-zinc-700 hover:bg-[#ececea] hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
18
- icon: active ? 'text-zinc-900 dark:text-zinc-100' : 'text-zinc-500 dark:text-zinc-500',
19
- badge: 'ml-auto grid h-5 min-w-5 place-items-center rounded-full bg-white/70 px-1.5 text-[11px] font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
20
- },
21
- desktop: {
22
- base: 'flex w-full items-center gap-3 rounded-[14px] px-3 py-1.5 text-[13.5px] leading-tight transition-colors duration-150',
23
- indent: '',
24
- active: 'bg-[#e8e8e6]/90 text-zinc-950 dark:bg-white/12 dark:text-white',
25
- inactive: 'text-zinc-700 hover:bg-white/38 hover:text-zinc-950 dark:text-zinc-300 dark:hover:bg-white/10',
26
- icon: active ? 'text-zinc-900 dark:text-white' : 'text-zinc-600 dark:text-zinc-400',
27
- badge: 'ml-auto grid h-5 min-w-5 place-items-center rounded-full bg-white/72 px-1.5 text-[11px] font-medium text-zinc-500 shadow-sm dark:bg-white/10 dark:text-zinc-400',
28
- },
29
- default: {
30
- base: 'flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-[13px] transition-all duration-150',
31
- indent: indent ? 'ml-2' : '',
32
- active: 'bg-blue-500/20 text-blue-600 dark:bg-blue-400/25 dark:text-blue-300 font-medium',
33
- inactive: 'text-black/80 dark:text-white/80 hover:bg-black/5 dark:hover:bg-white/10',
34
- icon: active ? 'text-blue-600 dark:text-blue-300' : 'text-black/50 dark:text-white/50',
35
- badge: 'ml-auto px-2 py-0.5 text-xs font-medium rounded-full bg-black/10 text-black/70 dark:bg-white/15 dark:text-white/70',
36
- },
37
- }
38
-
39
- const itemStyle = itemVariants[variant] || itemVariants.default
40
- const baseItemList = [itemStyle.base, itemStyle.indent]
41
- const stateClasses = disabled ? 'opacity-50 cursor-not-allowed' : active ? itemStyle.active : itemStyle.inactive
42
-
43
- export const itemClasses = [...baseItemList, stateClasses].filter(Boolean).join(' ')
44
- export const iconClasses = `${icon} h-[18px] w-[18px] flex-shrink-0 ${itemStyle.icon}`
45
- export const badgeClasses = itemStyle.badge
46
- </script>
47
-
48
- <script client>
49
- const emit = defineEmits()
50
- const isDisabled = {{ disabled }}
51
- const itemPayload = { id: {{ id }}, label: {{ label }}, href: {{ href }} }
52
-
53
- function onClick(event) {
54
- if (isDisabled) {
55
- event.preventDefault()
56
- return
57
- }
58
- emit('click', { item: itemPayload, event })
59
- }
60
- </script>
61
-
62
- @if(href && !disabled)
63
- <a
64
- href="{{ href }}"
65
- class="{{ itemClasses }}"
66
- @click="onClick($event)"
67
- @if(active)aria-current="page"@endif
68
- >
69
- @if(icon)
70
- <div class="{{ iconClasses }}"></div>
71
- @endif
72
- <span class="flex-1 truncate">{{ label }}</span>
73
- @if(badge)
74
- <span class="{{ badgeClasses }}">{{ badge }}</span>
75
- @endif
76
- </a>
77
- @else
78
- <button
79
- type="button"
80
- class="{{ itemClasses }}"
81
- @click="onClick($event)"
82
- @if(disabled)disabled aria-disabled="true"@endif
83
- >
84
- @if(icon)
85
- <div class="{{ iconClasses }}"></div>
86
- @endif
87
- <span class="flex-1 truncate text-left">{{ label }}</span>
88
- @if(badge)
89
- <span class="{{ badgeClasses }}">{{ badge }}</span>
90
- @endif
91
- </button>
92
- @endif
@@ -1,114 +0,0 @@
1
- <script server>
2
- export const id = $props.id || ''
3
- export const label = $props.label || ''
4
- export const icon = $props.icon || ''
5
- export const items = $props.items || []
6
- export const expanded = $props.expanded ?? true
7
- export const collapsible = $props.collapsible ?? true
8
- export const showLabel = $props.showLabel ?? true
9
- export const variant = $props.variant || 'tahoe'
10
-
11
- const headerVariants = {
12
- workspace: {
13
- base: 'w-full flex items-center gap-2.5 px-4 pb-2 pt-5 rounded-[14px] text-left transition-colors duration-150 text-[13px] font-semibold uppercase tracking-[0.12em] text-zinc-400 dark:text-zinc-500',
14
- hover: 'hover:text-zinc-600 dark:hover:text-zinc-300 cursor-pointer',
15
- },
16
- desktop: {
17
- base: 'w-full flex items-center gap-2.5 px-3 pb-1.5 pt-3 rounded-[14px] text-left transition-colors duration-150 text-[12px] font-semibold uppercase tracking-[0.10em] text-zinc-500/80 dark:text-zinc-400/80',
18
- hover: 'hover:bg-white/25 dark:hover:bg-white/10 cursor-pointer',
19
- },
20
- default: {
21
- base: 'w-full flex items-center gap-2.5 px-2.5 py-1.5 rounded-md text-left transition-all duration-150 text-black/60 dark:text-white/60',
22
- hover: 'hover:bg-black/5 dark:hover:bg-white/10 cursor-pointer',
23
- },
24
- }
25
-
26
- const headerStyle = headerVariants[variant] || headerVariants.default
27
- const headerClasses = [headerStyle.base, collapsible ? headerStyle.hover : ''].filter(Boolean).join(' ')
28
- export const headerClassesValue = headerClasses
29
- export const sectionClasses = variant === 'workspace' ? 'py-1.5' : variant === 'desktop' ? 'py-1' : 'py-1'
30
- export const groupClasses = variant === 'workspace' || variant === 'desktop' ? 'space-y-1 overflow-hidden' : 'mt-1 space-y-0.5 overflow-hidden'
31
- export const labelClasses = variant === 'workspace' || variant === 'desktop'
32
- ? 'flex-1 truncate'
33
- : 'flex-1 text-[13px] font-semibold tracking-tight truncate'
34
- </script>
35
-
36
- <script client>
37
- const emit = defineEmits()
38
- const sectionId = {{ id }}
39
- const collapsibleProp = {{ collapsible }}
40
- // Reactive prop binding — parent can drive `expanded` via signals. See #1704.
41
- const isExpanded = useReactiveProp('expanded', {{ expanded }})
42
-
43
- function onHeaderClick() {
44
- if (!collapsibleProp) return
45
- isExpanded.set(!isExpanded())
46
- emit('toggle', sectionId)
47
- }
48
-
49
- function onItemClick(event) {
50
- emit('itemClick', event.detail)
51
- }
52
-
53
- const arrowClass = derived(() => `i-hugeicons-arrow-right-01 w-3 h-3 transition-transform duration-200 opacity-50 ${isExpanded() ? 'rotate-90' : ''}`)
54
-
55
- defineExpose({ isExpanded })
56
- </script>
57
-
58
- <div class="{{ sectionClasses }}">
59
- @if(collapsible)
60
- <button
61
- type="button"
62
- class="{{ headerClassesValue }}"
63
- @click="onHeaderClick()"
64
- :aria-expanded="isExpanded() ? 'true' : 'false'"
65
- aria-controls="sidebar-section-{{ id }}"
66
- >
67
- @if(icon)
68
- <div class="{{ icon }} w-4 h-4 flex-shrink-0 opacity-70"></div>
69
- @endif
70
- @if(showLabel)
71
- <span class="{{ labelClasses }}">
72
- {{ label }}
73
- </span>
74
- <div :class="arrowClass()"></div>
75
- @endif
76
- </button>
77
- @else
78
- <div class="{{ headerClassesValue }}">
79
- @if(icon)
80
- <div class="{{ icon }} w-4 h-4 flex-shrink-0 opacity-70"></div>
81
- @endif
82
- @if(showLabel)
83
- <span class="{{ labelClasses }}">
84
- {{ label }}
85
- </span>
86
- @endif
87
- </div>
88
- @endif
89
-
90
- @if(showLabel && items.length > 0)
91
- <div
92
- :show="isExpanded()"
93
- id="sidebar-section-{{ id }}"
94
- class="{{ groupClasses }}"
95
- role="group"
96
- aria-label="{{ label }}"
97
- >
98
- @foreach(item in items)
99
- <SidebarItem
100
- :id="item.id"
101
- :label="item.label"
102
- :icon="item.icon"
103
- :href="item.href"
104
- :badge="item.badge"
105
- :active="item.active"
106
- :disabled="item.disabled"
107
- :indent="variant !== 'workspace' && variant !== 'desktop'"
108
- :variant="variant"
109
- @click="onItemClick($event)"
110
- />
111
- @endforeach
112
- </div>
113
- @endif
114
- </div>