1
0
mirror of https://github.com/axzilla/templui.git synced 2025-02-21 00:12:48 +00:00

feat: add accordion component

This commit is contained in:
“axzilla” 2024-10-07 09:49:40 +02:00
parent 846f0e87d7
commit aea8a59b85
7 changed files with 209 additions and 4 deletions

View File

@ -3,6 +3,7 @@
## 2024-10-07
- Added: Input component
- Added: Accordion component
- Changed: Add button type prop
- Changed: Use own input component on tabs example
- Changed: Update all current components with library comments

View File

@ -871,6 +871,10 @@ body {
max-width: 80rem;
}
.max-w-md {
max-width: 28rem;
}
.max-w-sm {
max-width: 24rem;
}
@ -879,14 +883,14 @@ body {
max-width: 20rem;
}
.max-w-md {
max-width: 28rem;
}
.flex-1 {
flex: 1 1 0%;
}
.shrink-0 {
flex-shrink: 0;
}
.-translate-x-1\/4 {
--tw-translate-x: -25%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@ -1023,6 +1027,17 @@ body {
margin-bottom: calc(2rem * var(--tw-space-y-reverse));
}
.divide-y > :not([hidden]) ~ :not([hidden]) {
--tw-divide-y-reverse: 0;
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
}
.divide-border > :not([hidden]) ~ :not([hidden]) {
--tw-divide-opacity: 1;
border-color: hsl(var(--border) / var(--tw-divide-opacity));
}
.overflow-auto {
overflow: auto;
}
@ -1195,6 +1210,11 @@ body {
padding-right: 1rem;
}
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.px-8 {
padding-left: 2rem;
padding-right: 2rem;
@ -1256,6 +1276,10 @@ body {
padding-top: 0px;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
@ -1709,6 +1733,10 @@ body {
width: 8rem;
}
.sm\:max-w-\[70\%\] {
max-width: 70%;
}
.sm\:px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
@ -1777,3 +1805,8 @@ body {
}
}
.\[\&\[aria-expanded\=true\]\>svg\]\:rotate-180[aria-expanded=true]>svg {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

View File

@ -26,6 +26,7 @@ func main() {
mux.Handle("GET /docs/components/tabs", templ.Handler(pages.Tabs()))
mux.Handle("GET /docs/components/card", templ.Handler(pages.Card()))
mux.Handle("GET /docs/components/input", templ.Handler(pages.Input()))
mux.Handle("GET /docs/components/accordion", templ.Handler(pages.Accordion()))
fmt.Println("Server is running on http://localhost:8090")
http.ListenAndServe(":8090", mux)

View File

@ -29,6 +29,10 @@ var Sections = []Section{
{
Title: "Components",
Links: []SideLink{
{
Text: "Accordion",
Href: "/docs/components/accordion",
},
{
Text: "Button",
Href: "/docs/components/button",

View File

@ -0,0 +1,95 @@
package components
// AccordionItem represents a single item in the Accordion component.
type AccordionItem struct {
// ID is the unique identifier for the accordion item.
// It is used to manage the open/closed state of the item.
ID string
// Trigger is the content of the accordion item's header/trigger.
// This is typically text, but can be any templ.Component.
Trigger templ.Component
// Content is the expandable content of the accordion item.
// This can be any templ.Component.
Content templ.Component
}
// AccordionProps defines the properties for the Accordion component.
type AccordionProps struct {
// Items is a slice of AccordionItem structs representing each item in the accordion.
Items []AccordionItem
// Class specifies additional CSS classes to apply to the accordion container.
// Default: "" (empty string)
Class string
// Attributes allows passing additional HTML attributes to the accordion container element.
// Default: nil
Attributes templ.Attributes
}
// Accordion renders an accordion component based on the provided props.
// It uses Alpine.js for interactivity and state management.
//
// Usage:
//
// @components.Accordion(components.AccordionProps{
// Items: []components.AccordionItem{
// {
// ID: "item-1",
// Trigger: templ.Raw("Is it accessible?"),
// Content: templ.Raw("Yes. It adheres to the WAI-ARIA design pattern."),
// },
// {
// ID: "item-2",
// Trigger: templ.Raw("Is it styled?"),
// Content: templ.Raw("Yes. It comes with default styles that match the other components' aesthetic."),
// },
// },
// Class: "w-full sm:max-w-[70%]",
// Attributes: templ.Attributes{"data-testid": "my-accordion"},
// })
//
// Props:
// - Items: A slice of AccordionItem structs, each representing an item in the accordion.
// - Class: Additional CSS classes to apply to the accordion container. Default: "" (empty string)
// - Attributes: Additional HTML attributes to apply to the accordion container element. Default: nil
templ Accordion(props AccordionProps) {
<div
x-data="{
activeItem: null,
toggleItem(itemId) {
this.activeItem = this.activeItem === itemId ? null : itemId;
}
}"
class={ "divide-y divide-border rounded-md border", props.Class }
{ props.Attributes... }
>
for _, item := range props.Items {
<div class="group">
<h3>
<button
type="button"
@click={ "toggleItem('" + item.ID + "')" }
class="flex w-full items-center justify-between py-4 px-5 text-left font-medium transition-all hover:underline [&[aria-expanded=true]>svg]:rotate-180"
:aria-expanded={ "activeItem === '" + item.ID + "'" }
>
@item.Trigger
<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="h-4 w-4 shrink-0 transition-transform duration-200">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
</h3>
<div
x-show={ "activeItem === '" + item.ID + "'" }
x-collapse
x-cloak
class="px-5 pb-4 pt-0"
>
@item.Content
</div>
</div>
}
</div>
}

View File

@ -0,0 +1,40 @@
package pages
import (
"github.com/axzilla/goilerplate/internals/ui/components"
"github.com/axzilla/goilerplate/internals/ui/layouts"
"github.com/axzilla/goilerplate/internals/ui/showcase"
)
templ Accordion() {
@layouts.DocsLayout() {
<div>
<div class="mb-16">
<h1 class="text-3xl font-bold mb-2">Accordion</h1>
<p class="mb-4 text-muted-foreground">A vertically stacked set of interactive headings that each reveal a section of content.</p>
</div>
@components.Tabs(components.TabsProps{
Tabs: []components.Tab{
{
ID: "preview",
Title: "Preview",
Content: showcase.AccordionShowcase(),
},
{
ID: "code",
Title: "Code",
Content: CodeSnippetFromEmbedded("accordion.templ", "go", showcase.TemplFiles),
},
{
ID: "component",
Title: "Component",
Content: CodeSnippetFromEmbedded("accordion.templ", "go", components.TemplFiles),
},
},
TabsContainerClass: "md:w-1/2",
ContentContainerClass: "w-full",
})
</div>
}
}

View File

@ -0,0 +1,31 @@
package showcase
import (
"github.com/axzilla/goilerplate/internals/ui/components"
)
templ AccordionShowcase() {
<div class="flex justify-center items-center border rounded-md py-16 px-4">
@components.Accordion(components.AccordionProps{
Items: []components.AccordionItem{
{
ID: "item-1",
Trigger: templ.Raw("Is it accessible?"),
Content: templ.Raw("Yes. It adheres to the WAI-ARIA design pattern."),
},
{
ID: "item-2",
Trigger: templ.Raw("Is it styled?"),
Content: templ.Raw("Yes. It comes with default styles that match the other components' aesthetic."),
},
{
ID: "item-3",
Trigger: templ.Raw("Is it animated?"),
Content: templ.Raw("Yes. It's animated by default, but you can disable it if you prefer."),
},
},
Class: "w-full sm:max-w-[70%]",
})
</div>
}