Skip to content

Commit

Permalink
Merge pull request #194 from woylie/tree
Browse files Browse the repository at this point in the history
add tree component
  • Loading branch information
woylie authored Jan 6, 2024
2 parents 3bc957f + 4c3f3a7 commit 2eec98e
Show file tree
Hide file tree
Showing 3 changed files with 470 additions and 5 deletions.
205 changes: 200 additions & 5 deletions lib/doggo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3332,8 +3332,8 @@ defmodule Doggo do
```heex
<Doggo.radio_group
id="favorite_dog"
name="favorite_dog"
id="favorite-dog"
name="favorite-dog"
label="Favorite Dog"
options={[
{"Labrador Retriever", "labrador"},
Expand Down Expand Up @@ -3380,7 +3380,7 @@ defmodule Doggo do
```html
<h3 id="dog-rg-label">Favorite Dog</h3>
<.radio_group labelled_by="dog-rg-label"></.radio_group>
<.radio_group labelledby="dog-rg-label"></.radio_group>
```
You should ensure that either the `label` or the `labelledby` attribute is
Expand Down Expand Up @@ -3424,8 +3424,10 @@ defmodule Doggo do
<Doggo.radio_group label="Favorite Dog" ... />
<h3 id="favorite_dog_label">Favorite Dog</h3>
<Doggo.radio_group labelledby="favorite_dog_label" ... />
With labelledby:
<h3 id="favorite-dog-label">Favorite Dog</h3>
<Doggo.radio_group labelledby="favorite-dog-label" ... />
"""
end

Expand Down Expand Up @@ -4311,6 +4313,199 @@ defmodule Doggo do
"""
end

@doc """
Renders a hierarchical list as a tree.
A good use case for this component is a folder structure. For navigation and
other menus, a regular nested list should be preferred.
> #### Not ready {: .warning}
>
> Doggo does not ship with JavaScript yet. The necessary JavaScript to
> expand and collapse nodes, to select items, and to navigate the tree will
> be added in a later release.
## Example
```heex
<Doggo.tree label="Dogs">
<Doggo.tree_item>
Breeds
<:items>
<Doggo.tree_item>Golden Retriever</Doggo.tree_item>
<Doggo.tree_item>Labrador Retriever</Doggo.tree_item>
</:items>
</Doggo.tree_item>
<Doggo.tree_item>
Characteristics
<:items>
<Doggo.tree_item>Playful</Doggo.tree_item>
<Doggo.tree_item>Loyal</Doggo.tree_item>
</:items>
</Doggo.tree_item>
</Doggo.tree>
```
## CSS
To target the wrapper, use an attribute selector:
```css
[role="tree"] {}
```
"""

@doc type: :form

attr :label, :string,
default: nil,
doc: """
A accessibility label for the truee. Set as `aria-label` attribute.
You should ensure that either the `label` or the `labelledby` attribute is
set.
Do not repeat the word `tree` in the label, since it is already announced
by screen readers.
"""

attr :labelledby, :string,
default: nil,
doc: """
The DOM ID of an element that labels this tree.
Example:
```html
<h3 id="dog-tree-label">Dogs</h3>
<.tree labelledby="dog-tree-label"></.tree>
```
You should ensure that either the `label` or the `labelledby` attribute is
set.
"""

attr :class, :any,
default: [],
doc: "Additional CSS classes. Can be a string or a list of strings."

attr :rest, :global, doc: "Any additional HTML attributes."

slot :inner_block,
required: true,
doc: """
Slot for the root nodes of the tree. Use the `tree_item/1` component as
direct children.
"""

def tree(assigns) do
label = assigns[:label]
labelledby = assigns[:labelledby]

if (label && labelledby) || !(label || labelledby) do
raise """
invalid label attributes for tree
Doggo.tree requires either 'label' or 'labelledby' set for accessibility,
but not both.
## Examples
With label:
<Doggo.tree label="Dogs" ... />
With labelledby:
<h3 id="dog-tree-label">Favorite Dog</h3>
<Doggo.tree labelledby="dog-tree-label" ... />
"""
end

~H"""
<ul
role="tree"
aria-label={@label}
aria-labelledby={@labelledby}
class={@class}
{@rest}
>
<%= render_slot(@inner_block) %>
</ul>
"""
end

@doc """
Renders a tree item within a `tree/1`.
This component can be used as a direct child of `tree/1` or within the `items`
slot of this component.
> #### Not ready {: .warning}
>
> Doggo does not ship with JavaScript yet. The necessary JavaScript to
> expand and collapse nodes, to select items, and to navigate the tree will
> be added in a later release.
## Example
```heex
<Doggo.tree label="Dogs">
<Doggo.tree_item>
Breeds
<:items>
<Doggo.tree_item>Golden Retriever</Doggo.tree_item>
<Doggo.tree_item>Labrador Retriever</Doggo.tree_item>
</:items>
</Doggo.tree_item>
<Doggo.tree_item>
Characteristics
<:items>
<Doggo.tree_item>Playful</Doggo.tree_item>
<Doggo.tree_item>Loyal</Doggo.tree_item>
</:items>
</Doggo.tree_item>
</Doggo.tree>
```
Icons can be added before the label:
<Doggo.tree_item>
<Heroicon.folder /> Breeds
<:items>
<Doggo.tree_item><Heroicon.document /> Golden Retriever</Doggo.tree_item>
<Doggo.tree_item><Heroicon.document /> Labrador Retriever</Doggo.tree_item>
</:items>
</Doggo.tree_item>
"""

slot :items,
doc: """
Slot for children of this item. Place one or more additional `tree_item/1`
components within this slot, or omit if this is a leaf node.
"""

slot :inner_block,
required: true,
doc: """
Slot for the item label.
"""

def tree_item(assigns) do
~H"""
<li
role="treeitem"
aria-selected="false"
aria-expanded={@items != [] && "false"}
>
<span><%= render_slot(@inner_block) %></span>
<ul :if={@items != []} role="group">
<%= render_slot(@items) %>
</ul>
</li>
"""
end

## Helpers

defp humanize(atom) when is_atom(atom) do
Expand Down
127 changes: 127 additions & 0 deletions priv/storybook/components/tree.story.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
defmodule Storybook.Components.Tree do
use PhoenixStorybook.Story, :component

def function, do: &Doggo.tree/1

def folder_svg do
"""
<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-folder"
>
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
</svg>
"""
end

def paw_svg do
"""
<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-paw-print"
>
<circle cx="11" cy="4" r="2" />
<circle cx="18" cy="8" r="2" />
<circle cx="20" cy="16" r="2" />
<path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z" />
</svg>
"""
end

def like_svg do
"""
<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-heart"
aria-hidden="true"
>
<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" />
</svg>
"""
end

def variations do
[
%Variation{
id: :default,
attributes: %{
label: "Dogs"
},
slots: [
"""
<Doggo.tree_item>
Breeds
<:items>
<Doggo.tree_item>Golden Retriever</Doggo.tree_item>
<Doggo.tree_item>Labrador Retriever</Doggo.tree_item>
</:items>
</Doggo.tree_item>
<Doggo.tree_item>
Characteristics
<:items>
<Doggo.tree_item>Playful</Doggo.tree_item>
<Doggo.tree_item>Loyal</Doggo.tree_item>
</:items>
</Doggo.tree_item>
"""
]
},
%Variation{
id: :with_icons,
attributes: %{
label: "Dogs"
},
slots: [
"""
<Doggo.tree_item>
#{folder_svg()} Breeds
<:items>
<Doggo.tree_item>
#{paw_svg()} Golden Retriever
</Doggo.tree_item>
<Doggo.tree_item>
#{paw_svg()} Labrador Retriever
</Doggo.tree_item>
</:items>
</Doggo.tree_item>
<Doggo.tree_item>
#{folder_svg()} Characteristics
<:items>
<Doggo.tree_item>
#{paw_svg()} Playful
</Doggo.tree_item>
<Doggo.tree_item>
#{paw_svg()} Loyal
</Doggo.tree_item>
</:items>
</Doggo.tree_item>
"""
]
}
]
end
end
Loading

0 comments on commit 2eec98e

Please sign in to comment.