Skip to content

Commit

Permalink
fix: next attrs/derived
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte committed Apr 18, 2024
1 parent 71456ce commit 7f63a91
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 413 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.2.2",
"prettier-plugin-tailwindcss": "0.5.13",
"svelte": "5.0.0-next.107",
"svelte": "5.0.0-next.108",
"svelte-eslint-parser": "^0.34.1",
"wrangler": "^3.44.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/bits-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"jsdom": "^24.0.0",
"publint": "^0.2.7",
"resize-observer-polyfill": "^1.5.1",
"svelte": "5.0.0-next.107",
"svelte": "5.0.0-next.108",
"svelte-check": "^3.6.9",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
Expand Down
139 changes: 56 additions & 83 deletions packages/bits-ui/src/lib/bits/accordion/accordion.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
type EventCallback,
type ReadonlyBox,
type ReadonlyBoxedValues,
boxedState,
composeHandlers,
getAriaDisabled,
getAriaExpanded,
Expand All @@ -28,9 +27,13 @@ type AccordionBaseStateProps = ReadonlyBoxedValues<{
}>;

class AccordionBaseState {
id: ReadonlyBox<string>;
id = undefined as unknown as ReadonlyBox<string>;
node: Box<HTMLElement | null>;
disabled: ReadonlyBox<boolean>;
props = $derived({
id: this.id.value,
"data-accordion-root": "",
} as const);

constructor(props: AccordionBaseStateProps) {
this.id = props.id;
Expand All @@ -45,13 +48,6 @@ class AccordionBaseState {
this.node.value.querySelectorAll<HTMLElement>("[data-accordion-trigger]")
).filter((el) => !el.dataset.disabled);
}

get props() {
return {
id: this.id.value,
"data-accordion-root": "",
} as const;
}
}

/**
Expand Down Expand Up @@ -118,38 +114,25 @@ type AccordionItemStateProps = ReadonlyBoxedValues<{
};

export class AccordionItemState {
#value: ReadonlyBox<string>;
disabled: ReadonlyBox<boolean>;
root: AccordionState;
value = undefined as unknown as ReadonlyBox<string>;
disabled = undefined as unknown as ReadonlyBox<boolean>;
root = undefined as unknown as AccordionState;
isSelected = $derived(this.root.includesItem(this.value.value));
isDisabled = $derived(this.disabled.value || this.root.disabled.value);
props = $derived({
"data-accordion-item": "",
"data-state": getDataOpenClosed(this.isSelected),
"data-disabled": getDataDisabled(this.isDisabled),
} as const);

constructor(props: AccordionItemStateProps) {
this.#value = props.value;
this.value = props.value;
this.disabled = props.disabled;
this.root = props.rootState;
}

get value() {
return this.#value.value;
}

get isSelected() {
return this.root.includesItem(this.value);
}

get isDisabled() {
return this.disabled.value || this.root.disabled.value;
}

updateValue() {
this.root.toggleItem(this.value);
}

get props() {
return {
"data-accordion-item": "",
"data-state": getDataOpenClosed(this.isSelected),
"data-disabled": getDataDisabled(this.isDisabled),
} as const;
this.root.toggleItem(this.value.value);
}

createTrigger(props: AccordionTriggerStateProps) {
Expand All @@ -173,13 +156,29 @@ type AccordionTriggerStateProps = ReadonlyBoxedValues<{
}>;

class AccordionTriggerState {
#disabled: ReadonlyBox<boolean>;
#id: ReadonlyBox<string>;
#disabled = undefined as unknown as ReadonlyBox<boolean>;
#id = undefined as unknown as ReadonlyBox<string>;
#node: Box<HTMLElement | null>;
#root: AccordionState;
#itemState: AccordionItemState;
#composedClick: EventCallback<MouseEvent>;
#composedKeydown: EventCallback<KeyboardEvent>;
#root = undefined as unknown as AccordionState;
#itemState = undefined as unknown as AccordionItemState;
#composedClick = undefined as unknown as EventCallback<MouseEvent>;
#composedKeydown = undefined as unknown as EventCallback<KeyboardEvent>;
#isDisabled = $derived(
this.#disabled.value || this.#itemState.disabled.value || this.#root.disabled.value
);
props = $derived({
id: this.#id.value,
disabled: this.#isDisabled,
"aria-expanded": getAriaExpanded(this.#itemState.isSelected),
"aria-disabled": getAriaDisabled(this.#isDisabled),
"data-disabled": getDataDisabled(this.#isDisabled),
"data-value": this.#itemState.value,
"data-state": getDataOpenClosed(this.#itemState.isSelected),
"data-accordion-trigger": "",
//
onclick: this.#composedClick,
onkeydown: this.#composedKeydown,
} as const);

constructor(props: AccordionTriggerStateProps, itemState: AccordionItemState) {
this.#disabled = props.disabled;
Expand All @@ -192,10 +191,6 @@ class AccordionTriggerState {
this.#node = useNodeById(this.#id);
}

get #isDisabled() {
return this.#disabled.value || this.#itemState.disabled.value || this.#root.disabled.value;
}

#onclick = () => {
if (this.#isDisabled) return;
this.#itemState.updateValue();
Expand Down Expand Up @@ -228,22 +223,6 @@ class AccordionTriggerState {

candidateItems[keyToIndex[e.key]!]?.focus();
};

get props() {
return {
id: this.#id.value,
disabled: this.#isDisabled,
"aria-expanded": getAriaExpanded(this.#itemState.isSelected),
"aria-disabled": getAriaDisabled(this.#isDisabled),
"data-disabled": getDataDisabled(this.#isDisabled),
"data-value": this.#itemState.value,
"data-state": getDataOpenClosed(this.#itemState.isSelected),
"data-accordion-trigger": "",
//
onclick: this.#composedClick,
onkeydown: this.#composedKeydown,
} as const;
}
}

/**
Expand All @@ -257,15 +236,28 @@ type AccordionContentStateProps = ReadonlyBoxedValues<{
}>;

class AccordionContentState {
item: AccordionItemState;
item = undefined as unknown as AccordionItemState;
node: Box<HTMLElement | null>;
#id: ReadonlyBox<string>;
#id = undefined as unknown as ReadonlyBox<string>;
#originalStyles: { transitionDuration: string; animationName: string } | undefined = undefined;
#isMountAnimationPrevented = false;
#width = $state(0);
#height = $state(0);
#forceMount: ReadonlyBox<boolean>;
#styleProp: ReadonlyBox<StyleProperties>;
#forceMount = undefined as unknown as ReadonlyBox<boolean>;
#styleProp = undefined as unknown as ReadonlyBox<StyleProperties>;
props = $derived({
id: this.#id.value,
"data-state": getDataOpenClosed(this.item.isSelected),
"data-disabled": getDataDisabled(this.item.isDisabled),
"data-value": this.item.value,
"data-accordion-content": "",
style: styleToString({
...this.#styleProp.value,
"--bits-accordion-content-height": `${this.#height}px`,
"--bits-accordion-content-width": `${this.#width}px`,
}),
} as const);
present = $derived(this.#forceMount.value || this.item.isSelected);

constructor(props: AccordionContentStateProps, item: AccordionItemState) {
this.item = item;
Expand Down Expand Up @@ -317,25 +309,6 @@ class AccordionContentState {
});
});
}

get present() {
return this.#forceMount.value || this.item.isSelected;
}

get props() {
return {
id: this.#id.value,
"data-state": getDataOpenClosed(this.item.isSelected),
"data-disabled": getDataDisabled(this.item.isDisabled),
"data-value": this.item.value,
"data-accordion-content": "",
style: styleToString({
...this.#styleProp.value,
"--bits-accordion-content-height": `${this.#height}px`,
"--bits-accordion-content-width": `${this.#width}px`,
}),
} as const;
}
}

//
Expand Down
80 changes: 34 additions & 46 deletions packages/bits-ui/src/lib/bits/checkbox/checkbox.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,31 @@ function getCheckboxDataState(checked: boolean | "indeterminate") {
}

class CheckboxRootState {
checked: Box<boolean | "indeterminate">;
disabled: ReadonlyBox<boolean>;
required: ReadonlyBox<boolean>;
checked = undefined as unknown as Box<boolean | "indeterminate">;
disabled = undefined as unknown as ReadonlyBox<boolean>;
required = undefined as unknown as ReadonlyBox<boolean>;
name: ReadonlyBox<string | undefined>;
value: ReadonlyBox<string | undefined>;
#composedClick: EventCallback<MouseEvent>;
#composedKeydown: EventCallback<KeyboardEvent>;
#composedClick = undefined as unknown as EventCallback<MouseEvent>;
#composedKeydown = undefined as unknown as EventCallback<KeyboardEvent>;
props = $derived({
"data-disabled": getDataDisabled(this.disabled.value),
"data-state": getCheckboxDataState(this.checked.value),
role: "checkbox",
type: "button",
"aria-checked": getAriaChecked(this.checked.value),
"aria-required": getAriaRequired(this.required.value),
"data-checkbox-root": "",
disabled: this.disabled.value,
//
onclick: this.#composedClick,
onkeydown: this.#composedKeydown,
} as const);
indicatorprops = $derived({
"data-disabled": getDataDisabled(this.disabled.value),
"data-state": getCheckboxDataState(this.checked.value),
"data-checkbox-indicator": "",
} as const);

constructor(props: CheckboxRootStateProps) {
this.checked = props.checked;
Expand Down Expand Up @@ -69,58 +87,28 @@ class CheckboxRootState {
createInput() {
return new CheckboxInputState(this);
}

get indicatorProps() {
return {
"data-disabled": getDataDisabled(this.disabled.value),
"data-state": getCheckboxDataState(this.checked.value),
"data-checkbox-indicator": "",
} as const;
}

get props() {
return {
"data-disabled": getDataDisabled(this.disabled.value),
"data-state": getCheckboxDataState(this.checked.value),
role: "checkbox",
type: "button",
"aria-checked": getAriaChecked(this.checked.value),
"aria-required": getAriaRequired(this.required.value),
"data-checkbox-root": "",
disabled: this.disabled.value,
//
onclick: this.#composedClick,
onkeydown: this.#composedKeydown,
} as const;
}
}

/**
* INPUT
*/

class CheckboxInputState {
root: CheckboxRootState;
root = undefined as unknown as CheckboxRootState;
props = $derived({
type: "checkbox",
checked: this.root.checked.value === true,
disabled: this.root.disabled.value,
required: this.root.required.value,
name: this.root.name.value,
value: this.root.value.value,
"data-checkbox-input": "",
} as const);
shouldRender = $derived(this.root.name.value !== undefined);

constructor(root: CheckboxRootState) {
this.root = root;
}

get shouldRender() {
return this.root.name.value !== undefined;
}

get props() {
return {
type: "checkbox",
checked: this.root.checked.value === true,
disabled: this.root.disabled.value,
required: this.root.required.value,
name: this.root.name.value,
value: this.root.value.value,
"data-checkbox-input": "",
} as const;
}
}

/**
Expand Down
Loading

0 comments on commit 7f63a91

Please sign in to comment.