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:
parent
846f0e87d7
commit
aea8a59b85
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -29,6 +29,10 @@ var Sections = []Section{
|
||||
{
|
||||
Title: "Components",
|
||||
Links: []SideLink{
|
||||
{
|
||||
Text: "Accordion",
|
||||
Href: "/docs/components/accordion",
|
||||
},
|
||||
{
|
||||
Text: "Button",
|
||||
Href: "/docs/components/button",
|
||||
|
95
internals/ui/components/accordion.templ
Normal file
95
internals/ui/components/accordion.templ
Normal 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>
|
||||
}
|
40
internals/ui/pages/accordion.templ
Normal file
40
internals/ui/pages/accordion.templ
Normal 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>
|
||||
}
|
||||
}
|
31
internals/ui/showcase/accordion.templ
Normal file
31
internals/ui/showcase/accordion.templ
Normal 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>
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user