Skip to content

Commit

Permalink
Merge pull request #62 from thetarnav/fix/list-section-children-are-n…
Browse files Browse the repository at this point in the history
…ot-dynamic

WIP Fix: list section children are not dynamic
  • Loading branch information
thetarnav authored Jul 21, 2022
2 parents 2811fe8 + a804a4c commit 860cfec
Show file tree
Hide file tree
Showing 16 changed files with 640 additions and 344 deletions.
4 changes: 4 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
tasks:
- init: pnpm install && pnpm run build
vscode:
extensions: ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"],
"unwantedRecommendations": []
}
11 changes: 11 additions & 0 deletions configs/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { resolve } from "path";
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";

const pathToPackages = resolve(__dirname, "..", "packages");
const resolvePackage = (name: string) => resolve(pathToPackages, name, "src");

export const viteConfig = defineConfig({
plugins: [solidPlugin()],
resolve: {
alias: {
"@solid-aria/list": resolvePackage("list"),
"@solid-aria/collection": resolvePackage("collection"),
"@solid-aria/selection": resolvePackage("selection")
}
},
build: {
target: "esnext",
polyfillDynamicImport: false
Expand Down
129 changes: 129 additions & 0 deletions packages/collection/dev/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { createFocusRing } from "@solid-aria/focus";
import {
AriaListBoxOptionProps,
AriaListBoxProps,
AriaListBoxSectionProps,
createListBox,
createListBoxOption,
createListBoxSection
} from "@solid-aria/listbox";
import { combineProps } from "@solid-primitives/props";
import { createSignal, For, ParentProps, Show } from "solid-js";

import { ForItems, Item, Section } from "../src";

function ListBox(props: AriaListBoxProps) {
let ref: HTMLUListElement | undefined;

const { ListBoxProvider, listBoxProps, labelProps, state } = createListBox(props, () => ref);

return (
<ListBoxProvider>
<div {...labelProps}>{props.label}</div>
<ul
{...listBoxProps}
ref={ref}
style={{
padding: 0,
margin: "5px 0",
"list-style": "none",
border: "1px solid gray",
"max-width": "250px"
}}
>
<ForItems in={state.collection()}>
{section => (
<ListBoxSection heading={section().title}>
<ForItems in={section().childNodes}>
{item => <Option key={item().key}>{item().children}</Option>}
</ForItems>
</ListBoxSection>
)}
</ForItems>
</ul>
</ListBoxProvider>
);
}

function ListBoxSection(props: ParentProps<AriaListBoxSectionProps>) {
const { itemProps, headingProps, groupProps } = createListBoxSection(props);

return (
<li {...itemProps}>
<Show when={props.heading}>
<span
{...headingProps}
style={{
"font-weight": "bold",
"font-size": "1.1em",
padding: "2px 5px"
}}
>
{props.heading}
</span>
</Show>
<ul
{...groupProps}
style={{
padding: 0,
"list-style": "none"
}}
>
{props.children}
</ul>
</li>
);
}

function Option(props: ParentProps<AriaListBoxOptionProps>) {
let ref: HTMLLIElement | undefined;

const { optionProps, isSelected } = createListBoxOption(props, () => ref);

// Determine whether we should show a keyboard
// focus ring for accessibility
const { isFocusVisible, focusProps } = createFocusRing();

const rootProps = combineProps(optionProps, focusProps);

return (
<li
{...rootProps}
ref={ref}
style={{
background: isSelected() ? "blueviolet" : "transparent",
color: isSelected() ? "white" : null,
padding: "2px 5px",
outline: isFocusVisible() ? "2px solid orange" : "none"
}}
>
{props.children}
</li>
);
}

function App() {
const [sectionItems, setSectionItems] = createSignal([1, 2, 3]);

function addItem() {
setSectionItems(i => i.concat(i.length + 1));
}

function removeItem() {
setSectionItems(i => i.slice(0, i.length - 1));
}

return (
<>
<button onClick={addItem}>Add Item</button>
<button onClick={removeItem}>Remove Item</button>
<ListBox label="Choose an option" selectionMode="multiple">
<Section key="section-one" title="Section 1">
<For each={sectionItems()}>{key => <Item key={`option-${key}`}>Option {key}</Item>}</For>
</Section>
</ListBox>
</>
);
}

export default App;
7 changes: 3 additions & 4 deletions packages/collection/dev/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* @refresh reload */
import { render } from "solid-js/web";

function App() {
return <div>Hello Solid Aria!</div>;
}
import App from "./App";

render(() => <App />, document.getElementById("root") as HTMLDivElement);
render(() => <App />, document.getElementById("root")!);
2 changes: 1 addition & 1 deletion packages/collection/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@solid-primitives/utils": "^2.1.0"
},
"devDependencies": {
"solid-js": "^1.4.4"
"solid-js": "^1.4.7"
},
"peerDependencies": {
"solid-js": "^1.4.4"
Expand Down
26 changes: 13 additions & 13 deletions packages/collection/src/createCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
* governing permissions and limitations under the License.
*/

import { Accessor, children, createEffect, createSignal, on } from "solid-js";
import { ResolvedChildren } from "solid-js/types/reactive/signal";
import { Many } from "@solid-primitives/utils";
import { Accessor, children, createMemo } from "solid-js";

import { CollectionBuilder } from "./CollectionBuilder";
import { Collection, CollectionBase, ItemMetaData, Node } from "./types";
Expand All @@ -28,31 +28,31 @@ export function createCollection<C extends Collection<Node> = Collection<Node>>(
factory: CollectionFactory<C>,
deps: Accessor<any>[] = []
): Accessor<C> {
const resolvedChildren = children(() => props.children);
const resolvedChildren = children(() => props.children) as unknown as Accessor<
Many<ItemMetaData>
>;

const builder = new CollectionBuilder();

const [collection, setCollection] = createSignal<C>(factory([]));

createEffect(
on([resolvedChildren, ...deps], ([resolvedChildren]) => {
const nodes = createNodes(builder, resolvedChildren);
setCollection(() => factory(nodes));
})
);
const collection = createMemo(() => {
// execute deps to track them
deps.forEach(f => f());
const nodes = createNodes(builder, resolvedChildren());
return factory(nodes);
});

return collection;
}

/**
* Create an Iterable of `Nodes` with the given builder and resolved children.
*/
function createNodes(builder: CollectionBuilder, resolvedChildren: ResolvedChildren) {
function createNodes(builder: CollectionBuilder, resolvedChildren: Many<ItemMetaData>) {
let items = resolvedChildren ?? [];

if (!Array.isArray(items)) {
items = [items];
}

return builder.build(items as unknown as ItemMetaData[]);
return builder.build(items);
}
17 changes: 7 additions & 10 deletions packages/list/src/createListState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
} from "@solid-aria/selection";
import { ItemKey } from "@solid-aria/types";
import { access } from "@solid-primitives/utils";
import { Accessor, createEffect, createMemo, on } from "solid-js";
import { Accessor, createComputed, createMemo } from "solid-js";

import { ListCollection } from "./ListCollection";

Expand Down Expand Up @@ -72,15 +72,12 @@ export function createListState(props: CreateListStateProps): ListState {
const selectionManager = createMemo(() => new SelectionManager(collection(), selectionState));

// Reset focused key if that item is deleted from the collection.
createEffect(
on([collection, selectionState.focusedKey], newValue => {
const [collection, focusedKey] = newValue;

if (focusedKey != null && !collection.getItem(focusedKey)) {
selectionState.setFocusedKey(undefined);
}
})
);
createComputed(() => {
const focusedKey = selectionState.focusedKey();
if (focusedKey != null && !collection().getItem(focusedKey)) {
selectionState.setFocusedKey(undefined);
}
});

return {
collection,
Expand Down
1 change: 1 addition & 0 deletions packages/listbox/src/createListBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export function createListBox<T extends HTMLElement>(
): ListBoxAria {
const defaultListboxId = createId();

// eslint-disable-next-line solid/reactivity
const domProps = mergeProps(createMemo(() => filterDOMProps(props, { labelable: true })));

const createSelectableListProps = mergeProps(props, {
Expand Down
Loading

0 comments on commit 860cfec

Please sign in to comment.