Skip to content

Commit

Permalink
Merge pull request #115 from bluzky/feature/accessibility-radio-group
Browse files Browse the repository at this point in the history
wip trying fix radio group
  • Loading branch information
bluzky authored Dec 25, 2024
2 parents b2bde49 + 11e4d12 commit 0d94231
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 43 deletions.
3 changes: 2 additions & 1 deletion lib/salad_ui/collapsible.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ defmodule SaladUI.Collapsible do
~H"""
<div
data-component="collapsible"
data-parts={Jason.encode!(["trigger", "content"])}
data-parts={Jason.encode!(["root", "trigger", "content"])}
data-part="root"
data-options={Jason.encode!(%{open: @open})}
data-listeners={Jason.encode!(@listeners)}
phx-hook="ZagHook"
Expand Down
97 changes: 66 additions & 31 deletions lib/salad_ui/radio_group.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ defmodule SaladUI.RadioGroup do
</.radio_group>
"""
attr :id, :string, default: nil, doc: "The id of the radio group."
attr :name, :string, default: nil
attr :value, :any, default: nil
attr :"default-value", :any
attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form, for example: @form[:email]"
attr :class, :string, default: nil
attr :disabled, :boolean, default: false
attr :on_value_change, :string, default: nil, doc: "`push_event` event to push to server when select value changed"

slot :inner_block, required: true

def radio_group(assigns) do
Expand All @@ -36,11 +40,14 @@ defmodule SaladUI.RadioGroup do

~H"""
<div
role="radiogroup"
aria-required="false"
dir="ltr"
id={@id}
data-component="radio_group"
data-parts={Jason.encode!(~w(root item))}
data-options={Jason.encode!(%{value: @value, name: @name, disabled: @disabled})}
data-listeners={Jason.encode!(%{value: ["push:#{@on_value_change}"]})}
data-part="root"
phx-hook="ZagHook"
class={classes(["grid gap-2", @class])}
tabindex="0"
style="outline: none;"
>
{render_slot(@inner_block, @builder)}
Expand All @@ -50,43 +57,71 @@ defmodule SaladUI.RadioGroup do

attr :builder, :map, required: true
attr :class, :string, default: nil
attr :checked, :any, default: false
attr :value, :string, default: nil
attr :rest, :global
slot :inner_block, required: true

def radio_group_item(assigns) do
~H"""
<label class={
classes([
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 inline-grid",
@class
])
}>
<input
<label
class={classes(["inline-flex space-x-2", @class])}
data-part="item"
data-parts={Jason.encode!(~w(item-hidden-input item-control item-text))}
data-props={Jason.encode!(%{value: @value})}
>
<span
data-part="item-control"
class="group/radio aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 inline-grid"
>
<span class="hidden items-center justify-center group-data-[state=checked]/radio:flex">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-circle h-2.5 w-2.5 fill-current text-current"
>
<circle cx="12" cy="12" r="10"></circle>
</svg>
</span>
</span>
{render_slot(@inner_block)}
<input
data-part="item-hidden-input"
type="radio"
class="hidden peer/radio"
name={@builder.name}
class="hidden"
value={@value}
checked={normalize_boolean(@checked) || @builder.value == @value}
checked={@builder.value == @value}
{@rest}
/>
<span class="hidden items-center justify-center peer-checked/radio:flex">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="lucide lucide-circle h-2.5 w-2.5 fill-current text-current"
>
<circle cx="12" cy="12" r="10"></circle>
</svg>
</span>
</label>
"""
end

attr :class, :string, default: nil
attr :rest, :global, include: ~w(disabled form name value for)
slot :inner_block, required: true

def radio_group_item_label(assigns) do
~H"""
<span
data-part="item-text"
class={
classes([
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
@class
])
}
{@rest}
>
{render_slot(@inner_block)}
</span>
"""
end
end
5 changes: 3 additions & 2 deletions lib/salad_ui/select.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ defmodule SaladUI.Select do
id={@id}
class={classes(["relative group", @class])}
data-component="select"
data-parts={Jason.encode!(["trigger", "value-text", "positioner", "content", "item"])}
data-parts={Jason.encode!(["root", "trigger", "value-text", "positioner", "content", "item"])}
data-part="root"
data-options={Jason.encode!(%{value: [@value], collection: %{items: @items}})}
data-listeners={
Jason.encode!(%{value: ["exec:#{@select_handler}", "push:#{@on_value_change}"]})
Expand Down Expand Up @@ -181,7 +182,7 @@ defmodule SaladUI.Select do
<label
data-part="item"
data-props={Jason.encode!(%{item: @item})}
data-parts={Jason.encode!(["indicator", "item-text"])}
data-parts={Jason.encode!(["item-indicator", "item-text"])}
class={
classes([
"group/item",
Expand Down
1 change: 0 additions & 1 deletion priv/static/assets/zag/ZagHook.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export default {
try {
const options = this.parseOptions();
const listeners = this.parseListeners();
console.log("listeners", listeners);
return {
id: this.el.id || "",
...options,
Expand Down
15 changes: 7 additions & 8 deletions priv/static/assets/zag/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class Component {

// Re-render on state updates
this.service.subscribe((e) => {
console.log("State updated", e.event);
// console.log("State updated", e.event);
this.api = this.initApi(this.componentModule);
this.render();
});
Expand Down Expand Up @@ -65,9 +65,7 @@ export class Component {
if (context.collection) {
context.collection = component.collection(context.collection);
}
return component.machine({
...context,
});
return component.machine(context);
}

initApi(component) {
Expand All @@ -81,7 +79,7 @@ export class Component {
render() {
this.cleanup();

for (const part of ["root", ...this.parts(this.el)]) {
for (const part of this.parts(this.el)) {
if (part === "item") continue;
this.renderPart(this.el, part, this.api);
}
Expand All @@ -92,10 +90,11 @@ export class Component {
}

renderPart(root, name, api, opts = {}) {
const isRoot = name === "root";
const isRoot = name === root.dataset.part;
const part = isRoot ? root : root.querySelector(`[data-part='${name}']`);

const getterName = `get${camelize(name, true)}Props`;
// console.log(getterName, opts);

if (part && api[getterName]) {
const cleanup = this.spreadProps(part, api[getterName](opts), isRoot);
Expand All @@ -109,12 +108,12 @@ export class Component {
if (item.dataset.props) {
itemProps = JSON.parse(item.dataset.props);
}

// console.log("itemProps", this.api.getItemProps(itemProps));
const cleanup = this.spreadProps(item, this.api.getItemProps(itemProps));
this.cleanupFunctions.set(item, cleanup);

for (const part of this.parts(item)) {
this.renderPart(item, `item-${part}`, this.api, itemProps);
this.renderPart(item, part, this.api, itemProps);
}
}

Expand Down

0 comments on commit 0d94231

Please sign in to comment.