@rokkit/ui 1.0.0-next.149 → 1.0.0-next.151

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rokkit/ui",
3
- "version": "1.0.0-next.149",
3
+ "version": "1.0.0-next.151",
4
4
  "description": "Data driven UI components for Rokkit applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { marked } from 'marked'
3
- import type { Token } from 'marked'
3
+ import type { Token, TokensList } from 'marked'
4
4
  import DOMPurify from 'isomorphic-dompurify'
5
5
  import type { MarkdownPlugin } from './markdown-plugin.js'
6
6
 
@@ -18,8 +18,8 @@
18
18
  const tokens = $derived(marked.lexer(markdown))
19
19
 
20
20
  function tokenToSafeHtml(token: Token): string {
21
- const tokenList = Object.assign([token], { links: (tokens as any).links ?? {} })
22
- const raw = marked.parser(tokenList as any)
21
+ const tokenList = Object.assign([token], { links: (tokens as TokensList).links ?? {} }) as TokensList
22
+ const raw = marked.parser(tokenList)
23
23
  return DOMPurify.sanitize(raw)
24
24
  }
25
25
  </script>
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte'
2
3
  import { SvelteSet } from 'svelte/reactivity'
3
4
  import { alerts } from '@rokkit/states'
4
5
  import Message from './Message.svelte'
@@ -18,17 +19,15 @@
18
19
 
19
20
  const { position = 'top-right', class: className = '' }: AlertListProps = $props()
20
21
 
21
- let el: HTMLElement | undefined = $state()
22
22
  const dismissing = new SvelteSet<string>()
23
23
 
24
24
  // Portal to document.body so position:fixed is relative to the viewport,
25
25
  // not clipped by any overflow:auto ancestor (e.g. the docs main column).
26
- $effect(() => {
27
- if (!el) return
28
-
29
- document.body.appendChild(el)
30
- return () => el?.remove()
31
- })
26
+
27
+ function mountPortal(node: HTMLElement) {
28
+ document.body.appendChild(node)
29
+ return { destroy: () => node.remove() }
30
+ }
32
31
 
33
32
  function startDismiss(id: string) {
34
33
  dismissing.add(id)
@@ -42,7 +41,7 @@
42
41
  }
43
42
  </script>
44
43
 
45
- <div bind:this={el} data-alert-list data-position={position} class={className || undefined}>
44
+ <div use:mountPortal data-alert-list data-position={position} class={className || undefined}>
46
45
  {#each alerts.current as alert (alert.id)}
47
46
  <div
48
47
  data-dismissing={dismissing.has(alert.id) || undefined}
@@ -52,7 +51,7 @@
52
51
  type={alert.type as 'error' | 'info' | 'success' | 'warning'}
53
52
  text={alert.text}
54
53
  dismissible={alert.dismissible}
55
- actions={alert.actions as any}
54
+ actions={alert.actions as Snippet}
56
55
  ondismiss={() => startDismiss(alert.id)}
57
56
  />
58
57
  </div>
@@ -86,28 +86,34 @@
86
86
  // collapsible=false: groups are fixed section headers — always expanded.
87
87
  // collapsible=true: only expand the ancestor group of the active value;
88
88
  // all other groups start collapsed (user can toggle them).
89
- $effect(() => {
90
- if (!collapsible) {
91
- for (const [, proxy] of wrapper.lookup) {
92
- if (proxy.hasChildren) proxy.expanded = true
93
- }
94
- } else {
95
- let activeKey: string | null = null
96
- for (const [key, proxy] of wrapper.lookup) {
97
- if (proxy.value === value) {
98
- activeKey = key
99
- break
100
- }
101
- }
102
- for (const [key, proxy] of wrapper.lookup) {
103
- if (proxy.hasChildren) {
104
- proxy.expanded =
105
- activeKey !== null &&
106
- (activeKey === key || activeKey.startsWith(`${key }-`))
107
- }
108
- }
89
+ function expandAllGroups() {
90
+ for (const [, proxy] of wrapper.lookup) {
91
+ if (proxy.hasChildren) proxy.expanded = true
109
92
  }
110
- })
93
+ }
94
+
95
+ function findActiveKey(): string | null {
96
+ for (const [key, proxy] of wrapper.lookup) {
97
+ if (proxy.value === value) return key
98
+ }
99
+ return null
100
+ }
101
+
102
+ function expandAncestorGroups(activeKey: string | null) {
103
+ for (const [key, proxy] of wrapper.lookup) {
104
+ if (!proxy.hasChildren) continue
105
+ proxy.expanded =
106
+ activeKey !== null &&
107
+ (activeKey === key || activeKey.startsWith(`${key }-`))
108
+ }
109
+ }
110
+
111
+ function syncExpandedGroups() {
112
+ if (!collapsible) { expandAllGroups(); return }
113
+ expandAncestorGroups(findActiveKey())
114
+ }
115
+
116
+ $effect(syncExpandedGroups)
111
117
 
112
118
  // ─── Sync external value → focused key ────────────────────────────────────
113
119
 
@@ -38,19 +38,25 @@
38
38
  return proxy.value === value
39
39
  }
40
40
 
41
+ function toggleMultiValue(extracted, original) {
42
+ const arr = Array.isArray(value) ? [...value] : []
43
+ const idx = arr.indexOf(extracted)
44
+ if (idx >= 0) arr.splice(idx, 1)
45
+ else arr.push(extracted)
46
+ value = arr
47
+ onchange?.(value, original)
48
+ }
49
+
50
+ function selectSingleValue(extracted, original) {
51
+ if (extracted === value) return
52
+ value = extracted
53
+ onchange?.(extracted, original)
54
+ }
55
+
41
56
  function handleSelect(extracted, proxy) {
42
57
  if (proxy.disabled || disabled) return
43
- if (multiple) {
44
- const arr = Array.isArray(value) ? [...value] : []
45
- const idx = arr.indexOf(extracted)
46
- if (idx >= 0) arr.splice(idx, 1)
47
- else arr.push(extracted)
48
- value = arr
49
- onchange?.(value, proxy.original)
50
- } else if (extracted !== value) {
51
- value = extracted
52
- onchange?.(extracted, proxy.original)
53
- }
58
+ if (multiple) toggleMultiValue(extracted, proxy.original)
59
+ else selectSingleValue(extracted, proxy.original)
54
60
  }
55
61
  </script>
56
62