mirror of
https://github.com/axzilla/templui.git
synced 2025-03-14 05:17:59 +00:00
feat(spinner): initial implementation
This commit is contained in:
parent
1bdd906ca4
commit
30492d9284
@ -66,6 +66,9 @@
|
||||
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--animate-spin: spin 1s linear infinite;
|
||||
--animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
--animate-bounce: bounce 1s infinite;
|
||||
--blur-xs: 4px;
|
||||
--aspect-video: 16 / 9;
|
||||
--default-transition-duration: 150ms;
|
||||
@ -627,6 +630,9 @@
|
||||
.w-5 {
|
||||
width: calc(var(--spacing) * 5);
|
||||
}
|
||||
.w-6 {
|
||||
width: calc(var(--spacing) * 6);
|
||||
}
|
||||
.w-7 {
|
||||
width: calc(var(--spacing) * 7);
|
||||
}
|
||||
@ -642,6 +648,9 @@
|
||||
.w-16 {
|
||||
width: calc(var(--spacing) * 16);
|
||||
}
|
||||
.w-24 {
|
||||
width: calc(var(--spacing) * 24);
|
||||
}
|
||||
.w-56 {
|
||||
width: calc(var(--spacing) * 56);
|
||||
}
|
||||
@ -693,6 +702,9 @@
|
||||
.max-w-xs {
|
||||
max-width: var(--container-xs);
|
||||
}
|
||||
.min-w-\[120px\] {
|
||||
min-width: 120px;
|
||||
}
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
@ -788,6 +800,15 @@
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x) var(--tw-rotate-y) var(--tw-rotate-z) var(--tw-skew-x) var(--tw-skew-y);
|
||||
}
|
||||
.animate-bounce {
|
||||
animation: var(--animate-bounce);
|
||||
}
|
||||
.animate-pulse {
|
||||
animation: var(--animate-pulse);
|
||||
}
|
||||
.animate-spin {
|
||||
animation: var(--animate-spin);
|
||||
}
|
||||
.cursor-default {
|
||||
cursor: default;
|
||||
}
|
||||
@ -845,6 +866,9 @@
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
}
|
||||
@ -872,6 +896,9 @@
|
||||
.gap-8 {
|
||||
gap: calc(var(--spacing) * 8);
|
||||
}
|
||||
.gap-12 {
|
||||
gap: calc(var(--spacing) * 12);
|
||||
}
|
||||
.space-y-1 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-y-reverse: 0;
|
||||
@ -917,6 +944,13 @@
|
||||
.gap-x-4 {
|
||||
column-gap: calc(var(--spacing) * 4);
|
||||
}
|
||||
.space-x-1 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-inline-start: calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse));
|
||||
margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse)));
|
||||
}
|
||||
}
|
||||
.space-x-2 {
|
||||
:where(& > :not(:last-child)) {
|
||||
--tw-space-x-reverse: 0;
|
||||
@ -995,6 +1029,18 @@
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 2px;
|
||||
}
|
||||
.border-4 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 4px;
|
||||
}
|
||||
.border-\[3px\] {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 3px;
|
||||
}
|
||||
.border-\[5px\] {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 5px;
|
||||
}
|
||||
.border-t {
|
||||
border-top-style: var(--tw-border-style);
|
||||
border-top-width: 1px;
|
||||
@ -1026,6 +1072,9 @@
|
||||
.border-border {
|
||||
border-color: var(--border);
|
||||
}
|
||||
.border-current {
|
||||
border-color: currentColor;
|
||||
}
|
||||
.border-destructive {
|
||||
border-color: var(--destructive);
|
||||
}
|
||||
@ -1041,6 +1090,15 @@
|
||||
.border-transparent {
|
||||
border-color: transparent;
|
||||
}
|
||||
.border-r-transparent {
|
||||
border-right-color: transparent;
|
||||
}
|
||||
.border-b-transparent {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.border-l-transparent {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
.bg-\(--theme-primary\) {
|
||||
background-color: var(--theme-primary);
|
||||
}
|
||||
@ -1362,6 +1420,9 @@
|
||||
.text-primary\/90 {
|
||||
color: color-mix(in oklab, var(--primary) 90%, transparent);
|
||||
}
|
||||
.text-purple-500 {
|
||||
color: var(--color-purple-500);
|
||||
}
|
||||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
}
|
||||
@ -2663,3 +2724,23 @@
|
||||
initial-value: "";
|
||||
inherits: false;
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
@keyframes bounce {
|
||||
0%, 100% {
|
||||
transform: translateY(-25%);
|
||||
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
|
||||
}
|
||||
50% {
|
||||
transform: none;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ func main() {
|
||||
mux.Handle("GET /docs/components/separator", templ.Handler(pages.Separator()))
|
||||
mux.Handle("GET /docs/components/sheet", templ.Handler(pages.Sheet()))
|
||||
mux.Handle("GET /docs/components/slider", templ.Handler(pages.Slider()))
|
||||
mux.Handle("GET /docs/components/spinner", templ.Handler(pages.Spinner()))
|
||||
mux.Handle("GET /docs/components/tabs", templ.Handler(pages.Tabs()))
|
||||
mux.Handle("GET /docs/components/textarea", templ.Handler(pages.Textarea()))
|
||||
mux.Handle("GET /docs/components/time-picker", templ.Handler(pages.TimePicker()))
|
||||
|
181
components/spinner.templ
Normal file
181
components/spinner.templ
Normal file
@ -0,0 +1,181 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"github.com/axzilla/templui/utils"
|
||||
)
|
||||
|
||||
// SpinnerSize represents the available sizes for the Spinner component
|
||||
type SpinnerSize string
|
||||
|
||||
const (
|
||||
SpinnerSizeXs SpinnerSize = "xs" // Extra small (16px)
|
||||
SpinnerSizeSm SpinnerSize = "sm" // Small (24px)
|
||||
SpinnerSizeMd SpinnerSize = "md" // Medium (32px) - default
|
||||
SpinnerSizeLg SpinnerSize = "lg" // Large (48px)
|
||||
SpinnerSizeXl SpinnerSize = "xl" // Extra large (64px)
|
||||
SpinnerSize2xl SpinnerSize = "2xl" // 2X Large (96px)
|
||||
)
|
||||
|
||||
// SpinnerVariant defines the visual style of the spinner
|
||||
type SpinnerVariant string
|
||||
|
||||
const (
|
||||
SpinnerVariantBorder SpinnerVariant = "border" // Border style spinning animation
|
||||
SpinnerVariantDots SpinnerVariant = "dots" // Bouncing dots animation
|
||||
SpinnerVariantPulse SpinnerVariant = "pulse" // Pulsing circle animation
|
||||
)
|
||||
|
||||
// SpinnerProps configures the Spinner component
|
||||
type SpinnerProps struct {
|
||||
Size SpinnerSize // Controls the size of the spinner
|
||||
Variant SpinnerVariant // Visual style variant
|
||||
Color string // Custom color - uses theme colors if empty
|
||||
Text string // Optional text to display below the spinner
|
||||
Class string // Additional CSS classes
|
||||
Attributes templ.Attributes // Additional HTML attributes
|
||||
}
|
||||
|
||||
// spinnerSizeClass returns the appropriate size class based on the size prop
|
||||
func spinnerSizeClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "w-4 h-4"
|
||||
case SpinnerSizeSm:
|
||||
return "w-6 h-6"
|
||||
case SpinnerSizeLg:
|
||||
return "w-12 h-12"
|
||||
case SpinnerSizeXl:
|
||||
return "w-16 h-16"
|
||||
case SpinnerSize2xl:
|
||||
return "w-24 h-24"
|
||||
default:
|
||||
return "w-8 h-8" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// borderSpinnerClass returns the appropriate border-width class based on the size prop
|
||||
func borderSpinnerClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "border-2"
|
||||
case SpinnerSizeSm:
|
||||
return "border-[3px]"
|
||||
case SpinnerSizeLg, SpinnerSizeXl, SpinnerSize2xl:
|
||||
return "border-[5px]"
|
||||
default:
|
||||
return "border-4" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// textSizeClass returns the appropriate text size class based on spinner size
|
||||
func textSizeClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "text-xs"
|
||||
case SpinnerSizeSm:
|
||||
return "text-sm"
|
||||
case SpinnerSizeLg:
|
||||
return "text-lg"
|
||||
case SpinnerSizeXl, SpinnerSize2xl:
|
||||
return "text-xl"
|
||||
default:
|
||||
return "text-base" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// Spinner component for indicating loading states with customizable options
|
||||
templ Spinner(props SpinnerProps) {
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"inline-flex flex-col items-center justify-center",
|
||||
props.Class,
|
||||
) }
|
||||
aria-label="Loading"
|
||||
role="status"
|
||||
{ props.Attributes... }
|
||||
>
|
||||
if props.Variant == SpinnerVariantDots {
|
||||
<!-- Dots spinner variant -->
|
||||
<div class="flex space-x-1">
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
) }
|
||||
style="animation-delay: 0ms;"
|
||||
></div>
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
) }
|
||||
style="animation-delay: 150ms;"
|
||||
></div>
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
) }
|
||||
style="animation-delay: 300ms;"
|
||||
></div>
|
||||
</div>
|
||||
} else if props.Variant == SpinnerVariantPulse {
|
||||
// Pulse spinner variant
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"animate-pulse rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
"opacity-75",
|
||||
) }
|
||||
></div>
|
||||
} else {
|
||||
/* <!-- Default border spinner variant --> */
|
||||
<div
|
||||
class={ utils.TwMerge(
|
||||
"animate-spin rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
borderSpinnerClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"border-primary border-l-transparent border-r-transparent border-b-transparent",
|
||||
"border-current border-l-transparent border-r-transparent border-b-transparent",
|
||||
),
|
||||
utils.TwIfElse(
|
||||
props.Color != "",
|
||||
props.Color,
|
||||
"",
|
||||
),
|
||||
) }
|
||||
></div>
|
||||
}
|
||||
if props.Text != "" {
|
||||
<span
|
||||
class={ utils.TwMerge(
|
||||
"mt-2 text-center",
|
||||
textSizeClass(props.Size),
|
||||
"text-foreground",
|
||||
) }
|
||||
>{ props.Text }</span>
|
||||
}
|
||||
</div>
|
||||
}
|
371
components/spinner_templ.go
Normal file
371
components/spinner_templ.go
Normal file
@ -0,0 +1,371 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.833
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/axzilla/templui/utils"
|
||||
)
|
||||
|
||||
// SpinnerSize represents the available sizes for the Spinner component
|
||||
type SpinnerSize string
|
||||
|
||||
const (
|
||||
SpinnerSizeXs SpinnerSize = "xs" // Extra small (16px)
|
||||
SpinnerSizeSm SpinnerSize = "sm" // Small (24px)
|
||||
SpinnerSizeMd SpinnerSize = "md" // Medium (32px) - default
|
||||
SpinnerSizeLg SpinnerSize = "lg" // Large (48px)
|
||||
SpinnerSizeXl SpinnerSize = "xl" // Extra large (64px)
|
||||
SpinnerSize2xl SpinnerSize = "2xl" // 2X Large (96px)
|
||||
)
|
||||
|
||||
// SpinnerVariant defines the visual style of the spinner
|
||||
type SpinnerVariant string
|
||||
|
||||
const (
|
||||
SpinnerVariantBorder SpinnerVariant = "border" // Border style spinning animation
|
||||
SpinnerVariantDots SpinnerVariant = "dots" // Bouncing dots animation
|
||||
SpinnerVariantPulse SpinnerVariant = "pulse" // Pulsing circle animation
|
||||
)
|
||||
|
||||
// SpinnerProps configures the Spinner component
|
||||
type SpinnerProps struct {
|
||||
Size SpinnerSize // Controls the size of the spinner
|
||||
Variant SpinnerVariant // Visual style variant
|
||||
Color string // Custom color - uses theme colors if empty
|
||||
Text string // Optional text to display below the spinner
|
||||
Class string // Additional CSS classes
|
||||
Attributes templ.Attributes // Additional HTML attributes
|
||||
}
|
||||
|
||||
// spinnerSizeClass returns the appropriate size class based on the size prop
|
||||
func spinnerSizeClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "w-4 h-4"
|
||||
case SpinnerSizeSm:
|
||||
return "w-6 h-6"
|
||||
case SpinnerSizeLg:
|
||||
return "w-12 h-12"
|
||||
case SpinnerSizeXl:
|
||||
return "w-16 h-16"
|
||||
case SpinnerSize2xl:
|
||||
return "w-24 h-24"
|
||||
default:
|
||||
return "w-8 h-8" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// borderSpinnerClass returns the appropriate border-width class based on the size prop
|
||||
func borderSpinnerClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "border-2"
|
||||
case SpinnerSizeSm:
|
||||
return "border-[3px]"
|
||||
case SpinnerSizeLg, SpinnerSizeXl, SpinnerSize2xl:
|
||||
return "border-[5px]"
|
||||
default:
|
||||
return "border-4" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// textSizeClass returns the appropriate text size class based on spinner size
|
||||
func textSizeClass(size SpinnerSize) string {
|
||||
switch size {
|
||||
case SpinnerSizeXs:
|
||||
return "text-xs"
|
||||
case SpinnerSizeSm:
|
||||
return "text-sm"
|
||||
case SpinnerSizeLg:
|
||||
return "text-lg"
|
||||
case SpinnerSizeXl, SpinnerSize2xl:
|
||||
return "text-xl"
|
||||
default:
|
||||
return "text-base" // Default to medium
|
||||
}
|
||||
}
|
||||
|
||||
// Spinner component for indicating loading states with customizable options
|
||||
func Spinner(props SpinnerProps) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
var templ_7745c5c3_Var2 = []any{utils.TwMerge(
|
||||
"inline-flex flex-col items-center justify-center",
|
||||
props.Class,
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" aria-label=\"Loading\" role=\"status\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, props.Attributes)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, ">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if props.Variant == SpinnerVariantDots {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<!-- Dots spinner variant --> <div class=\"flex space-x-1\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 = []any{utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var4).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" style=\"animation-delay: 0ms;\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 = []any{utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var6...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var6).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" style=\"animation-delay: 150ms;\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 = []any{utils.TwMerge(
|
||||
"animate-bounce rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var8).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "\" style=\"animation-delay: 300ms;\"></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else if props.Variant == SpinnerVariantPulse {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 = []any{utils.TwMerge(
|
||||
"animate-pulse rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"bg-primary",
|
||||
props.Color,
|
||||
),
|
||||
"opacity-75",
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var10).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 = []any{utils.TwMerge(
|
||||
"animate-spin rounded-full",
|
||||
spinnerSizeClass(props.Size),
|
||||
borderSpinnerClass(props.Size),
|
||||
utils.TwIfElse(
|
||||
props.Color == "",
|
||||
"border-primary border-l-transparent border-r-transparent border-b-transparent",
|
||||
"border-current border-l-transparent border-r-transparent border-b-transparent",
|
||||
),
|
||||
utils.TwIfElse(
|
||||
props.Color != "",
|
||||
props.Color,
|
||||
"",
|
||||
),
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var12).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if props.Text != "" {
|
||||
var templ_7745c5c3_Var14 = []any{utils.TwMerge(
|
||||
"mt-2 text-center",
|
||||
textSizeClass(props.Size),
|
||||
"text-foreground",
|
||||
)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var14).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(props.Text)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/spinner.templ`, Line: 178, Col: 16}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
@ -150,6 +150,10 @@ var Sections = []Section{
|
||||
Text: "Slider",
|
||||
Href: "/docs/components/slider",
|
||||
},
|
||||
{
|
||||
Text: "Spinner",
|
||||
Href: "/docs/components/spinner",
|
||||
},
|
||||
{
|
||||
Text: "Tabs",
|
||||
Href: "/docs/components/tabs",
|
||||
|
62
internal/ui/pages/spinner.templ
Normal file
62
internal/ui/pages/spinner.templ
Normal file
@ -0,0 +1,62 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"github.com/axzilla/templui/internal/ui/layouts"
|
||||
"github.com/axzilla/templui/internal/ui/modules"
|
||||
"github.com/axzilla/templui/internal/ui/showcase"
|
||||
)
|
||||
|
||||
templ Spinner() {
|
||||
@layouts.DocsLayout(
|
||||
"Spinner",
|
||||
"Visual indicators for loading states and processes in progress.",
|
||||
) {
|
||||
@modules.PageWrapper(modules.PageWrapperProps{
|
||||
Name: "Spinner",
|
||||
Description: templ.Raw("Visual indicators for loading states and processes in progress."),
|
||||
Tailwind: true,
|
||||
}) {
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
ShowcaseFile: showcase.SpinnerDefault(),
|
||||
PreviewCodeFile: "spinner_default.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
@modules.Usage(modules.UsageProps{
|
||||
ComponentName: "Spinner",
|
||||
PropsExample: "Size: SpinnerSizeMd, Variant: SpinnerVariantBorder",
|
||||
})
|
||||
@modules.ContainerWrapper(modules.ContainerWrapperProps{Title: "Examples"}) {
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
SectionName: "Sizes",
|
||||
ShowcaseFile: showcase.SpinnerSizes(),
|
||||
PreviewCodeFile: "spinner_sizes.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
SectionName: "Variants",
|
||||
ShowcaseFile: showcase.SpinnerVariants(),
|
||||
PreviewCodeFile: "spinner_variants.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
SectionName: "Colors",
|
||||
ShowcaseFile: showcase.SpinnerColors(),
|
||||
PreviewCodeFile: "spinner_colors.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
SectionName: "With Text",
|
||||
ShowcaseFile: showcase.SpinnerWithText(),
|
||||
PreviewCodeFile: "spinner_with_text.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
@modules.ExampleWrapper(modules.ExampleWrapperProps{
|
||||
SectionName: "In Button",
|
||||
ShowcaseFile: showcase.SpinnerInButton(),
|
||||
PreviewCodeFile: "spinner_in_button.templ",
|
||||
ComponentCodeFile: "spinner.templ",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
221
internal/ui/showcase/spinner.templ
Normal file
221
internal/ui/showcase/spinner.templ
Normal file
@ -0,0 +1,221 @@
|
||||
package showcase
|
||||
|
||||
import "github.com/axzilla/templui/components"
|
||||
|
||||
templ SpinnerDefault() {
|
||||
<div class="flex justify-center">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SpinnerSizes() {
|
||||
<div class="flex flex-wrap items-end justify-center gap-8">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">XS</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeXs,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">SM</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeSm,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">MD (Default)</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">LG</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeLg,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">XL</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeXl,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">2XL</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSize2xl,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SpinnerVariants() {
|
||||
<div class="flex flex-wrap items-end justify-center gap-12">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Border (Default)</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Dots</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantDots,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Pulse</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantPulse,
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SpinnerColors() {
|
||||
<div class="flex flex-wrap items-end justify-center gap-8">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Default (Primary)</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Red</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-red-500",
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Green</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-green-500",
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Blue</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-blue-500",
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Yellow</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-yellow-500",
|
||||
})
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-sm text-muted-foreground mb-2">Purple</span>
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-purple-500",
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SpinnerWithText() {
|
||||
<div class="flex flex-wrap items-end justify-center gap-8">
|
||||
<!-- Border spinner with text -->
|
||||
<div class="flex flex-col items-center">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Text: "Loading...",
|
||||
})
|
||||
</div>
|
||||
<!-- Dots spinner with text -->
|
||||
<div class="flex flex-col items-center">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeMd,
|
||||
Variant: components.SpinnerVariantDots,
|
||||
Text: "Please wait",
|
||||
})
|
||||
</div>
|
||||
<!-- Pulse spinner with text -->
|
||||
<div class="flex flex-col items-center">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeLg,
|
||||
Variant: components.SpinnerVariantPulse,
|
||||
Text: "Processing your request",
|
||||
Color: "text-blue-500",
|
||||
})
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ SpinnerInButton() {
|
||||
<div class="flex flex-wrap items-center justify-center gap-8">
|
||||
<!-- Primary button with spinner -->
|
||||
@components.Button(components.ButtonProps{
|
||||
Attributes: templ.Attributes{
|
||||
"disabled": "true",
|
||||
},
|
||||
Class: "min-w-[120px]",
|
||||
}) {
|
||||
<div class="flex items-center gap-2">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeXs,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-primary-foreground",
|
||||
})
|
||||
<span>Loading</span>
|
||||
</div>
|
||||
}
|
||||
<!-- Secondary button with spinner -->
|
||||
@components.Button(components.ButtonProps{
|
||||
Variant: components.ButtonVariantSecondary,
|
||||
Attributes: templ.Attributes{
|
||||
"disabled": "true",
|
||||
},
|
||||
Class: "min-w-[120px]",
|
||||
}) {
|
||||
<div class="flex items-center gap-2">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeXs,
|
||||
Variant: components.SpinnerVariantBorder,
|
||||
Color: "text-secondary-foreground",
|
||||
})
|
||||
<span>Processing</span>
|
||||
</div>
|
||||
}
|
||||
<!-- Outline button with spinner -->
|
||||
@components.Button(components.ButtonProps{
|
||||
Variant: components.ButtonVariantOutline,
|
||||
Attributes: templ.Attributes{
|
||||
"disabled": "true",
|
||||
},
|
||||
Class: "min-w-[120px]",
|
||||
}) {
|
||||
<div class="flex items-center gap-2">
|
||||
@components.Spinner(components.SpinnerProps{
|
||||
Size: components.SpinnerSizeXs,
|
||||
Variant: components.SpinnerVariantDots,
|
||||
})
|
||||
<span>Submitting</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user