Skip to content

Commit df58189

Browse files
authored
Selection final (#1751)
* final tuning to single selection * coderabbit
1 parent 8eed924 commit df58189

File tree

3 files changed

+29
-21
lines changed

3 files changed

+29
-21
lines changed

src/lib/tabs/TabItem.svelte

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,21 @@
1616
const { active, inactive } = $derived(tabs({ tabStyle: compoTabStyle, hasDivider: true }));
1717
1818
// Generate a unique ID for this tab button
19-
const tabId = `tab-${Math.random().toString(36).substring(2)}`;
19+
const tabId = $props.id();
20+
const self = $state({ id: `tab-${tabId}`, snippet: children });
2021
21-
const updateSingleSelection = useSingleSelection<SelectedTab>((value) => (open = value.id === tabId));
22+
const updateSingleSelection = useSingleSelection<SelectedTab>((value) => (open = value?.id === self.id));
2223
2324
$effect(() => {
2425
// monitor if open changes out side of that component
25-
updateSingleSelection?.(open, { snippet: children, id: tabId });
26+
updateSingleSelection(open, self);
2627
});
2728
2829
const { base, button } = $derived(tabItem({ open, disabled }));
2930
</script>
3031

3132
<li {...restProps} class={base({ class: clsx(theme?.base, className) })} role="presentation">
32-
<button type="button" onclick={() => (open = true)} role="tab" id={tabId} aria-controls={ctx.panelId} aria-selected={open} {disabled} class={button({ class: clsx(open ? (activeClass ?? active()) : (inactiveClass ?? inactive()), theme?.button, classes?.button) })}>
33+
<button type="button" onclick={() => (open = true)} role="tab" id={self.id} aria-controls={ctx.panelId} aria-selected={open} {disabled} class={button({ class: clsx(open ? (activeClass ?? active()) : (inactiveClass ?? inactive()), theme?.button, classes?.button) })}>
3334
{#if titleSlot}
3435
{@render titleSlot()}
3536
{:else}

src/lib/tabs/Tabs.svelte

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,10 @@
1717
const { base, content, divider: dividerClass } = $derived(tabs({ tabStyle, hasDivider: divider }));
1818
1919
// Generate a unique ID for the tab panel
20-
const panelId = `tab-panel-${Math.random().toString(36).substring(2)}`;
20+
const uuid = $props.id();
21+
const panelId = `tab-panel-${uuid}`;
2122
22-
const ctx: TabCtxType = $state({
23-
tabStyle,
24-
selected: undefined,
25-
panelId // Add panelId to the context
26-
});
23+
const ctx: TabCtxType = $state({ tabStyle, panelId });
2724
2825
let dividerBool = $derived(["full", "pill"].includes(tabStyle) ? false : divider);
2926
@@ -32,7 +29,7 @@
3229
createSingleSelectionContext<SelectedTab>();
3330
3431
let selected: SelectedTab = $state({});
35-
useSingleSelection<SelectedTab>((v) => (selected = v));
32+
useSingleSelection<SelectedTab>((v) => (selected = v ?? {}));
3633
</script>
3734

3835
<ul role="tablist" {...restProps} class={base({ class: clsx(theme?.base, className ?? ulClass) })}>
Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { getContext, setContext } from "svelte";
1+
import { getContext, setContext, untrack } from "svelte";
22

33
/**
44
* @template T
55
* @typedef {Object} SingleSelectionContext
6-
* @property {T=} value
6+
* @property {T|null} value
77
*/
88

99
/** @type {symbol} */
@@ -12,11 +12,16 @@ const SINGLE_SELECTION_KEY = Symbol("singleton");
1212
/**
1313
* @template T
1414
* @param {boolean} [nonReactive=false] - use a non-reactive placeholder to allow multiple selection and keep context shallow
15-
* @returns {SingleSelectionContext<T>|null}
15+
* @returns {SingleSelectionContext<T>}
1616
*/
1717
export function createSingleSelectionContext(nonReactive = false) {
18-
const context = $state({ value: undefined });
19-
return setContext(SINGLE_SELECTION_KEY, nonReactive ? null : context);
18+
if (nonReactive) {
19+
const context = { value: null };
20+
return setContext(SINGLE_SELECTION_KEY, context);
21+
} else {
22+
const context = $state({ value: null });
23+
return setContext(SINGLE_SELECTION_KEY, context);
24+
}
2025
}
2126

2227
/**
@@ -27,22 +32,27 @@ export function createSingleSelectionContext(nonReactive = false) {
2732
* @returns {SingleSelectionContext<T>}
2833
*/
2934
function setSelected(context, open, value) {
30-
if (open) context.value = value;
31-
else if (context.value === value) context.value = undefined;
35+
if (open) context.value = value ?? null;
36+
else if (context.value === value) context.value = null;
3237

3338
return context;
3439
}
3540

3641
/**
3742
* @template T
38-
* @param {(value: T) => void} callback
43+
* @param {(value: T|null) => void} callback
3944
* @returns {(open: boolean, v?: T) => SingleSelectionContext<T>}
4045
*/
4146
export function useSingleSelection(callback) {
4247
const context = getContext(SINGLE_SELECTION_KEY) ?? createSingleSelectionContext(false);
4348

49+
let initialized = $state(false);
4450
$effect(() => {
45-
if (context.value !== undefined) callback(context.value);
51+
if (initialized)
52+
// if (context.value !== null)
53+
callback(context.value);
54+
initialized = true;
4655
});
47-
return (open, v) => setSelected(context, open, v);
56+
57+
return (open, v) => untrack(() => setSelected(context, open, v));
4858
}

0 commit comments

Comments
 (0)