@opencode/ui 0.0.0-dev-20607 → 0.0.0-dev-20610

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/actions/button/button.tsx +1 -0
  3. package/src/actions/icon-button/icon-button.tsx +1 -0
  4. package/src/actions/split-button/split-button.tsx +3 -0
  5. package/src/components/animated-number.tsx +13 -0
  6. package/src/components/app-icon.tsx +2 -0
  7. package/src/components/card.tsx +21 -0
  8. package/src/components/collapsible.tsx +1 -0
  9. package/src/components/context-menu.tsx +34 -0
  10. package/src/components/dock-surface.tsx +3 -0
  11. package/src/components/file-icon.tsx +12 -0
  12. package/src/components/image-preview.tsx +1 -0
  13. package/src/components/list.tsx +32 -0
  14. package/src/components/motion-spring.tsx +5 -0
  15. package/src/components/popover.tsx +12 -0
  16. package/src/components/provider-icon.tsx +1 -0
  17. package/src/components/resize-handle.tsx +8 -0
  18. package/src/components/scroll-view.tsx +39 -0
  19. package/src/components/spinner.tsx +2 -0
  20. package/src/components/text-field.tsx +6 -0
  21. package/src/components/text-reveal.tsx +19 -0
  22. package/src/components/text-strikethrough.tsx +8 -0
  23. package/src/components/typewriter.tsx +4 -0
  24. package/src/context/dialog.tsx +12 -0
  25. package/src/context/file.tsx +1 -0
  26. package/src/context/helper.tsx +4 -0
  27. package/src/context/i18n.tsx +20 -0
  28. package/src/context/index.ts +3 -0
  29. package/src/context/marked-base.tsx +7 -0
  30. package/src/context/marked-parser.tsx +7 -0
  31. package/src/context/marked.tsx +3 -0
  32. package/src/context/worker-pool.tsx +2 -0
  33. package/src/data-display/avatar/avatar.tsx +4 -0
  34. package/src/data-display/badge/badge.tsx +1 -0
  35. package/src/data-display/diff-changes/diff-changes.tsx +2 -0
  36. package/src/data-display/keybind/keybind.tsx +1 -0
  37. package/src/data-display/line-comment/line-comment.tsx +22 -0
  38. package/src/data-display/project-avatar/project-avatar.tsx +10 -0
  39. package/src/feedback/loader/loader.tsx +1 -0
  40. package/src/feedback/toast/description.ts +1 -0
  41. package/src/forms/checkbox/checkbox.tsx +1 -0
  42. package/src/forms/field/field.tsx +8 -0
  43. package/src/forms/inline-input/inline-input.tsx +4 -0
  44. package/src/forms/radio/radio.tsx +2 -0
  45. package/src/forms/select/select.tsx +11 -0
  46. package/src/forms/switch/switch.tsx +1 -0
  47. package/src/forms/text-input/text-input.tsx +3 -0
  48. package/src/hooks/create-auto-scroll.tsx +34 -0
  49. package/src/hooks/index.ts +1 -0
  50. package/src/hooks/use-filtered-list.tsx +14 -0
  51. package/src/i18n/no.ts +1 -0
  52. package/src/layout/divider/divider.tsx +1 -0
  53. package/src/navigation/menu/menu.tsx +13 -0
  54. package/src/navigation/segmented-control/segmented-control.tsx +16 -0
  55. package/src/navigation/tab-state-indicator.tsx +1 -0
  56. package/src/navigation/tabs/tabs.tsx +18 -0
  57. package/src/overlays/dialog/dialog.tsx +3 -0
  58. package/src/overlays/tooltip/tooltip.tsx +15 -0
  59. package/src/storybook/scaffold.tsx +3 -0
  60. package/src/theme/color.ts +27 -0
  61. package/src/theme/context.tsx +58 -0
  62. package/src/theme/default-themes.ts +35 -0
  63. package/src/theme/index.ts +3 -0
  64. package/src/theme/loader.ts +11 -0
  65. package/src/theme/resolve.ts +28 -0
  66. package/src/theme/v2/foreground.ts +8 -0
  67. package/src/theme/v2/resolve.ts +10 -0
  68. package/src/typography/text-shimmer/text-shimmer.tsx +1 -0
@@ -53,10 +53,12 @@ export function createAutoScroll(options: AutoScrollOptions) {
53
53
 
54
54
  const isAuto = (el: HTMLElement) => {
55
55
  const a = auto
56
+
56
57
  if (!a) return false
57
58
 
58
59
  if (Date.now() - a.time > 1500) {
59
60
  auto = undefined
61
+
60
62
  return false
61
63
  }
62
64
 
@@ -65,10 +67,13 @@ export function createAutoScroll(options: AutoScrollOptions) {
65
67
 
66
68
  const scrollToBottomNow = (behavior: ScrollBehavior) => {
67
69
  const el = store.scrollRef
70
+
68
71
  if (!el) return
69
72
  markAuto(el)
73
+
70
74
  if (behavior === "smooth") {
71
75
  el.scrollTo({ top: el.scrollHeight, behavior })
76
+
72
77
  return
73
78
  }
74
79
 
@@ -82,13 +87,16 @@ export function createAutoScroll(options: AutoScrollOptions) {
82
87
  if (force && store.userScrolled) setStore("userScrolled", false)
83
88
 
84
89
  const el = store.scrollRef
90
+
85
91
  if (!el) return
86
92
 
87
93
  if (!force && store.userScrolled) return
88
94
 
89
95
  const distance = distanceFromBottom(el)
96
+
90
97
  if (distance < 2) {
91
98
  markAuto(el)
99
+
92
100
  return
93
101
  }
94
102
 
@@ -99,11 +107,15 @@ export function createAutoScroll(options: AutoScrollOptions) {
99
107
 
100
108
  const stop = () => {
101
109
  const el = store.scrollRef
110
+
102
111
  if (!el) return
112
+
103
113
  if (!canScroll(el)) {
104
114
  if (store.userScrolled) setStore("userScrolled", false)
115
+
105
116
  return
106
117
  }
118
+
107
119
  if (store.userScrolled) return
108
120
 
109
121
  setStore("userScrolled", true)
@@ -118,27 +130,32 @@ export function createAutoScroll(options: AutoScrollOptions) {
118
130
  const el = store.scrollRef
119
131
  const target = e.target instanceof Element ? e.target : undefined
120
132
  const nested = target?.closest("[data-scrollable]")
133
+
121
134
  if (el && nested && nested !== el) return
122
135
  stop()
123
136
  }
124
137
 
125
138
  const handleScroll = () => {
126
139
  const el = store.scrollRef
140
+
127
141
  if (!el) return
128
142
 
129
143
  if (!canScroll(el)) {
130
144
  if (store.userScrolled) setStore("userScrolled", false)
145
+
131
146
  return
132
147
  }
133
148
 
134
149
  if (distanceFromBottom(el) < threshold()) {
135
150
  if (store.userScrolled) setStore("userScrolled", false)
151
+
136
152
  return
137
153
  }
138
154
 
139
155
  // Ignore scroll events triggered by our own scrollToBottom calls.
140
156
  if (!store.userScrolled && isAuto(el)) {
141
157
  scrollToBottom(false)
158
+
142
159
  return
143
160
  }
144
161
 
@@ -148,6 +165,7 @@ export function createAutoScroll(options: AutoScrollOptions) {
148
165
  const handleInteraction = () => {
149
166
  if (!active()) return
150
167
  const selection = window.getSelection()
168
+
151
169
  if (selection && selection.toString().length > 0) {
152
170
  stop()
153
171
  }
@@ -158,11 +176,13 @@ export function createAutoScroll(options: AutoScrollOptions) {
158
176
 
159
177
  if (mode === "none") {
160
178
  el.style.overflowAnchor = "none"
179
+
161
180
  return
162
181
  }
163
182
 
164
183
  if (mode === "auto") {
165
184
  el.style.overflowAnchor = "auto"
185
+
166
186
  return
167
187
  }
168
188
 
@@ -173,11 +193,15 @@ export function createAutoScroll(options: AutoScrollOptions) {
173
193
  () => store.contentRef,
174
194
  () => {
175
195
  const el = store.scrollRef
196
+
176
197
  if (el && !canScroll(el)) {
177
198
  if (store.userScrolled) setStore("userScrolled", false)
199
+
178
200
  return
179
201
  }
202
+
180
203
  if (!active()) return
204
+
181
205
  if (store.userScrolled) return
182
206
  // ResizeObserver fires after layout, before paint.
183
207
  // Keep the bottom locked in the same frame to avoid visible
@@ -188,20 +212,26 @@ export function createAutoScroll(options: AutoScrollOptions) {
188
212
 
189
213
  createEffect(() => {
190
214
  const el = store.scrollRef
215
+
191
216
  if (!el) return
192
217
  const root = el.closest("main")
218
+
193
219
  if (!root) return
194
220
  let connected = el.isConnected
195
221
  const contains = (node: Node) => node === el || (node instanceof Element && node.contains(el))
222
+
196
223
  const observer = new MutationObserver((records) => {
197
224
  records.forEach((record) => {
198
225
  if ([...record.removedNodes].some(contains)) connected = false
226
+
199
227
  if (!record.addedNodes.length || ![...record.addedNodes].some(contains)) return
200
228
  const reattached = !connected
201
229
  connected = true
230
+
202
231
  if (reattached) scrollToBottom(false)
203
232
  })
204
233
  })
234
+
205
235
  observer.observe(root, { childList: true })
206
236
  onCleanup(() => observer.disconnect())
207
237
  })
@@ -209,11 +239,13 @@ export function createAutoScroll(options: AutoScrollOptions) {
209
239
  createEffect(
210
240
  on(options.working, (working: boolean) => {
211
241
  settling = false
242
+
212
243
  if (settleTimer) clearTimeout(settleTimer)
213
244
  settleTimer = undefined
214
245
 
215
246
  if (working) {
216
247
  if (!store.userScrolled) scrollToBottom(true)
248
+
217
249
  return
218
250
  }
219
251
 
@@ -229,6 +261,7 @@ export function createAutoScroll(options: AutoScrollOptions) {
229
261
  // update overflow anchoring once the element exists.
230
262
  store.userScrolled
231
263
  const el = store.scrollRef
264
+
232
265
  if (!el) return
233
266
  updateOverflowAnchor(el)
234
267
  })
@@ -237,6 +270,7 @@ export function createAutoScroll(options: AutoScrollOptions) {
237
270
 
238
271
  onCleanup(() => {
239
272
  if (settleTimer) clearTimeout(settleTimer)
273
+
240
274
  if (autoTimer) clearTimeout(autoTimer)
241
275
  })
242
276
 
@@ -1,2 +1,3 @@
1
1
  export * from "./use-filtered-list"
2
+
2
3
  export * from "./create-auto-scroll"
@@ -21,6 +21,7 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
21
21
  const [store, setStore] = createStore<{ filter: string }>({ filter: "" })
22
22
 
23
23
  type Group = { category: string; items: [T, ...T[]] }
24
+
24
25
  const empty: Group[] = []
25
26
 
26
27
  const [grouped, { refetch }] = createResource(
@@ -32,6 +33,7 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
32
33
  const query = filter ?? ""
33
34
  const needle = query.toLowerCase()
34
35
  const all = (await Promise.resolve(items)) || []
36
+
35
37
  const result = pipe(
36
38
  all,
37
39
  (x) => {
@@ -39,10 +41,12 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
39
41
  const skipFilter = props.skipFilter
40
42
  const filterable = skipFilter ? x.filter((item) => !skipFilter(item)) : x
41
43
  const skipped = skipFilter ? x.filter(skipFilter) : []
44
+
42
45
  const filtered =
43
46
  !props.filterKeys && Array.isArray(filterable) && filterable.every((e) => typeof e === "string")
44
47
  ? (fuzzysort.go(needle, filterable).map((x) => x.target) as T[])
45
48
  : fuzzysort.go(needle, filterable, { keys: props.filterKeys! }).map((x) => x.obj)
49
+
46
50
  return skipped.length ? [...filtered, ...skipped] : filtered
47
51
  },
48
52
  groupBy((x) => (props.groupBy ? props.groupBy(x) : "")),
@@ -50,6 +54,7 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
50
54
  map(([k, v]) => ({ category: k, items: props.sortBy ? v.sort(props.sortBy) : v })),
51
55
  (groups) => (props.sortGroupsBy ? groups.sort(props.sortGroupsBy) : groups),
52
56
  )
57
+
53
58
  return result
54
59
  },
55
60
  { initialValue: empty },
@@ -64,10 +69,13 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
64
69
 
65
70
  function initialActive() {
66
71
  if (props.noInitialSelection) return ""
72
+
67
73
  if (props.current) return props.key(props.current)
68
74
 
69
75
  const items = flat()
76
+
70
77
  if (items.length === 0) return ""
78
+
71
79
  return props.key(items[0])
72
80
  }
73
81
 
@@ -80,9 +88,12 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
80
88
  const reset = () => {
81
89
  if (props.noInitialSelection) {
82
90
  list.setActive("")
91
+
83
92
  return
84
93
  }
94
+
85
95
  const all = flat()
96
+
86
97
  if (all.length === 0) return
87
98
  list.setActive(props.key(all[0]))
88
99
  }
@@ -92,14 +103,17 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
92
103
  event.preventDefault()
93
104
  const selectedIndex = flat().findIndex((x) => props.key(x) === list.active())
94
105
  const selected = flat()[selectedIndex]
106
+
95
107
  if (selected) props.onSelect?.(selected, selectedIndex)
96
108
  } else if (event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey) {
97
109
  if (event.key === "n" || event.key === "p") {
98
110
  event.preventDefault()
111
+
99
112
  const navEvent = new KeyboardEvent("keydown", {
100
113
  key: event.key === "n" ? "ArrowDown" : "ArrowUp",
101
114
  bubbles: true,
102
115
  })
116
+
103
117
  list.onKeyDown(navEvent)
104
118
  }
105
119
  } else {
package/src/i18n/no.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { dict as en } from "./en"
2
+
2
3
  type Keys = keyof typeof en
3
4
 
4
5
  export const dict: Record<Keys, string> = {
@@ -5,6 +5,7 @@ export interface DividerProps extends ComponentProps<"div"> {}
5
5
 
6
6
  export function Divider(props: DividerProps) {
7
7
  const [local, rest] = splitProps(props, ["class", "classList"])
8
+
8
9
  return (
9
10
  <div
10
11
  {...rest}
@@ -47,7 +47,9 @@ const MenuAppearanceContext = createContext<{ appearance: Accessor<MenuAppearanc
47
47
 
48
48
  function useMenuContext() {
49
49
  const ctx = useContext(MenuAppearanceContext)
50
+
50
51
  if (!ctx) throw new Error("Menu components must be used inside Menu or Menu.Context")
52
+
51
53
  return ctx
52
54
  }
53
55
 
@@ -76,6 +78,7 @@ export interface MenuItemProps extends ComponentProps<typeof DropdownMenu.Item>
76
78
  function MenuItem(props: ParentProps<MenuItemProps>) {
77
79
  const ctx = useMenuContext()
78
80
  const [s, r] = splitProps(props, ["class", "classList", "children", "shortcut", "badge"])
81
+
79
82
  return (
80
83
  <DropdownMenu.Item
81
84
  {...r}
@@ -98,6 +101,7 @@ export interface MenuCheckboxItemProps extends ComponentProps<typeof DropdownMen
98
101
  function MenuCheckboxItem(props: ParentProps<MenuCheckboxItemProps>) {
99
102
  const ctx = useMenuContext()
100
103
  const [s, r] = splitProps(props, ["class", "classList", "children", "shortcut", "badge"])
104
+
101
105
  return (
102
106
  <DropdownMenu.CheckboxItem
103
107
  {...r}
@@ -128,6 +132,7 @@ export interface MenuRadioItemProps extends ComponentProps<typeof DropdownMenu.R
128
132
  function MenuRadioItem(props: ParentProps<MenuRadioItemProps>) {
129
133
  const ctx = useMenuContext()
130
134
  const [s, r] = splitProps(props, ["class", "classList", "children", "shortcut", "badge"])
135
+
131
136
  return (
132
137
  <DropdownMenu.RadioItem
133
138
  {...r}
@@ -178,6 +183,7 @@ function MenuSub(props: MenuSubProps) {
178
183
  function MenuSubTrigger(props: ParentProps<MenuSubTriggerProps>) {
179
184
  const ctx = useMenuContext()
180
185
  const [s, r] = splitProps(props, ["class", "classList", "children", "shortcut", "badge"])
186
+
181
187
  return (
182
188
  <DropdownMenu.SubTrigger
183
189
  {...r}
@@ -195,6 +201,7 @@ function MenuSubTrigger(props: ParentProps<MenuSubTriggerProps>) {
195
201
  function MenuSubContent(props: ComponentProps<typeof DropdownMenu.SubContent>) {
196
202
  const ctx = useMenuContext()
197
203
  const [s, r] = splitProps(props, ["class", "classList"])
204
+
198
205
  return (
199
206
  <DropdownMenu.SubContent
200
207
  {...r}
@@ -208,6 +215,7 @@ function MenuSubContent(props: ComponentProps<typeof DropdownMenu.SubContent>) {
208
215
  function MenuGroupLabel(props: ComponentProps<typeof DropdownMenu.GroupLabel>) {
209
216
  const ctx = useMenuContext()
210
217
  const [s, r] = splitProps(props, ["class", "classList"])
218
+
211
219
  return (
212
220
  <DropdownMenu.GroupLabel
213
221
  {...r}
@@ -221,6 +229,7 @@ function MenuGroupLabel(props: ComponentProps<typeof DropdownMenu.GroupLabel>) {
221
229
  function MenuSeparator(props: ComponentProps<typeof DropdownMenu.Separator>) {
222
230
  const ctx = useMenuContext()
223
231
  const [s, r] = splitProps(props, ["class", "classList"])
232
+
224
233
  return (
225
234
  <DropdownMenu.Separator
226
235
  {...r}
@@ -234,6 +243,7 @@ function MenuSeparator(props: ComponentProps<typeof DropdownMenu.Separator>) {
234
243
  function MenuContent(props: ComponentProps<typeof DropdownMenu.Content>) {
235
244
  const ctx = useMenuContext()
236
245
  const [s, r] = splitProps(props, ["class", "classList"])
246
+
237
247
  return (
238
248
  <DropdownMenu.Content
239
249
  {...r}
@@ -251,6 +261,7 @@ export interface MenuProps extends ComponentProps<typeof DropdownMenu> {
251
261
  function MenuRoot(props: MenuProps) {
252
262
  const [local, rest] = splitProps(props, ["appearance", "children"])
253
263
  const appearance = () => local.appearance ?? "compact"
264
+
254
265
  return (
255
266
  <MenuAppearanceContext.Provider value={{ appearance }}>
256
267
  <DropdownMenu {...rest}>{local.children}</DropdownMenu>
@@ -265,6 +276,7 @@ export interface MenuContextProps extends ComponentProps<typeof ContextMenu> {
265
276
  function MenuContextRoot(props: MenuContextProps) {
266
277
  const [local, rest] = splitProps(props, ["appearance", "children"])
267
278
  const appearance = () => local.appearance ?? "compact"
279
+
268
280
  return (
269
281
  <MenuAppearanceContext.Provider value={{ appearance }}>
270
282
  <ContextMenu {...rest}>{local.children}</ContextMenu>
@@ -275,6 +287,7 @@ function MenuContextRoot(props: MenuContextProps) {
275
287
  function MenuContextContent(props: ComponentProps<typeof ContextMenu.Content>) {
276
288
  const ctx = useMenuContext()
277
289
  const [s, r] = splitProps(props, ["class", "classList"])
290
+
278
291
  return (
279
292
  <ContextMenu.Content
280
293
  {...r}
@@ -26,7 +26,9 @@ const SegmentedControlContext = createContext<SegmentedControlContextValue>()
26
26
 
27
27
  function useSegmentedControlContext() {
28
28
  const ctx = useContext(SegmentedControlContext)
29
+
29
30
  if (!ctx) throw new Error("SegmentedControlItem must be used inside SegmentedControl")
31
+
30
32
  return ctx
31
33
  }
32
34
 
@@ -45,6 +47,7 @@ export type SegmentedControlProps = Omit<ComponentProps<"div">, "onChange"> &
45
47
  export function SegmentedControl(props: SegmentedControlProps) {
46
48
  const isControlled = createMemo(() => Object.hasOwn(props as object, "value"))
47
49
  const merged = mergeProps({ allowDeselect: false, disabled: false }, props)
50
+
48
51
  const [local, rest] = splitProps(merged, [
49
52
  "class",
50
53
  "classList",
@@ -77,10 +80,13 @@ export function SegmentedControl(props: SegmentedControlProps) {
77
80
 
78
81
  const focusNext = (from: HTMLButtonElement, direction: 1 | -1) => {
79
82
  const root = from.closest(`[data-slot="segmented-control-v2"]`)
83
+
80
84
  if (!root) return
85
+
81
86
  const buttons = Array.from(
82
87
  root.querySelectorAll<HTMLButtonElement>(`button[data-slot="segmented-control-v2-item"]`),
83
88
  ).filter((b) => !b.disabled)
89
+
84
90
  const i = buttons.indexOf(from)
85
91
  const next = buttons[i + direction]
86
92
  next?.focus()
@@ -96,6 +102,7 @@ export function SegmentedControl(props: SegmentedControlProps) {
96
102
 
97
103
  const assignRef = (el: HTMLDivElement | undefined) => {
98
104
  const r = local.ref
105
+
99
106
  if (typeof r === "function") (r as (el: HTMLDivElement | undefined) => void)(el)
100
107
  else if (r != null && typeof r === "object" && "value" in r) (r as { value: HTMLDivElement | undefined }).value = el
101
108
  }
@@ -134,6 +141,7 @@ function invokeButtonHandler<E extends Event>(
134
141
 
135
142
  export function SegmentedControlItem(props: SegmentedControlItemProps) {
136
143
  const merged = mergeProps({ disabled: false }, props)
144
+
137
145
  const [local, rest] = splitProps(merged, [
138
146
  "class",
139
147
  "classList",
@@ -143,6 +151,7 @@ export function SegmentedControlItem(props: SegmentedControlItemProps) {
143
151
  "onClick",
144
152
  "onKeyDown",
145
153
  ])
154
+
146
155
  const ctx = useSegmentedControlContext()
147
156
 
148
157
  const pressed = createMemo(() => ctx.selected() === local.value)
@@ -150,13 +159,16 @@ export function SegmentedControlItem(props: SegmentedControlItemProps) {
150
159
 
151
160
  const onClick: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent> = (e) => {
152
161
  invokeButtonHandler(local.onClick, e)
162
+
153
163
  if (e.defaultPrevented || disabled()) return
164
+
154
165
  if (pressed()) ctx.clearIfAllowed(local.value)
155
166
  else ctx.select(local.value)
156
167
  }
157
168
 
158
169
  const onKeyDown: JSX.EventHandlerUnion<HTMLButtonElement, KeyboardEvent> = (e) => {
159
170
  invokeButtonHandler(local.onKeyDown, e)
171
+
160
172
  if (e.defaultPrevented || disabled()) return
161
173
  const t = e.currentTarget
162
174
  const horizontal = t.closest(`[data-slot="segmented-control-v2"]`)?.matches(":dir(rtl)") ? -1 : 1
@@ -173,16 +185,20 @@ export function SegmentedControlItem(props: SegmentedControlItemProps) {
173
185
  else if (e.key === "Home") {
174
186
  e.preventDefault()
175
187
  const root = t.closest(`[data-slot="segmented-control-v2"]`)
188
+
176
189
  const first = root?.querySelector<HTMLButtonElement>(
177
190
  `button[data-slot="segmented-control-v2-item"]:not(:disabled)`,
178
191
  )
192
+
179
193
  first?.focus()
180
194
  } else if (e.key === "End") {
181
195
  e.preventDefault()
182
196
  const root = t.closest(`[data-slot="segmented-control-v2"]`)
197
+
183
198
  const buttons = root?.querySelectorAll<HTMLButtonElement>(
184
199
  `button[data-slot="segmented-control-v2-item"]:not(:disabled)`,
185
200
  )
201
+
186
202
  const last = buttons?.[buttons.length - 1]
187
203
  last?.focus()
188
204
  }
@@ -2,6 +2,7 @@ import { splitProps, type ComponentProps } from "solid-js"
2
2
 
3
3
  export function TabStateIndicator(props: ComponentProps<"svg">) {
4
4
  const [local, rest] = splitProps(props, ["class", "classList", "width", "height"])
5
+
5
6
  return (
6
7
  <svg
7
8
  {...rest}
@@ -10,7 +10,9 @@ export interface TabsProps extends ComponentProps<typeof Root> {
10
10
  variant?: TabsVariant
11
11
  orientation?: "horizontal" | "vertical"
12
12
  }
13
+
13
14
  export interface TabsListProps extends ComponentProps<typeof List> {}
15
+
14
16
  export interface TabsTriggerProps extends ComponentProps<typeof Trigger> {
15
17
  classes?: { button?: string }
16
18
  closeButton?: JSX.Element
@@ -18,7 +20,9 @@ export interface TabsTriggerProps extends ComponentProps<typeof Trigger> {
18
20
  onMiddleClick?: () => void
19
21
  subtext?: JSX.Element | string
20
22
  }
23
+
21
24
  export interface TabsCloseButtonProps extends ComponentProps<"button"> {}
25
+
22
26
  export interface TabsContentProps extends ComponentProps<typeof Content> {}
23
27
 
24
28
  const TabsContext = createContext<{ current: () => boolean }>({ current: () => false })
@@ -27,12 +31,17 @@ function TabsRoot(props: TabsProps) {
27
31
  const [local, rest] = splitProps(props, ["class", "classList", "variant", "orientation", "children"])
28
32
  const variant = () => local.variant ?? "panel"
29
33
  const current = () => variant() === "line" || variant() === "pill" || variant() === "settings"
34
+
30
35
  const dataVariant = () => {
31
36
  if (variant() === "panel" || variant() === "line") return "normal"
37
+
32
38
  if (variant() === "underline") return "alt"
39
+
33
40
  if (variant() === "surface") return "pill"
41
+
34
42
  return variant()
35
43
  }
44
+
36
45
  return (
37
46
  <TabsContext.Provider value={{ current }}>
38
47
  <Root
@@ -55,6 +64,7 @@ function TabsRoot(props: TabsProps) {
55
64
  function TabsList(props: TabsListProps) {
56
65
  const ctx = useContext(TabsContext)
57
66
  const [local, rest] = splitProps(props, ["class", "classList"])
67
+
58
68
  return (
59
69
  <List
60
70
  {...rest}
@@ -69,6 +79,7 @@ function TabsList(props: TabsListProps) {
69
79
 
70
80
  function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
71
81
  const ctx = useContext(TabsContext)
82
+
72
83
  const [local, rest] = splitProps(props, [
73
84
  "class",
74
85
  "classList",
@@ -80,9 +91,11 @@ function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
80
91
  "subtext",
81
92
  "dir",
82
93
  ])
94
+
83
95
  const wrapperSlot = () => (ctx.current() ? "tabs-v2-trigger-wrapper" : "tabs-trigger-wrapper")
84
96
  const triggerSlot = () => (ctx.current() ? "tabs-v2-trigger" : "tabs-trigger")
85
97
  const closeSlot = () => (ctx.current() ? "tabs-v2-trigger-close-button" : "tabs-trigger-close-button")
98
+
86
99
  return (
87
100
  <div
88
101
  data-slot={wrapperSlot()}
@@ -135,6 +148,7 @@ function TabsTrigger(props: ParentProps<TabsTriggerProps>) {
135
148
  function TabsCloseButton(props: TabsCloseButtonProps) {
136
149
  const i18n = useI18n()
137
150
  const [local, rest] = splitProps(props, ["class", "classList", "onClick", "onPointerDown", "aria-label"])
151
+
138
152
  return (
139
153
  <button
140
154
  type="button"
@@ -147,11 +161,13 @@ function TabsCloseButton(props: TabsCloseButtonProps) {
147
161
  }}
148
162
  onPointerDown={(event) => {
149
163
  event.stopPropagation()
164
+
150
165
  if (typeof local.onPointerDown === "function") local.onPointerDown(event)
151
166
  }}
152
167
  onClick={(event) => {
153
168
  event.preventDefault()
154
169
  event.stopPropagation()
170
+
155
171
  if (typeof local.onClick === "function") local.onClick(event)
156
172
  }}
157
173
  >
@@ -166,6 +182,7 @@ function TabsCloseButton(props: TabsCloseButtonProps) {
166
182
  function TabsContent(props: ParentProps<TabsContentProps>) {
167
183
  const ctx = useContext(TabsContext)
168
184
  const [local, rest] = splitProps(props, ["class", "classList", "children"])
185
+
169
186
  return (
170
187
  <Content
171
188
  {...rest}
@@ -182,6 +199,7 @@ function TabsContent(props: ParentProps<TabsContentProps>) {
182
199
 
183
200
  const TabsSectionTitle: Component<ParentProps> = (props) => {
184
201
  const ctx = useContext(TabsContext)
202
+
185
203
  return <div data-slot={ctx.current() ? "tabs-v2-section-title" : "tabs-section-title"}>{props.children}</div>
186
204
  }
187
205
 
@@ -31,6 +31,7 @@ export function DialogFooter(props: ParentProps) {
31
31
 
32
32
  export function DialogBody(props: ParentProps & { class?: ComponentProps<"div">["class"] }) {
33
33
  const [local] = splitProps(props, ["class", "children"])
34
+
34
35
  return (
35
36
  <div data-slot="dialog-body" class={local.class}>
36
37
  {local.children}
@@ -96,6 +97,7 @@ export function Dialog(props: DialogProps) {
96
97
  "onCloseAutoFocus",
97
98
  "preventBackdropDismiss",
98
99
  ])
100
+
99
101
  const layer = useDialogLayer()
100
102
  createEffect(() => layer?.setBackdropDismiss(!local.preventBackdropDismiss))
101
103
 
@@ -120,6 +122,7 @@ export function Dialog(props: DialogProps) {
120
122
  onOpenAutoFocus={(e) => {
121
123
  const target = e.currentTarget as HTMLElement | null
122
124
  const autofocusEl = target?.querySelector("[autofocus]") as HTMLElement | null
125
+
123
126
  if (autofocusEl) {
124
127
  e.preventDefault()
125
128
  autofocusEl.focus({ preventScroll: true })
@@ -17,11 +17,13 @@ export interface TooltipProps extends ComponentProps<typeof Root> {
17
17
 
18
18
  export function Tooltip(props: TooltipProps) {
19
19
  let ref: HTMLDivElement | undefined
20
+
20
21
  const [state, setState] = createStore({
21
22
  open: false,
22
23
  block: false,
23
24
  expand: false,
24
25
  })
26
+
25
27
  const [local, others] = splitProps(props, [
26
28
  "children",
27
29
  "appearance",
@@ -40,13 +42,17 @@ export function Tooltip(props: TooltipProps) {
40
42
 
41
43
  const inside = () => {
42
44
  const active = document.activeElement
45
+
43
46
  if (!ref || !active) return false
47
+
44
48
  return ref.contains(active)
45
49
  }
46
50
 
47
51
  const drop = (expand = state.expand) => {
48
52
  if (expand || !state.block) return
53
+
49
54
  if (ref?.matches(":hover")) return
55
+
50
56
  if (inside()) return
51
57
  setState("block", false)
52
58
  }
@@ -54,11 +60,14 @@ export function Tooltip(props: TooltipProps) {
54
60
  const sync = () => {
55
61
  const expand = !!ref?.querySelector('[aria-expanded="true"], [data-expanded]')
56
62
  setState("expand", expand)
63
+
57
64
  if (expand) {
58
65
  setState("block", true)
59
66
  close()
67
+
60
68
  return
61
69
  }
70
+
62
71
  drop(expand)
63
72
  }
64
73
 
@@ -101,11 +110,15 @@ export function Tooltip(props: TooltipProps) {
101
110
  open={controlled() ? local.forceOpen : state.open}
102
111
  onOpenChange={(open) => {
103
112
  if (controlled()) return
113
+
104
114
  if (state.block && open) return
115
+
105
116
  if (justClickedTrigger) {
106
117
  justClickedTrigger = false
118
+
107
119
  return
108
120
  }
121
+
109
122
  setState("open", open)
110
123
  }}
111
124
  >
@@ -129,6 +142,7 @@ export function Tooltip(props: TooltipProps) {
129
142
  <Content
130
143
  ref={(el) => {
131
144
  const theme = ref?.closest("[data-theme]")?.getAttribute("data-theme")
145
+
132
146
  if (theme) el.setAttribute("data-theme", theme)
133
147
  }}
134
148
  data-component="tooltip-v2"
@@ -141,6 +155,7 @@ export function Tooltip(props: TooltipProps) {
141
155
  if (ref === e.target || (e.target instanceof Node && ref?.contains(e.target))) {
142
156
  justClickedTrigger = true
143
157
  }
158
+
144
159
  e.preventDefault()
145
160
  }}
146
161
  >