Files
personal-website/src/components/TuiSegment.astro

193 lines
4.9 KiB
Plaintext

---
interface Props {
id: string;
label: string;
abbr?: string;
options: string[];
value?: string;
title?: string;
description?: string;
}
const {
id,
label,
abbr,
options,
value = options[0],
title = "",
description = "",
} = Astro.props;
---
<div
class="tui-segment"
data-segment-id={id}
data-tooltip-title={title}
data-tooltip-desc={description}
>
<span class:list={["tui-segment-label", { "has-abbr": !!abbr }]}>
<span class="full">{label}</span>
{abbr && <span class="abbr">{abbr}</span>}
</span>
<div class="tui-segment-options" id={id} data-value={value}>
{
options.map((opt) => (
<button
type="button"
class:list={[
"tui-segment-option",
{ active: opt === value },
]}
data-value={opt}
>
{opt}
</button>
))
}
</div>
</div>
<style>
.tui-segment {
display: flex;
align-items: center;
gap: 12px;
font-size: 11px;
user-select: none;
}
.tui-segment-label {
min-width: 3ch;
font-weight: 700;
font-family: var(--font-mono);
color: #fff;
opacity: 0.7;
display: flex;
transition: all 0.2s;
}
.tui-segment-label .abbr {
display: none;
}
@media (max-width: 1400px) {
.tui-segment-label.has-abbr .full {
display: none;
}
.tui-segment-label.has-abbr .abbr {
display: inline;
}
}
.tui-segment-options {
display: flex;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.02);
border-radius: 2px;
overflow: hidden;
}
.tui-segment-option {
background: transparent;
border: none;
border-right: 1px solid rgba(255, 255, 255, 0.05);
color: #fff;
opacity: 0.6;
font-family: inherit;
font-size: inherit;
padding: 4px 10px;
cursor: pointer;
transition: all 0.2s;
min-width: 3ch;
text-align: center;
}
.tui-segment-option:last-child {
border-right: none;
}
.tui-segment-option:hover {
color: var(--accent-color);
opacity: 1;
background: color-mix(in srgb, var(--accent-color), transparent 95%);
}
.tui-segment-option.active {
background: var(--accent-color);
color: #fff;
font-weight: 700;
opacity: 1;
}
/* Hover the whole group */
.tui-segment:hover .tui-segment-label {
opacity: 1;
color: var(--accent-color);
}
.tui-segment:hover .tui-segment-options {
border-color: var(--accent-color);
}
</style>
<script>
function initSegments() {
document
.querySelectorAll(".tui-segment")
.forEach((segmentContainer) => {
const optionsContainer = segmentContainer.querySelector(
".tui-segment-options",
) as HTMLElement;
const buttons = segmentContainer.querySelectorAll(
".tui-segment-option",
);
if (!optionsContainer) return;
buttons.forEach((btn) => {
btn.addEventListener("click", (e) => {
e.stopPropagation();
const value = (btn as HTMLElement).dataset.value;
// Update active state
buttons.forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
// Update data attribute
optionsContainer.dataset.value = value;
// Dispatch custom event
optionsContainer.dispatchEvent(
new CustomEvent("segment-change", {
detail: { value },
bubbles: true,
}),
);
});
});
});
}
document.addEventListener("DOMContentLoaded", initSegments);
initSegments();
// Expose update function globally
(window as any).updateSegmentValue = function (
segmentId: string,
newValue: string,
) {
const container = document.getElementById(segmentId) as HTMLElement;
if (container) {
const buttons = container.querySelectorAll(".tui-segment-option");
buttons.forEach((btn) => {
btn.classList.toggle(
"active",
(btn as HTMLElement).dataset.value === newValue,
);
});
container.dataset.value = newValue;
}
};
</script>