PoC for new components, including select

This commit is contained in:
Ronan Berder
2025-06-12 10:37:19 +08:00
parent cdc52a810e
commit 3b0b38a4cf
38 changed files with 1551 additions and 1041 deletions
-8
View File
@@ -1,15 +1,7 @@
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Geist+Mono&display=swap");
@import "tailwindcss";
@import "../../src/css/basecoat.css";
@import "./custom.css";
@theme {
--font-sans: 'Geist';
--font-mono: 'Geist Mono';
}
@import "./themes/claude.css";
@import "./themes/doom-64.css";
@import "./themes/supabase.css";
+3 -15
View File
@@ -78,24 +78,12 @@
<link rel="stylesheet" href="/assets/styles.css">
<!-- JS -->
<script src="/assets/js/dialog.js" defer></script>
<script src="/assets/js/dropdown-menu.js" defer></script>
<script src="/assets/js/popover.js" defer></script>
<script src="/assets/js/toast.js" defer></script>
<script src="/assets/js/select.js" defer></script>
<script src="/assets/js/sidebar.js" defer></script>
<script src="/assets/js/tabs.js" defer></script>
<script src="/assets/js/toast.js" defer></script>
<script>
document.addEventListener('alpine:init', () => {
window.basecoat.registerDialog(Alpine);
window.basecoat.registerDropdownMenu(Alpine);
window.basecoat.registerPopover(Alpine);
window.basecoat.registerSelect(Alpine);
window.basecoat.registerSidebar(Alpine);
window.basecoat.registerTabs(Alpine);
window.basecoat.registerToast(Alpine);
});
</script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
@@ -19,7 +19,6 @@
] %}
{{ tabs(
id="code-block-{{ id }}",
tabsets=tabsets,
default_tab_index=(2 if default_code else 1),
register_on=["alpine:init", "htmx:afterSwap"]
+3 -9
View File
@@ -1,18 +1,12 @@
<div class="flex h-14 w-full items-center gap-2 px-4">
<button
type="button"
aria-label="Hide sidebar"
data-tooltip="Hide sidebar"
:data-tooltip="sidebarOpen ? 'Hide sidebar' : 'Show sidebar'"
onclick="window.dispatchEvent(new CustomEvent('sidebar:toggle'))"
aria-label="Toggle sidebar"
data-tooltip="Toggle sidebar"
data-side="bottom"
data-align="start"
class="btn-sm-icon-ghost mr-auto size-7 -ml-1.5"
x-init
x-data="{ sidebarOpen: true }"
@click="
sidebarOpen = !sidebarOpen;
$dispatch('sidebar:toggle');
"
>
{% lucide "panel-left" %}
</button>
@@ -1,4 +1,4 @@
{% from "popover.njk" import popover %}
{% from "dropdown-menu.njk" import dropdown_menu %}
<section id="breadcrumb" class="w-full rounded-lg border scroll-mt-14">
<header class="border-b px-4 py-3 flex items-center justify-between">
@@ -17,17 +17,17 @@
{% set trigger %}
{% lucide "ellipsis" %}
{% endset %}
{% call popover(
{{ dropdown_menu(
id="demo-breadcrumb-menu",
trigger=trigger,
trigger_attrs={"class": "flex size-9 items-center justify-center h-4 w-4 hover:text-foreground cursor-pointer"}
) %}
<nav role="menu">
<button type="button" role="menuitem">Documentation</button>
<button type="button" role="menuitem">Themes</button>
<button type="button" role="menuitem">GitHub</button>
</nav>
{% endcall %}
trigger_attrs={"class": "flex size-9 items-center justify-center h-4 w-4 hover:text-foreground cursor-pointer"},
popover_attrs={"class": "p-1"},
items=[
{ label: "Documentation" },
{ label: "Themes" },
{ label: "GitHub" }
]
) }}
</li>
<li>{% lucide "chevron-right", { "class": "size-3.5" } %}</li>
<li class="inline-flex items-center gap-1.5">
@@ -10,7 +10,7 @@
<div class="p-4">
<div class="flex flex-wrap items-start gap-4">
{% call dropdown_menu(
id="dropdown-menu",
id="dropdown-menu-default",
trigger="Open",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "min-w-56"}
@@ -134,8 +134,6 @@
</button>
{% endcall %}
{% set profile_menu %}
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<img alt="@hunvreus" src="https://github.com/hunvreus.png" class="size-8 shrink-0 rounded-full" alt="Ronan Berder"/>
@@ -170,7 +168,6 @@
{% endset %}
{% call dropdown_menu(
id="dropdown-menu-radio-group",
trigger="Radio Group",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "min-w-56"},
@@ -12,7 +12,7 @@
id="demo-popover",
trigger="Open popover",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "w-80 p-4"}
popover_attrs={"class": "w-80"}
) %}
<div class="grid gap-4">
<header class="grid gap-1.5">
@@ -8,7 +8,6 @@
</a>
</header>
<div class="p-4 gap">
<div class="flex flex-col gap-4">
<div class="flex flex-wrap items-center gap-2 md:flex-row">
<select class="select w-[180px]">
@@ -28,46 +27,70 @@
<div class="flex flex-wrap items-center gap-4">
<div class="flex flex-wrap items-center gap-2 md:flex-row">
{% call select(trigger_attrs={"class": "w-[180px]"}) %}
<div role="group" aria-labelledby="fruit-options-label">
<span id="fruit-options-label" role="heading">Fruits</span>
<div role="option" data-value="apple">Apple</div>
<div role="option" data-value="banana">Banana</div>
<div role="option" data-value="blueberry">Blueberry</div>
</div>
<div role="group" aria-labelledby="vegetable-options-label">
<span id="vegetable-options-label" role="heading">Grapes</span>
<div role="option" data-value="pineapple">Pineapple</div>
</div>
{% endcall %}
{{ select(
id="select-default",
trigger_attrs={"class": "w-[180px]"},
selected="blueberry",
items=[
{
type: "group",
label: "Fruits",
items: [
{ type: "item", label: "Apple", value: "apple" },
{ type: "item", label: "Banana", value: "banana" },
{ type: "item", label: "Blueberry Blueberry Blueberry Blueberry Blueberry", value: "blueberry" }
]
},
{
type: "group",
label: "Grapes",
items: [
{ type: "item", label: "Pineapple", value: "pineapple" }
]
}
]
) }}
{% call select(
id="select-scrollbar",
trigger_attrs={"class": "w-[180px]"},
listbox_attrs={"class": "scrollbar overflow-y-auto max-h-64"}
) %}
{% for i in range(0, 99) %}
<div role="option" data-value="item-{{ i }}">Item {{ i }}</div>
{% endfor %}
{% for i in range(0, 99) %}
<div role="option" data-value="item-{{ i }}">Item {{ i }}</div>
{% endfor %}
{% endcall %}
{% call select(trigger_attrs={"disabled": "disabled", "class": "w-[180px]"}) %}
{% call select(
id="select-disabled",
trigger_attrs={"disabled": "disabled", "class": "w-[180px]"}
) %}
<div role="option" data-value="disabled">Disabled</div>
{% endcall %}
{% call select(name="just-testing", trigger_attrs={'class': 'w-[180px]'}) %}
<div type="button" role="option" data-value="bar">
{% lucide "chart-bar", {"class": "text-muted-foreground"} %}
Bar
</div>
<div type="button" role="option" data-value="line">
{% lucide "chart-line", {"class": "text-muted-foreground"} %}
Line
</div>
<div type="button" role="option" data-value="pie">
{% lucide "chart-pie", {"class": "text-muted-foreground"} %}
Pie
</div>
{% call select(
id="select-with-icon",
name="chart-type",
trigger_attrs={'class': 'w-[180px]'}
) %}
<div type="button" role="option" data-value="bar">
<span class="flex items-center gap-2">
{% lucide "chart-bar", {"class": "text-muted-foreground"} %}
Bar
</span>
</div>
<div type="button" role="option" data-value="line">
<span class="flex items-center gap-2">
{% lucide "chart-line", {"class": "text-muted-foreground"} %}
Line
</span>
</div>
<div type="button" role="option" data-value="pie">
<span class="flex items-center gap-2">
{% lucide "chart-pie", {"class": "text-muted-foreground"} %}
Pie
</span>
</div>
{% endcall %}
</div>
</div>
@@ -13,18 +13,24 @@
min="0"
max="27"
value="12"
x-data="{
updatePercent(el) {
const min = parseFloat(el.min || 0);
const max = parseFloat(el.max || 100);
const value = parseFloat(el.value);
const percent = (max === min) ? 0 : ((value - min) / (max - min)) * 100;
el.style.setProperty('--slider-value', `${percent}%`);
}
}"
x-init="updatePercent($el)"
@input="updatePercent($el)"
>
</div>
</div>
</section>
</section>
<script>
(() => {
const sliderEl = document.querySelector('input[type="range"].input');
if (!sliderEl) return;
const updateSlider = (el) => {
const min = parseFloat(el.min || 0);
const max = parseFloat(el.max || 100);
const value = parseFloat(el.value);
const percent = (max === min) ? 0 : ((value - min) / (max - min)) * 100;
el.style.setProperty('--slider-value', `${percent}%`);
};
updateSlider(sliderEl);
sliderEl.addEventListener('input', (e) => updateSlider(e.target));
})();
</script>
+3 -2
View File
@@ -63,7 +63,7 @@
"hx-select": "#content",
"hx-target": "#content",
"hx-swap": "outerHTML",
"@click": "if (window.innerWidth < 768) { open = false }"
"data-sidebar-close-on-mobile": ""
} %}
{% set icon_home %}{% lucide "house" %}{% endset %}
@@ -79,7 +79,7 @@
{ label: "Kitchen sink", url: "/kitchen-sink", icon: icon_layout_dashboard, attrs: link_attrs },
{ label: "GitHub", url: "https://github.com/hunvreus/basecoat", icon: icon_github, attrs: {
"target": "_blank",
"@click": "if (window.innerWidth < 768) { open = false }"
"data-sidebar-close-on-mobile": ""
}
}
]},
@@ -117,6 +117,7 @@
{{ sidebar(
id="sidebar",
label="Sidebar navigation",
header=header,
footer=footer,
+341 -448
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -18,8 +18,8 @@ toc:
{% set code_html %}
{% set footer %}
<button class="btn-outline" @click="open = false">Cancel</button>
<button class="btn-primary" @click="open = false">Continue</button>
<button class="btn-outline" onClick="document.getElementById('alert-dialog').close()">Cancel</button>
<button class="btn-primary" onClick="document.getElementById('alert-dialog').close()">Continue</button>
{% endset %}
{{ dialog(
id="alert-dialog",
@@ -105,8 +105,8 @@ document.addEventListener('alpine:init', () => {
</div>
{% set raw_code %}{% raw %}{% set footer %}
<button class="btn-outline" @click="open = false">Cancel</button>
<button class="btn-primary" @click="open = false">Continue</button>
<button class="btn-outline" onClick="document.getElementById('demo-alert-dialog').close()">Cancel</button>
<button class="btn-primary" onClick="document.getElementById('demo-alert-dialog').close()">Continue</button>
{% endset %}
{{ dialog(
id="demo-alert-dialog",
+3 -3
View File
@@ -23,8 +23,8 @@ toc:
{% set code_html %}
{% set footer %}
<button class="btn-outline" @click="open = false">Cancel</button>
<button class="btn" @click="open = false">Save changes</button>
<button class="btn-outline" onClick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onClick="this.closest('dialog').close()">Save changes</button>
{% endset %}
{% call dialog(
id="demo-dialog-edit-profile",
@@ -175,7 +175,7 @@ document.addEventListener('alpine:init', () => {
{% set code %}
{% set footer %}
<button class="btn-outline" @click="open = false">Close</button>
<button class="btn-outline" onClick="this.closest('dialog').close()">Close</button>
{% endset %}
{% call dialog(
id="dialog-example",
+3 -4
View File
@@ -21,8 +21,7 @@ toc:
id="demo-popover",
trigger="Open popover",
trigger_attrs={"class": "btn-outline"},
content_attrs={"class": "w-80 p-4"},
register_on=["alpine:init", "htmx:afterSwap"]
popover_attrs={"class": "w-80"}
) %}
<div class="grid gap-4">
<header class="grid gap-1.5">
@@ -52,13 +51,13 @@ toc:
{% endset %}
{% set code_script %}
<script>
{# <script>
{% fetchCode "src/js/popover.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerPopover(Alpine);
});
</script>
</script> #}
{% endset %}
{{ code_preview("popover", code_script | prettyHtml ~ code_html | prettyHtml) }}
+31 -30
View File
@@ -34,34 +34,30 @@ toc:
<option>Blueberry</option>
<option>Grapes</option>
<option>Pineapple</option>
</optgroup>
</select>{% endset %}
{{ code_preview("select-native", code_native) }}
{% set code_html %}
{% call select(
name="select-js",
{{ select(
trigger_attrs={"class": "w-[180px]"},
register_on=["alpine:init", "htmx:afterSwap"]
) %}
<div role="group" aria-labelledby="fruit-options-label">
<span id="fruit-options-label" role="heading">Fruits</span>
<div role="option" data-value="apple">Apple</div>
<div role="option" data-value="banana">Banana</div>
<div role="option" data-value="blueberry">Blueberry</div>
<div role="option" data-value="pineapple">Grapes</div>
<div role="option" data-value="pineapple">Pineapple</div>
</div>
{% endcall %}
items=[
{
type: "group",
label: "Fruits",
items: [
{ type: "item", value: "apple", label: "Apple" },
{ type: "item", value: "banana", label: "Banana" },
{ type: "item", value: "blueberry", label: "Blueberry" },
{ type: "item", value: "pineapple", label: "Grapes" },
{ type: "item", value: "pineapple", label: "Pineapple" }
]
}
]
) }}
{% endset %}
{% set code_script %}
<script>
{% fetchCode "src/js/select.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerSelect(Alpine);
});
</script>
{% endset %}
{{ code_preview("select", code_script | prettyHtml ~ code_html | prettyHtml) }}
@@ -153,16 +149,21 @@ document.addEventListener('alpine:init', () => {
</a>
</div>
{% set raw_code %}{% raw %}{% call select(name="select-js") %}
<div role="group" aria-labelledby="fruit-options-label">
<span id="fruit-options-label" role="heading">Fruits</span>
<div role="option" data-value="apple">Apple</div>
<div role="option" data-value="banana">Banana</div>
<div role="option" data-value="blueberry">Blueberry</div>
<div role="option" data-value="pineapple">Grapes</div>
<div role="option" data-value="pineapple">Pineapple</div>
</div>
{% endcall %}{% endraw %}{% endset %}
{% set raw_code %}{% raw %}{{ select(
items=[
{
type: "group",
label: "Fruits",
items: [
{ type: "item", value: "apple", label: "Apple" },
{ type: "item", value: "banana", label: "Banana" },
{ type: "item", value: "blueberry", label: "Blueberry" },
{ type: "item", value: "pineapple", label: "Grapes" },
{ type: "item", value: "pineapple", label: "Pineapple" }
]
}
]
) }}{% endraw %}{% endset %}
{{ code_block(raw_code, "jinja") }}
<h2 id="examples"><a href="#examples">Examples</a></h2>
+17
View File
@@ -0,0 +1,17 @@
---
layout: layouts/base.njk
---
<div id="my-popover" open popover="manual">
SOMETHING
SOMETHING
SOMETHING
SOMETHING
SOMETHING
</div>
<button popovertarget="my-popover">
TOGGLE SIDEBAR
</button>
+2 -2
View File
@@ -84,13 +84,13 @@ toc:
{% set code_script %}
<script>
{# <script>
{% fetchCode "src/js/popover.js" %}
document.addEventListener('alpine:init', () => {
window.basecoat.registerPopover(Alpine);
});
</script>
</script> #}
{% endset %}
{% set code_html_toaster %}{{ toaster() }}{% endset %}
+9 -8
View File
@@ -32,16 +32,17 @@ layout: layouts/layout.njk
<p class="text-sm text-muted-foreground">m@example.com</p>
</div>
{% call select(
{{ select(
is_combobox=true,
selected="owner",
content_attrs={"data-align": "end"}
) %}
<div role="option" data-value="viewer">Viewer</div>
<div role="option" data-value="developer">Developer</div>
<div role="option" data-value="billing">Billing</div>
<div role="option" data-value="owner">Owner</div>
{% endcall %}
popover_attrs={"data-align": "end"},
items=[
{ type: "item", value: "viewer", label: "Viewer" },
{ type: "item", value: "developer", label: "Developer" },
{ type: "item", value: "billing", label: "Billing" },
{ type: "item", value: "owner", label: "Owner" }
]
) }}
</li>
<li class="flex items-center gap-4">
<img src="/assets/images/avatar-2.png" alt="Jackson Lee" class="w-10 h-10 rounded-full" />