@vobs/ui 0.3.0 → 1.0.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.
Files changed (212) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +60 -0
  3. package/package.json +52 -110
  4. package/src/activity-rail.ts +125 -0
  5. package/src/alert.ts +65 -0
  6. package/src/assets/icons/alert-circle.svg +1 -0
  7. package/src/assets/icons/alert-triangle.svg +1 -0
  8. package/src/assets/icons/alert.d27e24.svg +1 -0
  9. package/src/assets/icons/alert.svg +1 -0
  10. package/src/assets/icons/apple.svg +1 -0
  11. package/src/assets/icons/arrow-down.svg +1 -0
  12. package/src/assets/icons/arrow-expand.svg +1 -0
  13. package/src/assets/icons/arrow-left.svg +1 -0
  14. package/src/assets/icons/arrow-minimize.svg +1 -0
  15. package/src/assets/icons/arrow-right-to-line.svg +1 -0
  16. package/src/assets/icons/arrow-right.svg +1 -0
  17. package/src/assets/icons/arrow-up-right.svg +1 -0
  18. package/src/assets/icons/arrow-up.0c0c0d.svg +1 -0
  19. package/src/assets/icons/arrow-up.svg +1 -0
  20. package/src/assets/icons/at.svg +1 -0
  21. package/src/assets/icons/atom.0c0c0d.svg +1 -0
  22. package/src/assets/icons/atom.svg +1 -0
  23. package/src/assets/icons/bar.svg +1 -0
  24. package/src/assets/icons/bell.svg +1 -0
  25. package/src/assets/icons/box.svg +1 -0
  26. package/src/assets/icons/bug.svg +1 -0
  27. package/src/assets/icons/calendar.svg +1 -0
  28. package/src/assets/icons/check-circle.33c192.svg +1 -0
  29. package/src/assets/icons/check-circle.svg +1 -0
  30. package/src/assets/icons/check.d1d3db.svg +1 -0
  31. package/src/assets/icons/check.svg +1 -0
  32. package/src/assets/icons/chevron-down.svg +1 -0
  33. package/src/assets/icons/chevron-right.svg +1 -0
  34. package/src/assets/icons/chevron-up.svg +1 -0
  35. package/src/assets/icons/circle.svg +1 -0
  36. package/src/assets/icons/clock.svg +1 -0
  37. package/src/assets/icons/cmd.svg +1 -0
  38. package/src/assets/icons/code.svg +1 -0
  39. package/src/assets/icons/columns.svg +1 -0
  40. package/src/assets/icons/copy.svg +1 -0
  41. package/src/assets/icons/cpu.svg +1 -0
  42. package/src/assets/icons/dashboard.0c0c0d.svg +1 -0
  43. package/src/assets/icons/dashboard.svg +1 -0
  44. package/src/assets/icons/dollar.svg +1 -0
  45. package/src/assets/icons/download.svg +1 -0
  46. package/src/assets/icons/edit.svg +1 -0
  47. package/src/assets/icons/extensions.svg +1 -0
  48. package/src/assets/icons/external.svg +1 -0
  49. package/src/assets/icons/eye.svg +1 -0
  50. package/src/assets/icons/file-text.svg +1 -0
  51. package/src/assets/icons/file.svg +1 -0
  52. package/src/assets/icons/files.svg +1 -0
  53. package/src/assets/icons/filter.svg +1 -0
  54. package/src/assets/icons/folder.svg +1 -0
  55. package/src/assets/icons/gear.svg +1 -0
  56. package/src/assets/icons/git.svg +1 -0
  57. package/src/assets/icons/github.svg +1 -0
  58. package/src/assets/icons/globe.svg +1 -0
  59. package/src/assets/icons/grid-2x2.svg +1 -0
  60. package/src/assets/icons/hash.svg +1 -0
  61. package/src/assets/icons/heart.svg +1 -0
  62. package/src/assets/icons/help.svg +1 -0
  63. package/src/assets/icons/home.svg +1 -0
  64. package/src/assets/icons/image.svg +1 -0
  65. package/src/assets/icons/info-circle.svg +1 -0
  66. package/src/assets/icons/info-square.svg +1 -0
  67. package/src/assets/icons/info.387bff.svg +1 -0
  68. package/src/assets/icons/info.svg +1 -0
  69. package/src/assets/icons/key.svg +1 -0
  70. package/src/assets/icons/layers.svg +1 -0
  71. package/src/assets/icons/layout.svg +1 -0
  72. package/src/assets/icons/link.svg +1 -0
  73. package/src/assets/icons/list.svg +1 -0
  74. package/src/assets/icons/lock.svg +1 -0
  75. package/src/assets/icons/log-out.svg +1 -0
  76. package/src/assets/icons/logo.svg +1 -0
  77. package/src/assets/icons/mail.svg +1 -0
  78. package/src/assets/icons/menu.svg +1 -0
  79. package/src/assets/icons/message-circle.svg +1 -0
  80. package/src/assets/icons/message-plus.svg +1 -0
  81. package/src/assets/icons/message-square.svg +1 -0
  82. package/src/assets/icons/mic.svg +1 -0
  83. package/src/assets/icons/minus.svg +1 -0
  84. package/src/assets/icons/moon.svg +1 -0
  85. package/src/assets/icons/more-h.svg +1 -0
  86. package/src/assets/icons/mouse.svg +1 -0
  87. package/src/assets/icons/palette.svg +1 -0
  88. package/src/assets/icons/panel-bottom.svg +1 -0
  89. package/src/assets/icons/pause.svg +1 -0
  90. package/src/assets/icons/play.svg +1 -0
  91. package/src/assets/icons/plug.svg +1 -0
  92. package/src/assets/icons/plus.0c0c0d.svg +1 -0
  93. package/src/assets/icons/plus.svg +1 -0
  94. package/src/assets/icons/refresh.svg +1 -0
  95. package/src/assets/icons/ruler.svg +1 -0
  96. package/src/assets/icons/scroll-text.svg +1 -0
  97. package/src/assets/icons/search-menu.svg +1 -0
  98. package/src/assets/icons/search.svg +1 -0
  99. package/src/assets/icons/send.0c0c0d.svg +1 -0
  100. package/src/assets/icons/send.svg +1 -0
  101. package/src/assets/icons/settings.0c0c0d.svg +1 -0
  102. package/src/assets/icons/settings.svg +1 -0
  103. package/src/assets/icons/shield.svg +1 -0
  104. package/src/assets/icons/sidebar-left.svg +1 -0
  105. package/src/assets/icons/sidebar-right.svg +1 -0
  106. package/src/assets/icons/sliders.svg +1 -0
  107. package/src/assets/icons/sparkles.32f08c.svg +1 -0
  108. package/src/assets/icons/sparkles.svg +1 -0
  109. package/src/assets/icons/square.svg +1 -0
  110. package/src/assets/icons/star.svg +1 -0
  111. package/src/assets/icons/sun.svg +1 -0
  112. package/src/assets/icons/terminal.svg +1 -0
  113. package/src/assets/icons/trash.svg +1 -0
  114. package/src/assets/icons/trending-down.svg +1 -0
  115. package/src/assets/icons/trending-up.svg +1 -0
  116. package/src/assets/icons/type.svg +1 -0
  117. package/src/assets/icons/upload.svg +1 -0
  118. package/src/assets/icons/user-circle.svg +1 -0
  119. package/src/assets/icons/user.svg +1 -0
  120. package/src/assets/icons/users.svg +1 -0
  121. package/src/assets/icons/wrench.svg +1 -0
  122. package/src/assets/icons/x-circle.svg +1 -0
  123. package/src/assets/icons/x.svg +1 -0
  124. package/src/assets/icons/zap.0c0c0d.svg +1 -0
  125. package/src/assets/icons/zap.svg +1 -0
  126. package/src/avatar.ts +64 -0
  127. package/src/button.ts +88 -0
  128. package/src/card.ts +59 -0
  129. package/src/chat-composer.ts +156 -0
  130. package/src/dialog.ts +127 -0
  131. package/src/drawer.ts +131 -0
  132. package/src/editor-tabs.ts +184 -0
  133. package/src/file-tree.ts +214 -0
  134. package/src/forms.ts +270 -0
  135. package/src/icon.ts +117 -0
  136. package/src/icons.js +165 -0
  137. package/src/index.test.ts +213 -0
  138. package/src/index.ts +94 -0
  139. package/src/kbd.ts +72 -0
  140. package/src/menu.ts +119 -0
  141. package/src/nav-list.ts +157 -0
  142. package/src/overlay.test.ts +66 -0
  143. package/src/overlay.ts +205 -0
  144. package/src/page-header.ts +97 -0
  145. package/src/pagination.ts +163 -0
  146. package/src/second-batch.test.ts +205 -0
  147. package/src/stat-card.ts +92 -0
  148. package/src/status-bar.ts +108 -0
  149. package/src/styles/base.css +240 -0
  150. package/src/styles/components.css +1234 -0
  151. package/{dist → src/styles}/styles.css +2 -0
  152. package/src/styles/toast.css +57 -0
  153. package/src/styles/tokens.css +438 -0
  154. package/src/styles/vui.css +47 -0
  155. package/src/table.ts +191 -0
  156. package/src/tabs.ts +100 -0
  157. package/src/tag.ts +33 -0
  158. package/src/third-batch.test.ts +137 -0
  159. package/src/toast.test.ts +55 -0
  160. package/src/toast.ts +79 -0
  161. package/src/types.ts +25 -0
  162. package/src/utils.ts +221 -0
  163. package/src/workbench-titlebar.ts +137 -0
  164. package/dist/app-shell-dom.d.ts +0 -30
  165. package/dist/app-shell-dom.js +0 -913
  166. package/dist/app-shell.d.ts +0 -235
  167. package/dist/app-shell.js +0 -415
  168. package/dist/base.css +0 -51
  169. package/dist/calendar-picker.d.ts +0 -45
  170. package/dist/calendar-picker.js +0 -217
  171. package/dist/calendar.d.ts +0 -95
  172. package/dist/calendar.js +0 -194
  173. package/dist/complex-inputs.d.ts +0 -300
  174. package/dist/complex-inputs.js +0 -866
  175. package/dist/components.css +0 -4756
  176. package/dist/data-display.d.ts +0 -1094
  177. package/dist/data-display.js +0 -2341
  178. package/dist/dialog.d.ts +0 -26
  179. package/dist/dialog.js +0 -94
  180. package/dist/feedback.d.ts +0 -88
  181. package/dist/feedback.js +0 -281
  182. package/dist/forms.d.ts +0 -378
  183. package/dist/forms.js +0 -1019
  184. package/dist/icons-config.d.ts +0 -25
  185. package/dist/icons-config.js +0 -14
  186. package/dist/icons.d.ts +0 -41
  187. package/dist/icons.js +0 -185
  188. package/dist/index.d.ts +0 -31
  189. package/dist/index.js +0 -40
  190. package/dist/locale/en-US.d.ts +0 -31
  191. package/dist/locale/en-US.js +0 -20
  192. package/dist/locale/zh-CN.d.ts +0 -31
  193. package/dist/locale/zh-CN.js +0 -20
  194. package/dist/navigation.d.ts +0 -609
  195. package/dist/navigation.js +0 -1707
  196. package/dist/overlay.d.ts +0 -308
  197. package/dist/overlay.js +0 -974
  198. package/dist/page-state.d.ts +0 -23
  199. package/dist/page-state.js +0 -96
  200. package/dist/primitives.d.ts +0 -268
  201. package/dist/primitives.js +0 -764
  202. package/dist/theme-data.d.ts +0 -394
  203. package/dist/theme-data.js +0 -417
  204. package/dist/theme.d.ts +0 -97
  205. package/dist/theme.js +0 -301
  206. package/dist/tokens.css +0 -570
  207. package/dist/tree.d.ts +0 -144
  208. package/dist/tree.js +0 -475
  209. package/dist/virtual-list.d.ts +0 -190
  210. package/dist/virtual-list.js +0 -538
  211. package/dist/workbench.d.ts +0 -136
  212. package/dist/workbench.js +0 -156
package/src/utils.ts ADDED
@@ -0,0 +1,221 @@
1
+ import { effect } from '@vobs/reactivity'
2
+ import {
3
+ addEventListener,
4
+ createFragment,
5
+ createText,
6
+ insertBefore,
7
+ insertDynamic,
8
+ setAttribute,
9
+ setProperty,
10
+ setTextContent,
11
+ type VobsNode
12
+ } from '@vobs/vobs'
13
+ import type { VuiCommonProps } from './types'
14
+
15
+ type VuiProps = VuiCommonProps | object
16
+
17
+ export function classNames(...values: readonly (string | false | null | undefined)[]): string {
18
+ return values
19
+ .flatMap(value => typeof value === 'string' ? value.trim().split(/\s+/u) : [])
20
+ .filter(Boolean)
21
+ .join(' ')
22
+ }
23
+
24
+ export function readProp<T>(props: object, name: string, fallback: T): T {
25
+ const value = Reflect.get(props, name)
26
+ return (value === undefined ? fallback : value) as T
27
+ }
28
+
29
+ export function hasProp(props: object, name: string): boolean {
30
+ return name in props
31
+ }
32
+
33
+ export function bindClass(
34
+ root: Element,
35
+ props: VuiProps,
36
+ ...baseClasses: readonly (string | undefined)[]
37
+ ): void {
38
+ effect(() => {
39
+ setAttribute(root, 'class', classNames(
40
+ ...baseClasses,
41
+ readString(props, 'class'),
42
+ readString(props, 'className')
43
+ ))
44
+ })
45
+ }
46
+
47
+ export function bindClassList(
48
+ root: Element,
49
+ props: VuiProps,
50
+ readBaseClasses: () => readonly (string | undefined)[]
51
+ ): void {
52
+ effect(() => {
53
+ setAttribute(root, 'class', classNames(
54
+ ...readBaseClasses(),
55
+ readString(props, 'class'),
56
+ readString(props, 'className')
57
+ ))
58
+ })
59
+ }
60
+
61
+ export function bindCommonAttributes(
62
+ root: Element,
63
+ props: VuiProps,
64
+ skip: readonly string[] = [],
65
+ options: { readonly includeDataAria?: boolean } = {}
66
+ ): void {
67
+ const ignored = new Set(skip)
68
+ ignored.add('class')
69
+ ignored.add('className')
70
+ ignored.add('children')
71
+ ignored.add('style')
72
+
73
+ effect(() => {
74
+ const next = new Map<string, string>()
75
+ for (const name of Object.keys(props)) {
76
+ if (ignored.has(name) || name.startsWith('on')) continue
77
+ if (!isCommonAttribute(name)) continue
78
+ if (options.includeDataAria === false && (name.startsWith('aria-') || name.startsWith('data-'))) continue
79
+
80
+ const value = Reflect.get(props, name)
81
+ const normalized = normalizeAttribute(name, value)
82
+ if (normalized !== undefined) next.set(normalized.name, normalized.value)
83
+ }
84
+
85
+ const managed = getManagedAttributes(root)
86
+ for (const name of managed) {
87
+ if (!next.has(name)) removeAttribute(root, name)
88
+ }
89
+ for (const [name, value] of next) setAttribute(root, name, value)
90
+ setManagedAttributes(root, next.keys())
91
+ })
92
+ }
93
+
94
+ export function bindUserStyle(root: Element, props: VuiProps): void {
95
+ bindStyle(root, props, () => '')
96
+ }
97
+
98
+ export function bindStyle(root: Element, props: VuiProps, createStyle: () => string): void {
99
+ effect(() => {
100
+ const userStyle = readString(props, 'style')
101
+ const internalStyle = createStyle()
102
+ const style = [internalStyle, userStyle].filter(Boolean).join('; ')
103
+ if (style) setAttribute(root, 'style', style)
104
+ else removeAttribute(root, 'style')
105
+ })
106
+ }
107
+
108
+ export function bindTextContent(
109
+ node: Text,
110
+ read: () => unknown
111
+ ): void {
112
+ effect(() => {
113
+ setTextContent(node, String(read() ?? ''))
114
+ })
115
+ }
116
+
117
+ export function bindPropertyValue(
118
+ node: Element,
119
+ name: string,
120
+ read: () => unknown
121
+ ): void {
122
+ effect(() => setProperty(node, name, read()))
123
+ }
124
+
125
+ export function bindAttributeValue(
126
+ node: Element,
127
+ name: string,
128
+ read: () => unknown
129
+ ): void {
130
+ effect(() => {
131
+ const value = read()
132
+ if (value === undefined || value === null || value === false) removeAttribute(node, name)
133
+ else setAttribute(node, name, String(value))
134
+ })
135
+ }
136
+
137
+ export function listen(
138
+ root: Element,
139
+ event: string,
140
+ props: object,
141
+ propName: string,
142
+ disabled?: () => boolean
143
+ ): void {
144
+ addEventListener(root, event, (reason: Event) => {
145
+ if (disabled?.()) return
146
+ const handler = Reflect.get(props, propName)
147
+ if (typeof handler === 'function') handler(reason)
148
+ })
149
+ }
150
+
151
+ export function mountSlot(
152
+ parent: Node,
153
+ props: object,
154
+ propName: string
155
+ ): void {
156
+ insertDynamic(parent, null, () => resolveSlot(Reflect.get(props, propName)))
157
+ }
158
+
159
+ export function resolveSlot(value: unknown): VobsNode | null {
160
+ const resolved = typeof value === 'function' ? value() : value
161
+ if (resolved === undefined || resolved === null || resolved === false) return null
162
+ if (typeof resolved === 'string' || typeof resolved === 'number') return createText(String(resolved))
163
+ if (Array.isArray(resolved)) {
164
+ const children = resolved
165
+ return createFragment((parent, anchor) => {
166
+ for (const child of children) {
167
+ const node = resolveSlot(child)
168
+ if (node) insertBefore(parent, node, anchor)
169
+ }
170
+ })
171
+ }
172
+ return resolved as VobsNode
173
+ }
174
+
175
+ export function setOptionalAttribute(node: Element, name: string, value: unknown): void {
176
+ if (value === undefined || value === null || value === false || value === '') {
177
+ removeAttribute(node, name)
178
+ return
179
+ }
180
+ setAttribute(node, name, String(value))
181
+ }
182
+
183
+ export function setOptionalProperty(node: Element, name: string, value: unknown): void {
184
+ if (value === undefined || value === null) return
185
+ setProperty(node, name, value)
186
+ }
187
+
188
+ function readString(props: object, name: string): string | undefined {
189
+ const value = Reflect.get(props, name)
190
+ return typeof value === 'string' ? value : undefined
191
+ }
192
+
193
+ function isCommonAttribute(name: string): boolean {
194
+ return name === 'id'
195
+ || name === 'title'
196
+ || name === 'role'
197
+ || name === 'tabIndex'
198
+ || name.startsWith('aria-')
199
+ || name.startsWith('data-')
200
+ }
201
+
202
+ function normalizeAttribute(name: string, value: unknown): { name: string; value: string } | undefined {
203
+ if (value === undefined || value === null) return undefined
204
+ if (value === false && !name.startsWith('aria-') && !name.startsWith('data-')) return undefined
205
+ return { name: name === 'tabIndex' ? 'tabindex' : name, value: String(value) }
206
+ }
207
+
208
+ const managedAttributes = new WeakMap<object, Set<string>>()
209
+
210
+ function getManagedAttributes(node: Element): Set<string> {
211
+ return managedAttributes.get(node) ?? new Set<string>()
212
+ }
213
+
214
+ function setManagedAttributes(node: Element, names: IterableIterator<string>): void {
215
+ managedAttributes.set(node, new Set(names))
216
+ }
217
+
218
+ function removeAttribute(node: Element, name: string): void {
219
+ const candidate = node as Element & { removeAttribute?: (attribute: string) => void }
220
+ candidate.removeAttribute?.(name)
221
+ }
@@ -0,0 +1,137 @@
1
+ import {
2
+ addEventListener,
3
+ createElement,
4
+ createFragment,
5
+ createText,
6
+ insertBefore,
7
+ insertDynamic,
8
+ setAttribute,
9
+ type VobsNode
10
+ } from '@vobs/vobs'
11
+ import {
12
+ bindClassList,
13
+ bindCommonAttributes,
14
+ bindTextContent,
15
+ bindUserStyle,
16
+ hasProp,
17
+ mountSlot,
18
+ readProp,
19
+ setOptionalAttribute
20
+ } from './utils'
21
+ import type { VuiChildren, VuiCommonProps } from './types'
22
+
23
+ export interface WorkbenchTitlebarProps extends VuiCommonProps {
24
+ readonly mode?: string
25
+ readonly modeIcon?: VuiChildren
26
+ readonly project?: string
27
+ readonly projectIcon?: VuiChildren
28
+ readonly projectChevron?: VuiChildren
29
+ readonly actions?: VuiChildren
30
+ readonly windowControls?: boolean
31
+ readonly onWindowClose?: (event: MouseEvent) => void
32
+ readonly onWindowMinimize?: (event: MouseEvent) => void
33
+ readonly onWindowMaximize?: (event: MouseEvent) => void
34
+ readonly onProjectClick?: (event: MouseEvent) => void
35
+ }
36
+
37
+ export function WorkbenchTitlebar(props: WorkbenchTitlebarProps = {}): VobsNode {
38
+ const root = createElement('header')
39
+ const left = createElement('div')
40
+ const right = createElement('div')
41
+ bindClassList(root, props, () => ['vui-wbtitlebar'])
42
+ bindCommonAttributes(root, props, [
43
+ 'mode',
44
+ 'modeIcon',
45
+ 'project',
46
+ 'projectIcon',
47
+ 'projectChevron',
48
+ 'actions',
49
+ 'windowControls',
50
+ 'onWindowClose',
51
+ 'onWindowMinimize',
52
+ 'onWindowMaximize',
53
+ 'onProjectClick'
54
+ ])
55
+ bindUserStyle(root, props)
56
+ setAttribute(left, 'class', 'vui-wbtitlebar__left')
57
+ setAttribute(right, 'class', 'vui-wbtitlebar__right')
58
+ insertDynamic(left, null, () => createTitlebarLeft(props))
59
+ if (hasProp(props, 'actions')) mountSlot(right, props, 'actions')
60
+ if (hasProp(props, 'children')) mountSlot(right, props, 'children')
61
+ insertBefore(root, left, null)
62
+ insertBefore(root, right, null)
63
+ return root
64
+ }
65
+
66
+ function createTitlebarLeft(props: WorkbenchTitlebarProps): VobsNode {
67
+ return createFragment((parent, anchor) => {
68
+ if (readProp(props, 'windowControls', true)) {
69
+ const lights = createElement('div')
70
+ setAttribute(lights, 'class', 'vui-wbtitlebar__lights')
71
+ insertBefore(lights, createWindowLight('close', props, 'onWindowClose'), null)
72
+ insertBefore(lights, createWindowLight('min', props, 'onWindowMinimize'), null)
73
+ insertBefore(lights, createWindowLight('max', props, 'onWindowMaximize'), null)
74
+ insertBefore(parent, lights, anchor)
75
+ }
76
+
77
+ if (hasProp(props, 'mode')) {
78
+ const mode = createElement('span')
79
+ setAttribute(mode, 'class', 'vui-wbtitlebar__mode-chip')
80
+ if (hasProp(props, 'modeIcon')) {
81
+ const icon = createElement('span')
82
+ setAttribute(icon, 'class', 'vui-wbtitlebar__mode-icon')
83
+ mountSlot(icon, props, 'modeIcon')
84
+ insertBefore(mode, icon, null)
85
+ }
86
+ const text = createText('')
87
+ bindTextContent(text, () => readProp(props, 'mode', ''))
88
+ insertBefore(mode, text, null)
89
+ insertBefore(parent, mode, anchor)
90
+ }
91
+
92
+ if (hasProp(props, 'project')) insertBefore(parent, createProjectSelector(props), anchor)
93
+ })
94
+ }
95
+
96
+ function createWindowLight(
97
+ kind: 'close' | 'min' | 'max',
98
+ props: WorkbenchTitlebarProps,
99
+ handlerName: 'onWindowClose' | 'onWindowMinimize' | 'onWindowMaximize'
100
+ ): VobsNode {
101
+ const light = createElement('button')
102
+ setAttribute(light, 'class', `vui-wbtitlebar__light vui-wbtitlebar__light--${kind}`)
103
+ setAttribute(light, 'type', 'button')
104
+ setAttribute(light, 'aria-label', kind === 'close' ? 'Close window' : kind === 'min' ? 'Minimize window' : 'Maximize window')
105
+ addEventListener(light, 'click', event => {
106
+ const handler = readProp<unknown>(props, handlerName, undefined)
107
+ if (typeof handler === 'function') (handler as WorkbenchTitlebarProps[typeof handlerName])!(event as MouseEvent)
108
+ })
109
+ return light
110
+ }
111
+
112
+ function createProjectSelector(props: WorkbenchTitlebarProps): VobsNode {
113
+ const button = createElement('button')
114
+ setAttribute(button, 'class', 'vui-wbtitlebar__project-selector')
115
+ setAttribute(button, 'type', 'button')
116
+ setOptionalAttribute(button, 'aria-label', readProp(props, 'project', 'Project'))
117
+ if (hasProp(props, 'projectIcon')) {
118
+ const icon = createElement('span')
119
+ setAttribute(icon, 'class', 'vui-wbtitlebar__project-icon')
120
+ mountSlot(icon, props, 'projectIcon')
121
+ insertBefore(button, icon, null)
122
+ }
123
+ const project = createText('')
124
+ bindTextContent(project, () => readProp(props, 'project', ''))
125
+ insertBefore(button, project, null)
126
+ if (hasProp(props, 'projectChevron')) {
127
+ const chevron = createElement('span')
128
+ setAttribute(chevron, 'class', 'vui-wbtitlebar__project-chevron')
129
+ mountSlot(chevron, props, 'projectChevron')
130
+ insertBefore(button, chevron, null)
131
+ }
132
+ addEventListener(button, 'click', event => {
133
+ const handler = readProp<unknown>(props, 'onProjectClick', undefined)
134
+ if (typeof handler === 'function') (handler as WorkbenchTitlebarProps['onProjectClick'])!(event as MouseEvent)
135
+ })
136
+ return button
137
+ }
@@ -1,30 +0,0 @@
1
- /** @license MIT
2
- * Copyright (c) 2026 vobsjs
3
- * @vobs/ui
4
- */
5
- import { type Owner, type TemplateEnvironment } from '@vobs/runtime-dom';
6
- import type { AppShellDefinition, AppShellComponentScope, ResolvedAppShellState, ResolvedAppShellStatus } from './app-shell.js';
7
- export interface AppShellFrame {
8
- readonly root: HTMLDivElement;
9
- readonly brand: HTMLAnchorElement;
10
- readonly sidebar: HTMLElement;
11
- readonly main: HTMLElement;
12
- readonly primaryNav: HTMLElement;
13
- readonly utilityNav: HTMLElement;
14
- readonly pageFrame: HTMLElement;
15
- readonly pageHeader: HTMLElement;
16
- readonly pageToolbar: HTMLElement;
17
- readonly statusbar: HTMLElement;
18
- readonly statusIcon: HTMLElement;
19
- readonly statusText: HTMLSpanElement;
20
- readonly pageKicker: HTMLParagraphElement;
21
- readonly pageTitle: HTMLHeadingElement;
22
- readonly pageDescription: HTMLParagraphElement;
23
- readonly version: HTMLSpanElement;
24
- }
25
- export declare function createAppShellFrame(shell: AppShellDefinition, scope: AppShellComponentScope, environment: TemplateEnvironment): AppShellFrame;
26
- export declare function bindAppShellThemeControls(owner: Owner, frame: AppShellFrame, shell: AppShellDefinition, scope: AppShellComponentScope, environment: TemplateEnvironment): void;
27
- export declare function bindAppShellAccountMenu(owner: Owner, frame: AppShellFrame, shell: AppShellDefinition): void;
28
- export declare function bindAppShellActions(owner: Owner, frame: AppShellFrame, shell: AppShellDefinition): void;
29
- export declare function syncAppShellFrame(frame: AppShellFrame, shell: AppShellDefinition, state: ResolvedAppShellState, status: ResolvedAppShellStatus, scope: AppShellComponentScope, environment: TemplateEnvironment): void;
30
- export declare function bindAppShellLanguageMenu(owner: Owner, frame: AppShellFrame, shell: AppShellDefinition, scope: AppShellComponentScope, environment: TemplateEnvironment): void;