feat: add 13 components adapted from Oat UI
Components (.cljc + .css + tests): - Alert (success/warning/danger/info variants) - Badge (primary/secondary/outline/success/warning/danger) - Card (card/card-header/card-body/card-footer) - Accordion (collapsible with open/closed state) - Table (headers/rows, striped/bordered variants) - Dialog (modal with header/body/footer sections) - Breadcrumb (nav with active item) - Pagination (current/total with prev/next) - Progress (value bar with color variants) - Spinner (sm/md/lg sizes) - Skeleton (line/box/circle/heading placeholders) - Switch (toggle with checked/disabled states) - Tooltip (hover text via data-tooltip attr) CSS-only additions: - Form elements (inputs, selects, checkboxes, radios, range, groups) - Grid (12-column system with offsets, responsive) - Utilities (flex, spacing, alignment, sr-only) Also adds warning/fg-on-warning tokens to light and dark themes. All 3 dev targets updated with full component showcase. 40 tests, 213 assertions, all passing.
This commit is contained in:
@@ -1,35 +1,170 @@
|
||||
(ns dev.hiccup
|
||||
(:require [org.httpkit.server :as http]
|
||||
[hiccup2.core :as h]
|
||||
[ui.button :as button]))
|
||||
[ui.button :as button]
|
||||
[ui.alert :as alert]
|
||||
[ui.badge :as badge]
|
||||
[ui.card :as card]
|
||||
[ui.accordion :as accordion]
|
||||
[ui.table :as table]
|
||||
[ui.dialog :as dialog]
|
||||
[ui.spinner :as spinner]
|
||||
[ui.skeleton :as skeleton]
|
||||
[ui.progress :as progress]
|
||||
[ui.switch :as switch]
|
||||
[ui.tooltip :as tooltip]
|
||||
[ui.breadcrumb :as breadcrumb]
|
||||
[ui.pagination :as pagination]))
|
||||
|
||||
(def variants [:primary :secondary :ghost :danger])
|
||||
(def sizes [:sm :md :lg])
|
||||
(defn section [title & children]
|
||||
[:section {:style "margin-bottom: 2.5rem;"}
|
||||
[:h3 {:style "color: var(--fg-1); margin-bottom: 1rem; border-bottom: var(--border-0); padding-bottom: 0.5rem;"} title]
|
||||
(into [:div {:style "display: flex; flex-direction: column; gap: 1rem;"}] children)])
|
||||
|
||||
(defn button-grid []
|
||||
[:div {:style "display: grid; grid-template-columns: repeat(4, auto); gap: 1rem; align-items: center;"}
|
||||
;; Header row
|
||||
[:div]
|
||||
(for [size sizes]
|
||||
[:div {:style "font-weight: 600; text-align: center; color: var(--fg-1); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em;"}
|
||||
(name size)])
|
||||
;; ── Button ──────────────────────────────────────────────────────────
|
||||
(def button-variants [:primary :secondary :ghost :danger])
|
||||
(def button-sizes [:sm :md :lg])
|
||||
|
||||
;; Variant rows
|
||||
(for [variant variants]
|
||||
(list
|
||||
[:div {:style "font-weight: 600; color: var(--fg-1); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em;"}
|
||||
(name variant)]
|
||||
(for [size sizes]
|
||||
[:div {:style "text-align: center;"}
|
||||
(button/button {:variant variant :size size}
|
||||
(str (name variant) " " (name size)))])))])
|
||||
(defn button-demo []
|
||||
(section "Button"
|
||||
[:div {:style "display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;"}
|
||||
(for [v button-variants]
|
||||
(button/button {:variant v} (name v)))]
|
||||
[:div {:style "display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;"}
|
||||
(for [s button-sizes]
|
||||
(button/button {:variant :primary :size s} (str "size " (name s))))]
|
||||
[:div {:style "display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;"}
|
||||
(for [v button-variants]
|
||||
(button/button {:variant v :disabled true} (str (name v) " disabled")))]))
|
||||
|
||||
(defn disabled-row []
|
||||
[:div {:style "display: flex; gap: 0.75rem; flex-wrap: wrap;"}
|
||||
(for [variant variants]
|
||||
(button/button {:variant variant :disabled true}
|
||||
(str (name variant) " disabled")))])
|
||||
;; ── Alert ───────────────────────────────────────────────────────────
|
||||
(defn alert-demo []
|
||||
(section "Alert"
|
||||
(alert/alert {:variant :success :title "Success!"} "Your changes have been saved.")
|
||||
(alert/alert {:variant :warning :title "Warning!"} "Please review before continuing.")
|
||||
(alert/alert {:variant :danger :title "Error!"} "Something went wrong.")
|
||||
(alert/alert {:variant :info :title "Info"} "This is an informational alert.")
|
||||
(alert/alert {:title "Neutral"} "A neutral alert with no variant.")))
|
||||
|
||||
;; ── Badge ───────────────────────────────────────────────────────────
|
||||
(defn badge-demo []
|
||||
(section "Badge"
|
||||
[:div {:style "display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;"}
|
||||
(badge/badge {} "Default")
|
||||
(badge/badge {:variant :secondary} "Secondary")
|
||||
(badge/badge {:variant :outline} "Outline")
|
||||
(badge/badge {:variant :success} "Success")
|
||||
(badge/badge {:variant :warning} "Warning")
|
||||
(badge/badge {:variant :danger} "Danger")]))
|
||||
|
||||
;; ── Card ────────────────────────────────────────────────────────────
|
||||
(defn card-demo []
|
||||
(section "Card"
|
||||
(card/card {}
|
||||
(card/card-header {} [:h4 "Card Title"] [:p "Card description goes here."])
|
||||
(card/card-body {} [:p "This is the card content. It can contain any HTML."])
|
||||
(card/card-footer {}
|
||||
(button/button {:variant :secondary :size :sm} "Cancel")
|
||||
(button/button {:variant :primary :size :sm} "Save")))))
|
||||
|
||||
;; ── Accordion ───────────────────────────────────────────────────────
|
||||
(defn accordion-demo []
|
||||
(section "Accordion"
|
||||
(accordion/accordion {:title "What is this framework?"} "A cross-target component library for Clojure, ClojureScript, and Squint.")
|
||||
(accordion/accordion {:title "How do I use it?" :open true} "Just require the namespace and call the component functions.")
|
||||
(accordion/accordion {:title "Is it accessible?"} "Yes, components follow ARIA best practices.")))
|
||||
|
||||
;; ── Table ───────────────────────────────────────────────────────────
|
||||
(defn table-demo []
|
||||
(section "Table"
|
||||
(table/table {:headers ["Name" "Email" "Role" "Status"]
|
||||
:rows [["Alice Johnson" "alice@example.com" "Admin" "Active"]
|
||||
["Bob Smith" "bob@example.com" "Editor" "Active"]
|
||||
["Carol White" "carol@example.com" "Viewer" "Pending"]]})))
|
||||
|
||||
;; ── Dialog ──────────────────────────────────────────────────────────
|
||||
(defn dialog-demo []
|
||||
(section "Dialog"
|
||||
[:p {:style "color: var(--fg-2); font-size: var(--font-sm);"} "Click button to open dialog."]
|
||||
(button/button {:variant :primary
|
||||
:attrs {:onclick "document.getElementById('demo-dialog').showModal()"}}
|
||||
"Open dialog")
|
||||
(dialog/dialog {:id "demo-dialog"}
|
||||
(dialog/dialog-header {} [:h3 "Dialog Title"] [:p "Are you sure you want to continue?"])
|
||||
(dialog/dialog-body {} [:p "This action cannot be undone."])
|
||||
(dialog/dialog-footer {}
|
||||
(button/button {:variant :secondary :size :sm
|
||||
:attrs {:onclick "document.getElementById('demo-dialog').close()"}}
|
||||
"Cancel")
|
||||
(button/button {:variant :primary :size :sm
|
||||
:attrs {:onclick "document.getElementById('demo-dialog').close()"}}
|
||||
"Confirm")))))
|
||||
|
||||
;; ── Spinner ─────────────────────────────────────────────────────────
|
||||
(defn spinner-demo []
|
||||
(section "Spinner"
|
||||
[:div {:style "display: flex; gap: 1.5rem; align-items: center;"}
|
||||
(spinner/spinner {:size :sm})
|
||||
(spinner/spinner {})
|
||||
(spinner/spinner {:size :lg})]))
|
||||
|
||||
;; ── Skeleton ────────────────────────────────────────────────────────
|
||||
(defn skeleton-demo []
|
||||
(section "Skeleton"
|
||||
[:div {:style "max-width: 400px;"}
|
||||
(skeleton/skeleton {:variant :heading})
|
||||
(skeleton/skeleton {:variant :line})
|
||||
(skeleton/skeleton {:variant :line})
|
||||
[:div {:style "display: flex; gap: 1rem; margin-top: var(--size-3);"}
|
||||
(skeleton/skeleton {:variant :circle})
|
||||
[:div {:style "flex: 1;"}
|
||||
(skeleton/skeleton {:variant :line})
|
||||
(skeleton/skeleton {:variant :line})]]]))
|
||||
|
||||
;; ── Progress ────────────────────────────────────────────────────────
|
||||
(defn progress-demo []
|
||||
(section "Progress"
|
||||
(progress/progress {:value 25})
|
||||
(progress/progress {:value 50 :variant :success})
|
||||
(progress/progress {:value 75 :variant :warning})
|
||||
(progress/progress {:value 90 :variant :danger})))
|
||||
|
||||
;; ── Switch ──────────────────────────────────────────────────────────
|
||||
(defn switch-demo []
|
||||
(section "Switch"
|
||||
[:div {:style "display: flex; flex-direction: column; gap: 0.75rem;"}
|
||||
(switch/switch-toggle {:label "Notifications" :checked false})
|
||||
(switch/switch-toggle {:label "Dark mode" :checked true})
|
||||
(switch/switch-toggle {:label "Disabled off" :disabled true})
|
||||
(switch/switch-toggle {:label "Disabled on" :checked true :disabled true})]))
|
||||
|
||||
;; ── Tooltip ─────────────────────────────────────────────────────────
|
||||
(defn tooltip-demo []
|
||||
(section "Tooltip"
|
||||
[:div {:style "display: flex; gap: 1.5rem; padding-top: 2rem;"}
|
||||
(tooltip/tooltip {:text "Save your changes"}
|
||||
(button/button {:variant :primary} "Save"))
|
||||
(tooltip/tooltip {:text "Delete this item"}
|
||||
(button/button {:variant :danger} "Delete"))
|
||||
(tooltip/tooltip {:text "View profile"}
|
||||
[:a {:href "#" :style "color: var(--accent);"} "Profile"])]))
|
||||
|
||||
;; ── Breadcrumb ──────────────────────────────────────────────────────
|
||||
(defn breadcrumb-demo []
|
||||
(section "Breadcrumb"
|
||||
(breadcrumb/breadcrumb
|
||||
{:items [{:label "Home" :href "#"}
|
||||
{:label "Projects" :href "#"}
|
||||
{:label "Oat Docs" :href "#"}
|
||||
{:label "Components"}]})))
|
||||
|
||||
;; ── Pagination ──────────────────────────────────────────────────────
|
||||
(defn pagination-demo []
|
||||
(section "Pagination"
|
||||
(pagination/pagination {:current 3 :total 5
|
||||
:href-fn (fn [p] (str "#page-" p))})))
|
||||
|
||||
;; ── Page ────────────────────────────────────────────────────────────
|
||||
(defn page []
|
||||
(str
|
||||
"<!DOCTYPE html>\n"
|
||||
@@ -47,10 +182,20 @@
|
||||
[:button {:onclick "document.documentElement.dataset.theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'"
|
||||
:style "padding: 0.5rem 1rem; cursor: pointer; border-radius: var(--radius-md); border: var(--border-0); background: var(--bg-1); color: var(--fg-0);"}
|
||||
"Toggle Dark Mode"]]
|
||||
[:h3 {:style "color: var(--fg-1); margin-bottom: 1rem;"} "Button Grid"]
|
||||
(button-grid)
|
||||
[:h3 {:style "color: var(--fg-1); margin: 2rem 0 1rem;"} "Disabled States"]
|
||||
(disabled-row)]]])))
|
||||
(button-demo)
|
||||
(alert-demo)
|
||||
(badge-demo)
|
||||
(card-demo)
|
||||
(accordion-demo)
|
||||
(table-demo)
|
||||
(dialog-demo)
|
||||
(spinner-demo)
|
||||
(skeleton-demo)
|
||||
(progress-demo)
|
||||
(switch-demo)
|
||||
(tooltip-demo)
|
||||
(breadcrumb-demo)
|
||||
(pagination-demo)]]])))
|
||||
|
||||
(defn handler [{:keys [uri]}]
|
||||
(case uri
|
||||
|
||||
@@ -1,43 +1,180 @@
|
||||
(ns dev.replicant
|
||||
(:require [replicant.dom :as d]
|
||||
[ui.button :as button]))
|
||||
[ui.button :as button]
|
||||
[ui.alert :as alert]
|
||||
[ui.badge :as badge]
|
||||
[ui.card :as card]
|
||||
[ui.accordion :as accordion]
|
||||
[ui.table :as table]
|
||||
[ui.dialog :as dialog]
|
||||
[ui.spinner :as spinner]
|
||||
[ui.skeleton :as skeleton]
|
||||
[ui.progress :as progress]
|
||||
[ui.switch :as switch]
|
||||
[ui.tooltip :as tooltip]
|
||||
[ui.breadcrumb :as breadcrumb]
|
||||
[ui.pagination :as pagination]))
|
||||
|
||||
(def variants [:primary :secondary :ghost :danger])
|
||||
(def sizes [:sm :md :lg])
|
||||
(defn section [title & children]
|
||||
[:section {:style {:margin-bottom "2.5rem"}}
|
||||
[:h3 {:style {:color "var(--fg-1)" :margin-bottom "1rem"
|
||||
:border-bottom "var(--border-0)" :padding-bottom "0.5rem"}} title]
|
||||
(into [:div {:style {:display "flex" :flex-direction "column" :gap "1rem"}}] children)])
|
||||
|
||||
(defn button-grid []
|
||||
[:div {:style {:display "grid"
|
||||
:grid-template-columns "repeat(4, auto)"
|
||||
:gap "1rem"
|
||||
:align-items "center"}}
|
||||
[:div]
|
||||
(for [size sizes]
|
||||
[:div {:style {:font-weight "600" :text-align "center" :color "var(--fg-1)"
|
||||
:font-size "0.75rem" :text-transform "uppercase" :letter-spacing "0.05em"}}
|
||||
(name size)])
|
||||
(for [variant variants]
|
||||
(list
|
||||
[:div {:style {:font-weight "600" :color "var(--fg-1)"
|
||||
:font-size "0.75rem" :text-transform "uppercase" :letter-spacing "0.05em"}}
|
||||
(name variant)]
|
||||
(for [size sizes]
|
||||
[:div {:style {:text-align "center"}}
|
||||
(button/button {:variant variant :size size
|
||||
:on-click (fn [_] (js/console.log (str "Clicked: " (name variant) " " (name size))))}
|
||||
(str (name variant) " " (name size)))])))])
|
||||
;; ── Button ──────────────────────────────────────────────────────────
|
||||
(def button-variants [:primary :secondary :ghost :danger])
|
||||
(def button-sizes [:sm :md :lg])
|
||||
|
||||
(defn disabled-row []
|
||||
[:div {:style {:display "flex" :gap "0.75rem" :flex-wrap "wrap"}}
|
||||
(for [variant variants]
|
||||
(button/button {:variant variant :disabled true}
|
||||
(str (name variant) " disabled")))])
|
||||
(defn button-demo []
|
||||
(section "Button"
|
||||
[:div {:style {:display "flex" :gap "0.75rem" :flex-wrap "wrap" :align-items "center"}}
|
||||
(for [v button-variants]
|
||||
(button/button {:variant v :on-click (fn [_] (js/console.log (str "Clicked: " (name v))))}
|
||||
(name v)))]
|
||||
[:div {:style {:display "flex" :gap "0.75rem" :flex-wrap "wrap" :align-items "center"}}
|
||||
(for [s button-sizes]
|
||||
(button/button {:variant :primary :size s} (str "size " (name s))))]
|
||||
[:div {:style {:display "flex" :gap "0.75rem" :flex-wrap "wrap" :align-items "center"}}
|
||||
(for [v button-variants]
|
||||
(button/button {:variant v :disabled true} (str (name v) " disabled")))]))
|
||||
|
||||
;; ── Alert ───────────────────────────────────────────────────────────
|
||||
(defn alert-demo []
|
||||
(section "Alert"
|
||||
(alert/alert {:variant :success :title "Success!"} "Your changes have been saved.")
|
||||
(alert/alert {:variant :warning :title "Warning!"} "Please review before continuing.")
|
||||
(alert/alert {:variant :danger :title "Error!"} "Something went wrong.")
|
||||
(alert/alert {:variant :info :title "Info"} "This is an informational alert.")
|
||||
(alert/alert {:title "Neutral"} "A neutral alert with no variant.")))
|
||||
|
||||
;; ── Badge ───────────────────────────────────────────────────────────
|
||||
(defn badge-demo []
|
||||
(section "Badge"
|
||||
[:div {:style {:display "flex" :gap "0.5rem" :flex-wrap "wrap" :align-items "center"}}
|
||||
(badge/badge {} "Default")
|
||||
(badge/badge {:variant :secondary} "Secondary")
|
||||
(badge/badge {:variant :outline} "Outline")
|
||||
(badge/badge {:variant :success} "Success")
|
||||
(badge/badge {:variant :warning} "Warning")
|
||||
(badge/badge {:variant :danger} "Danger")]))
|
||||
|
||||
;; ── Card ────────────────────────────────────────────────────────────
|
||||
(defn card-demo []
|
||||
(section "Card"
|
||||
(card/card {}
|
||||
(card/card-header {} [:h4 "Card Title"] [:p "Card description goes here."])
|
||||
(card/card-body {} [:p "This is the card content. It can contain any HTML."])
|
||||
(card/card-footer {}
|
||||
(button/button {:variant :secondary :size :sm} "Cancel")
|
||||
(button/button {:variant :primary :size :sm} "Save")))))
|
||||
|
||||
;; ── Accordion ───────────────────────────────────────────────────────
|
||||
(defn accordion-demo []
|
||||
(section "Accordion"
|
||||
(accordion/accordion {:title "What is this framework?"} "A cross-target component library.")
|
||||
(accordion/accordion {:title "How do I use it?" :open true} "Just require the namespace and call functions.")
|
||||
(accordion/accordion {:title "Is it accessible?"} "Yes, follows ARIA best practices.")))
|
||||
|
||||
;; ── Table ───────────────────────────────────────────────────────────
|
||||
(defn table-demo []
|
||||
(section "Table"
|
||||
(table/table {:headers ["Name" "Email" "Role" "Status"]
|
||||
:rows [["Alice Johnson" "alice@example.com" "Admin" "Active"]
|
||||
["Bob Smith" "bob@example.com" "Editor" "Active"]
|
||||
["Carol White" "carol@example.com" "Viewer" "Pending"]]})))
|
||||
|
||||
;; ── Dialog ──────────────────────────────────────────────────────────
|
||||
(defn dialog-demo []
|
||||
(section "Dialog"
|
||||
[:p {:style {:color "var(--fg-2)" :font-size "var(--font-sm)"}} "Click button to open dialog."]
|
||||
(button/button {:variant :primary
|
||||
:on-click (fn [_]
|
||||
(when-let [el (.getElementById js/document "demo-dialog")]
|
||||
(.showModal el)))}
|
||||
"Open dialog")
|
||||
(dialog/dialog {:id "demo-dialog"}
|
||||
(dialog/dialog-header {} [:h3 "Dialog Title"] [:p "Are you sure you want to continue?"])
|
||||
(dialog/dialog-body {} [:p "This action cannot be undone."])
|
||||
(dialog/dialog-footer {}
|
||||
(button/button {:variant :secondary :size :sm
|
||||
:on-click (fn [_] (.close (.getElementById js/document "demo-dialog")))}
|
||||
"Cancel")
|
||||
(button/button {:variant :primary :size :sm
|
||||
:on-click (fn [_] (.close (.getElementById js/document "demo-dialog")))}
|
||||
"Confirm")))))
|
||||
|
||||
;; ── Spinner ─────────────────────────────────────────────────────────
|
||||
(defn spinner-demo []
|
||||
(section "Spinner"
|
||||
[:div {:style {:display "flex" :gap "1.5rem" :align-items "center"}}
|
||||
(spinner/spinner {:size :sm})
|
||||
(spinner/spinner {})
|
||||
(spinner/spinner {:size :lg})]))
|
||||
|
||||
;; ── Skeleton ────────────────────────────────────────────────────────
|
||||
(defn skeleton-demo []
|
||||
(section "Skeleton"
|
||||
[:div {:style {:max-width "400px"}}
|
||||
(skeleton/skeleton {:variant :heading})
|
||||
(skeleton/skeleton {:variant :line})
|
||||
(skeleton/skeleton {:variant :line})
|
||||
[:div {:style {:display "flex" :gap "1rem" :margin-top "var(--size-3)"}}
|
||||
(skeleton/skeleton {:variant :circle})
|
||||
[:div {:style {:flex "1"}}
|
||||
(skeleton/skeleton {:variant :line})
|
||||
(skeleton/skeleton {:variant :line})]]]))
|
||||
|
||||
;; ── Progress ────────────────────────────────────────────────────────
|
||||
(defn progress-demo []
|
||||
(section "Progress"
|
||||
(progress/progress {:value 25})
|
||||
(progress/progress {:value 50 :variant :success})
|
||||
(progress/progress {:value 75 :variant :warning})
|
||||
(progress/progress {:value 90 :variant :danger})))
|
||||
|
||||
;; ── Switch ──────────────────────────────────────────────────────────
|
||||
(defn switch-demo []
|
||||
(section "Switch"
|
||||
[:div {:style {:display "flex" :flex-direction "column" :gap "0.75rem"}}
|
||||
(switch/switch-toggle {:label "Notifications" :checked false})
|
||||
(switch/switch-toggle {:label "Dark mode" :checked true})
|
||||
(switch/switch-toggle {:label "Disabled off" :disabled true})
|
||||
(switch/switch-toggle {:label "Disabled on" :checked true :disabled true})]))
|
||||
|
||||
;; ── Tooltip ─────────────────────────────────────────────────────────
|
||||
(defn tooltip-demo []
|
||||
(section "Tooltip"
|
||||
[:div {:style {:display "flex" :gap "1.5rem" :padding-top "2rem"}}
|
||||
(tooltip/tooltip {:text "Save your changes"}
|
||||
(button/button {:variant :primary} "Save"))
|
||||
(tooltip/tooltip {:text "Delete this item"}
|
||||
(button/button {:variant :danger} "Delete"))
|
||||
(tooltip/tooltip {:text "View profile"}
|
||||
[:a {:href "#" :style {:color "var(--accent)"}} "Profile"])]))
|
||||
|
||||
;; ── Breadcrumb ──────────────────────────────────────────────────────
|
||||
(defn breadcrumb-demo []
|
||||
(section "Breadcrumb"
|
||||
(breadcrumb/breadcrumb
|
||||
{:items [{:label "Home" :href "#"}
|
||||
{:label "Projects" :href "#"}
|
||||
{:label "Oat Docs" :href "#"}
|
||||
{:label "Components"}]})))
|
||||
|
||||
;; ── Pagination ──────────────────────────────────────────────────────
|
||||
(defn pagination-demo []
|
||||
(section "Pagination"
|
||||
(pagination/pagination {:current 3 :total 5
|
||||
:on-click (fn [p] (js/console.log (str "Page: " p)))})))
|
||||
|
||||
;; ── Theme toggle ────────────────────────────────────────────────────
|
||||
(defn toggle-theme! [_e]
|
||||
(let [el (.-documentElement js/document)
|
||||
current (.. el -dataset -theme)]
|
||||
(set! (.. el -dataset -theme)
|
||||
(if (= current "dark") "light" "dark"))))
|
||||
|
||||
;; ── App ─────────────────────────────────────────────────────────────
|
||||
(defn app []
|
||||
[:div {:style {:max-width "800px" :margin "0 auto"}}
|
||||
[:div {:style {:display "flex" :justify-content "space-between" :align-items "center" :margin-bottom "2rem"}}
|
||||
@@ -46,10 +183,20 @@
|
||||
:style {:padding "0.5rem 1rem" :cursor "pointer" :border-radius "var(--radius-md)"
|
||||
:border "var(--border-0)" :background "var(--bg-1)" :color "var(--fg-0)"}}
|
||||
"Toggle Dark Mode"]]
|
||||
[:h3 {:style {:color "var(--fg-1)" :margin-bottom "1rem"}} "Button Grid"]
|
||||
(button-grid)
|
||||
[:h3 {:style {:color "var(--fg-1)" :margin "2rem 0 1rem"}} "Disabled States"]
|
||||
(disabled-row)])
|
||||
(button-demo)
|
||||
(alert-demo)
|
||||
(badge-demo)
|
||||
(card-demo)
|
||||
(accordion-demo)
|
||||
(table-demo)
|
||||
(dialog-demo)
|
||||
(spinner-demo)
|
||||
(skeleton-demo)
|
||||
(progress-demo)
|
||||
(switch-demo)
|
||||
(tooltip-demo)
|
||||
(breadcrumb-demo)
|
||||
(pagination-demo)])
|
||||
|
||||
(defn ^:export init! []
|
||||
(d/set-dispatch! (fn [_ _]))
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
(ns dev.squint
|
||||
(:require ["eucalypt" :as eu]
|
||||
[ui.button :as button]))
|
||||
|
||||
(def variants ["primary" "secondary" "ghost" "danger"])
|
||||
(def sizes ["sm" "md" "lg"])
|
||||
[ui.button :as button]
|
||||
[ui.alert :as alert]
|
||||
[ui.badge :as badge]
|
||||
[ui.card :as card]
|
||||
[ui.accordion :as accordion]
|
||||
[ui.table :as table]
|
||||
[ui.dialog :as dialog]
|
||||
[ui.spinner :as spinner]
|
||||
[ui.skeleton :as skeleton]
|
||||
[ui.progress :as progress]
|
||||
[ui.switch :as switch]
|
||||
[ui.tooltip :as tooltip]
|
||||
[ui.breadcrumb :as breadcrumb]
|
||||
[ui.pagination :as pagination]))
|
||||
|
||||
(defn toggle-theme! [_e]
|
||||
(let [el (.-documentElement js/document)
|
||||
@@ -11,46 +21,161 @@
|
||||
(set! (.. el -dataset -theme)
|
||||
(if (= current "dark") "light" "dark"))))
|
||||
|
||||
(def label-style {"font-weight" "600"
|
||||
"color" "var(--fg-1)"
|
||||
"font-size" "0.75rem"
|
||||
"text-transform" "uppercase"
|
||||
"letter-spacing" "0.05em"})
|
||||
(defn section [title & children]
|
||||
(into [:section {:style {"margin-bottom" "2.5rem"}}
|
||||
[:h3 {:style {"color" "var(--fg-1)" "margin-bottom" "1rem"
|
||||
"border-bottom" "var(--border-0)" "padding-bottom" "0.5rem"}} title]]
|
||||
children))
|
||||
|
||||
(defn button-grid []
|
||||
(into
|
||||
[:div {:style {"display" "grid"
|
||||
"grid-template-columns" "repeat(4, auto)"
|
||||
"gap" "1rem"
|
||||
"align-items" "center"}}
|
||||
[:div]
|
||||
[:div {:style (merge label-style {"text-align" "center"})} "sm"]
|
||||
[:div {:style (merge label-style {"text-align" "center"})} "md"]
|
||||
[:div {:style (merge label-style {"text-align" "center"})} "lg"]]
|
||||
(mapcat (fn [variant]
|
||||
[[:div {:style label-style} variant]
|
||||
[:div {:style {"text-align" "center"}}
|
||||
(button/button {:variant variant :size "sm"
|
||||
:on-click (fn [_] (js/console.log (str "Clicked: " variant " sm")))}
|
||||
(str variant " sm"))]
|
||||
[:div {:style {"text-align" "center"}}
|
||||
(button/button {:variant variant :size "md"
|
||||
:on-click (fn [_] (js/console.log (str "Clicked: " variant " md")))}
|
||||
(str variant " md"))]
|
||||
[:div {:style {"text-align" "center"}}
|
||||
(button/button {:variant variant :size "lg"
|
||||
:on-click (fn [_] (js/console.log (str "Clicked: " variant " lg")))}
|
||||
(str variant " lg"))]])
|
||||
variants)))
|
||||
;; ── Button ──────────────────────────────────────────────────────────
|
||||
(def button-variants ["primary" "secondary" "ghost" "danger"])
|
||||
(def button-sizes ["sm" "md" "lg"])
|
||||
|
||||
(defn disabled-row []
|
||||
(into
|
||||
[:div {:style {"display" "flex" "gap" "0.75rem" "flex-wrap" "wrap"}}]
|
||||
(map (fn [variant]
|
||||
(button/button {:variant variant :disabled true}
|
||||
(str variant " disabled")))
|
||||
variants)))
|
||||
(defn button-demo []
|
||||
(section "Button"
|
||||
(into [:div {:style {"display" "flex" "gap" "0.75rem" "flex-wrap" "wrap" "align-items" "center"}}]
|
||||
(map (fn [v]
|
||||
(button/button {:variant v :on-click (fn [_] (js/console.log (str "Clicked: " v)))} v))
|
||||
button-variants))
|
||||
(into [:div {:style {"display" "flex" "gap" "0.75rem" "flex-wrap" "wrap" "align-items" "center"}}]
|
||||
(map (fn [s]
|
||||
(button/button {:variant "primary" :size s} (str "size " s)))
|
||||
button-sizes))
|
||||
(into [:div {:style {"display" "flex" "gap" "0.75rem" "flex-wrap" "wrap" "align-items" "center"}}]
|
||||
(map (fn [v]
|
||||
(button/button {:variant v :disabled true} (str v " disabled")))
|
||||
button-variants))))
|
||||
|
||||
;; ── Alert ───────────────────────────────────────────────────────────
|
||||
(defn alert-demo []
|
||||
(section "Alert"
|
||||
(alert/alert {:variant "success" :title "Success!"} "Your changes have been saved.")
|
||||
(alert/alert {:variant "warning" :title "Warning!"} "Please review before continuing.")
|
||||
(alert/alert {:variant "danger" :title "Error!"} "Something went wrong.")
|
||||
(alert/alert {:variant "info" :title "Info"} "This is an informational alert.")
|
||||
(alert/alert {:title "Neutral"} "A neutral alert with no variant.")))
|
||||
|
||||
;; ── Badge ───────────────────────────────────────────────────────────
|
||||
(defn badge-demo []
|
||||
(section "Badge"
|
||||
(into [:div {:style {"display" "flex" "gap" "0.5rem" "flex-wrap" "wrap" "align-items" "center"}}]
|
||||
[(badge/badge {} "Default")
|
||||
(badge/badge {:variant "secondary"} "Secondary")
|
||||
(badge/badge {:variant "outline"} "Outline")
|
||||
(badge/badge {:variant "success"} "Success")
|
||||
(badge/badge {:variant "warning"} "Warning")
|
||||
(badge/badge {:variant "danger"} "Danger")])))
|
||||
|
||||
;; ── Card ────────────────────────────────────────────────────────────
|
||||
(defn card-demo []
|
||||
(section "Card"
|
||||
(card/card {}
|
||||
(card/card-header {} [:h4 "Card Title"] [:p "Card description goes here."])
|
||||
(card/card-body {} [:p "This is the card content. It can contain any HTML."])
|
||||
(card/card-footer {}
|
||||
(button/button {:variant "secondary" :size "sm"} "Cancel")
|
||||
(button/button {:variant "primary" :size "sm"} "Save")))))
|
||||
|
||||
;; ── Accordion ───────────────────────────────────────────────────────
|
||||
(defn accordion-demo []
|
||||
(section "Accordion"
|
||||
(accordion/accordion {:title "What is this framework?"} "A cross-target component library.")
|
||||
(accordion/accordion {:title "How do I use it?" :open true} "Just require the namespace and call functions.")
|
||||
(accordion/accordion {:title "Is it accessible?"} "Yes, follows ARIA best practices.")))
|
||||
|
||||
;; ── Table ───────────────────────────────────────────────────────────
|
||||
(defn table-demo []
|
||||
(section "Table"
|
||||
(table/table {:headers ["Name" "Email" "Role" "Status"]
|
||||
:rows [["Alice Johnson" "alice@example.com" "Admin" "Active"]
|
||||
["Bob Smith" "bob@example.com" "Editor" "Active"]
|
||||
["Carol White" "carol@example.com" "Viewer" "Pending"]]})))
|
||||
|
||||
;; ── Dialog ──────────────────────────────────────────────────────────
|
||||
(defn dialog-demo []
|
||||
(section "Dialog"
|
||||
[:p {:style {"color" "var(--fg-2)" "font-size" "var(--font-sm)"}} "Click button to open dialog."]
|
||||
(button/button {:variant "primary"
|
||||
:on-click (fn [_]
|
||||
(when-let [el (js/document.getElementById "demo-dialog-sq")]
|
||||
(.showModal el)))}
|
||||
"Open dialog")
|
||||
(dialog/dialog {:id "demo-dialog-sq"}
|
||||
(dialog/dialog-header {} [:h3 "Dialog Title"] [:p "Are you sure you want to continue?"])
|
||||
(dialog/dialog-body {} [:p "This action cannot be undone."])
|
||||
(dialog/dialog-footer {}
|
||||
(button/button {:variant "secondary" :size "sm"
|
||||
:on-click (fn [_] (.close (js/document.getElementById "demo-dialog-sq")))}
|
||||
"Cancel")
|
||||
(button/button {:variant "primary" :size "sm"
|
||||
:on-click (fn [_] (.close (js/document.getElementById "demo-dialog-sq")))}
|
||||
"Confirm")))))
|
||||
|
||||
;; ── Spinner ─────────────────────────────────────────────────────────
|
||||
(defn spinner-demo []
|
||||
(section "Spinner"
|
||||
[:div {:style {"display" "flex" "gap" "1.5rem" "align-items" "center"}}
|
||||
(spinner/spinner {:size "sm"})
|
||||
(spinner/spinner {})
|
||||
(spinner/spinner {:size "lg"})]))
|
||||
|
||||
;; ── Skeleton ────────────────────────────────────────────────────────
|
||||
(defn skeleton-demo []
|
||||
(section "Skeleton"
|
||||
[:div {:style {"max-width" "400px"}}
|
||||
(skeleton/skeleton {:variant "heading"})
|
||||
(skeleton/skeleton {:variant "line"})
|
||||
(skeleton/skeleton {:variant "line"})
|
||||
[:div {:style {"display" "flex" "gap" "1rem" "margin-top" "var(--size-3)"}}
|
||||
(skeleton/skeleton {:variant "circle"})
|
||||
[:div {:style {"flex" "1"}}
|
||||
(skeleton/skeleton {:variant "line"})
|
||||
(skeleton/skeleton {:variant "line"})]]]))
|
||||
|
||||
;; ── Progress ────────────────────────────────────────────────────────
|
||||
(defn progress-demo []
|
||||
(section "Progress"
|
||||
(progress/progress {:value 25})
|
||||
(progress/progress {:value 50 :variant "success"})
|
||||
(progress/progress {:value 75 :variant "warning"})
|
||||
(progress/progress {:value 90 :variant "danger"})))
|
||||
|
||||
;; ── Switch ──────────────────────────────────────────────────────────
|
||||
(defn switch-demo []
|
||||
(section "Switch"
|
||||
[:div {:style {"display" "flex" "flex-direction" "column" "gap" "0.75rem"}}
|
||||
(switch/switch-toggle {:label "Notifications" :checked false})
|
||||
(switch/switch-toggle {:label "Dark mode" :checked true})
|
||||
(switch/switch-toggle {:label "Disabled off" :disabled true})
|
||||
(switch/switch-toggle {:label "Disabled on" :checked true :disabled true})]))
|
||||
|
||||
;; ── Tooltip ─────────────────────────────────────────────────────────
|
||||
(defn tooltip-demo []
|
||||
(section "Tooltip"
|
||||
[:div {:style {"display" "flex" "gap" "1.5rem" "padding-top" "2rem"}}
|
||||
(tooltip/tooltip {:text "Save your changes"}
|
||||
(button/button {:variant "primary"} "Save"))
|
||||
(tooltip/tooltip {:text "Delete this item"}
|
||||
(button/button {:variant "danger"} "Delete"))
|
||||
(tooltip/tooltip {:text "View profile"}
|
||||
[:a {:href "#" :style {"color" "var(--accent)"}} "Profile"])]))
|
||||
|
||||
;; ── Breadcrumb ──────────────────────────────────────────────────────
|
||||
(defn breadcrumb-demo []
|
||||
(section "Breadcrumb"
|
||||
(breadcrumb/breadcrumb
|
||||
{:items [{:label "Home" :href "#"}
|
||||
{:label "Projects" :href "#"}
|
||||
{:label "Oat Docs" :href "#"}
|
||||
{:label "Components"}]})))
|
||||
|
||||
;; ── Pagination ──────────────────────────────────────────────────────
|
||||
(defn pagination-demo []
|
||||
(section "Pagination"
|
||||
(pagination/pagination {:current 3 :total 5
|
||||
:on-click (fn [p] (js/console.log (str "Page: " p)))})))
|
||||
|
||||
;; ── App ─────────────────────────────────────────────────────────────
|
||||
(defn app []
|
||||
[:div {:style {"max-width" "800px" "margin" "0 auto"}}
|
||||
[:div {:style {"display" "flex" "justify-content" "space-between" "align-items" "center" "margin-bottom" "2rem"}}
|
||||
@@ -59,10 +184,20 @@
|
||||
:style {"padding" "0.5rem 1rem" "cursor" "pointer" "border-radius" "var(--radius-md)"
|
||||
"border" "var(--border-0)" "background" "var(--bg-1)" "color" "var(--fg-0)"}}
|
||||
"Toggle Dark Mode"]]
|
||||
[:h3 {:style {"color" "var(--fg-1)" "margin-bottom" "1rem"}} "Button Grid"]
|
||||
(button-grid)
|
||||
[:h3 {:style {"color" "var(--fg-1)" "margin" "2rem 0 1rem"}} "Disabled States"]
|
||||
(disabled-row)])
|
||||
(button-demo)
|
||||
(alert-demo)
|
||||
(badge-demo)
|
||||
(card-demo)
|
||||
(accordion-demo)
|
||||
(table-demo)
|
||||
(dialog-demo)
|
||||
(spinner-demo)
|
||||
(skeleton-demo)
|
||||
(progress-demo)
|
||||
(switch-demo)
|
||||
(tooltip-demo)
|
||||
(breadcrumb-demo)
|
||||
(pagination-demo)])
|
||||
|
||||
(defn init! []
|
||||
(eu/render (app) (js/document.getElementById "app")))
|
||||
|
||||
Reference in New Issue
Block a user