xtablo-source/docs/design-system/selects.html

303 lines
15 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Selects</title>
<link rel="stylesheet" href="../../go-backend/static/tailwind.css">
<link rel="stylesheet" href="../../go-backend/static/styles.css">
</head>
<body>
<main class="catalog-page"><nav class="catalog-nav" aria-label="Catalog navigation"><a href="./index.html" class="catalog-home-link">Catalog</a><div class="catalog-nav-links"><a href="./tokens.html" class="catalog-nav-link">Tokens</a><a href="./buttons.html" class="catalog-nav-link">Buttons</a><a href="./badges.html" class="catalog-nav-link">Badges</a><a href="./icon-buttons.html" class="catalog-nav-link">Icon Buttons</a><a href="./inputs.html" class="catalog-nav-link">Inputs</a><a href="./selects.html" class="catalog-nav-link is-active">Selects</a><a href="./form-fields.html" class="catalog-nav-link">Form Fields</a><a href="./modals.html" class="catalog-nav-link">Modals</a><a href="./spacing.html" class="catalog-nav-link">Spacing</a><a href="./tables.html" class="catalog-nav-link">Tables</a><a href="./empty-states.html" class="catalog-nav-link">Empty States</a><a href="./cards.html" class="catalog-nav-link">Cards</a></div></nav><header class="catalog-page-header"><p class="catalog-eyebrow">Design System</p><h1>Selects</h1><p>Single and multi-value select controls with a shared server-rendered shell.</p></header><div class="catalog-example-list"><section class="catalog-example"><div class="catalog-example-copy"><h2>Single select</h2><p>Single-choice dropdown with the shared input shell and custom chevron.</p></div><div class="catalog-example-preview"><div class="ui-select" data-ui-select-root data-ui-select-multiple="false" data-placeholder="Select a status" data-selected-label="In progress"><select id="status-native" name="status" class="ui-select-native" data-ui-select-native><option value="">Select a status</option> <option value="todo">To do</option><option value="in-progress" selected>In progress</option><option value="done">Done</option></select> <button id="status" type="button" class="ui-select-control" data-ui-select-trigger aria-haspopup="listbox" aria-expanded="false" aria-controls="status-menu"><span class="ui-select-value-wrapper" data-ui-select-label>In progress</span> <span class="ui-select-arrow-zone"><svg class="ui-select-arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg></span></button><div id="status-menu" class="ui-select-menu" data-ui-select-menu role="listbox" hidden><button type="button" class="ui-select-option" data-ui-select-option data-value="todo" data-label="To do" role="option" aria-selected="false"><span class="ui-select-option-text">To do</span> <span class="ui-select-option-check" aria-hidden="true"></span></button><button type="button" class="ui-select-option is-selected" data-ui-select-option data-value="in-progress" data-label="In progress" role="option" aria-selected="true"><span class="ui-select-option-text">In progress</span> <span class="ui-select-option-check" aria-hidden="true"></span></button><button type="button" class="ui-select-option" data-ui-select-option data-value="done" data-label="Done" role="option" aria-selected="false"><span class="ui-select-option-text">Done</span> <span class="ui-select-option-check" aria-hidden="true"></span></button></div><script>
(function () {
if (!window.__uiSelectInitAll) {
window.__uiSelectSetOpen = function (root, open) {
var trigger = root.querySelector("[data-ui-select-trigger]");
var menu = root.querySelector("[data-ui-select-menu]");
if (!trigger || !menu) {
return;
}
root.classList.toggle("is-open", open);
trigger.setAttribute("aria-expanded", open ? "true" : "false");
menu.hidden = !open;
};
window.__uiSelectCloseAll = function (exceptRoot) {
document.querySelectorAll("[data-ui-select-root].is-open").forEach(function (root) {
if (root !== exceptRoot) {
window.__uiSelectSetOpen(root, false);
}
});
};
window.__uiSelectSync = function (root) {
var nativeSelect = root.querySelector("[data-ui-select-native]");
var outlet = root.querySelector("[data-ui-select-label]");
var placeholder = root.getAttribute("data-placeholder") || "";
var multiple = root.getAttribute("data-ui-select-multiple") === "true";
if (!nativeSelect || !outlet) {
return;
}
var labels = Array.from(nativeSelect.options).filter(function (option) {
return option.selected && option.value !== "";
}).map(function (option) {
return option.textContent;
});
root.setAttribute("data-selected-label", labels.join(", "));
outlet.innerHTML = "";
if (labels.length === 0) {
var placeholderNode = document.createElement("span");
placeholderNode.className = "ui-select-placeholder";
placeholderNode.textContent = placeholder;
outlet.appendChild(placeholderNode);
} else if (multiple) {
labels.forEach(function (label) {
var chip = document.createElement("span");
chip.className = "ui-select-chip";
chip.textContent = label;
outlet.appendChild(chip);
});
} else {
outlet.textContent = labels[0];
}
root.querySelectorAll("[data-ui-select-option]").forEach(function (optionButton) {
var selected = Array.from(nativeSelect.options).some(function (option) {
return option.value === optionButton.getAttribute("data-value") && option.selected;
});
optionButton.classList.toggle("is-selected", selected);
optionButton.setAttribute("aria-selected", selected ? "true" : "false");
});
};
window.__uiSelectToggleValue = function (root, optionButton) {
var nativeSelect = root.querySelector("[data-ui-select-native]");
var multiple = root.getAttribute("data-ui-select-multiple") === "true";
var value = optionButton.getAttribute("data-value");
if (!nativeSelect || value === null) {
return;
}
Array.from(nativeSelect.options).forEach(function (option) {
if (option.value !== value && !multiple) {
option.selected = false;
}
if (option.value === value) {
option.selected = multiple ? !option.selected : true;
}
});
window.__uiSelectSync(root);
nativeSelect.dispatchEvent(new Event("change", { bubbles: true }));
if (!multiple) {
window.__uiSelectSetOpen(root, false);
}
};
window.__uiSelectInitAll = function (scope) {
(scope || document).querySelectorAll("[data-ui-select-root]").forEach(function (root) {
window.__uiSelectSync(root);
});
};
document.addEventListener("click", function (event) {
var optionButton = event.target.closest("[data-ui-select-option]");
if (optionButton) {
var optionRoot = optionButton.closest("[data-ui-select-root]");
if (optionRoot) {
window.__uiSelectToggleValue(optionRoot, optionButton);
}
return;
}
var trigger = event.target.closest("[data-ui-select-trigger]");
if (trigger) {
var root = trigger.closest("[data-ui-select-root]");
var shouldOpen = root && !root.classList.contains("is-open");
window.__uiSelectCloseAll(root);
if (root) {
window.__uiSelectSetOpen(root, shouldOpen);
}
return;
}
if (!event.target.closest("[data-ui-select-root]")) {
window.__uiSelectCloseAll(null);
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
window.__uiSelectCloseAll(null);
}
});
document.addEventListener("htmx:afterSwap", function (event) {
window.__uiSelectInitAll(event.target);
});
}
window.__uiSelectInitAll(document);
})();
</script></div></div><pre class="catalog-example-snippet"><code>@ui.Select(ui.SelectProps{
Name: &#34;status&#34;,
Placeholder: &#34;Select a status&#34;,
Value: &#34;in-progress&#34;,
Options: []ui.SelectOption{
{Value: &#34;todo&#34;, Label: &#34;To do&#34;},
{Value: &#34;in-progress&#34;, Label: &#34;In progress&#34;},
{Value: &#34;done&#34;, Label: &#34;Done&#34;},
},
})</code></pre></section><section class="catalog-example"><div class="catalog-example-copy"><h2>Multiple select</h2><p>Multi-value selection with inline pills that stay form-compatible.</p></div><div class="catalog-example-preview"><div class="ui-select" data-ui-select-root data-ui-select-multiple="true" data-placeholder="Select multiple values" data-selected-label="Alice, Bob"><select id="assignee_ids-native" name="assignee_ids" class="ui-select-native" data-ui-select-native multiple><option value="alice" selected>Alice</option><option value="bob" selected>Bob</option><option value="charlie">Charlie</option></select> <button id="assignee_ids" type="button" class="ui-select-control" data-ui-select-trigger aria-haspopup="listbox" aria-expanded="false" aria-controls="assignee_ids-menu"><span class="ui-select-value-wrapper" data-ui-select-label><span class="ui-select-chip">Alice</span><span class="ui-select-chip">Bob</span></span> <span class="ui-select-arrow-zone"><svg class="ui-select-arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg></span></button><div id="assignee_ids-menu" class="ui-select-menu" data-ui-select-menu role="listbox" aria-multiselectable="true" hidden><button type="button" class="ui-select-option is-selected" data-ui-select-option data-value="alice" data-label="Alice" role="option" aria-selected="true"><span class="ui-select-option-text">Alice</span> <span class="ui-select-option-check" aria-hidden="true"></span></button><button type="button" class="ui-select-option is-selected" data-ui-select-option data-value="bob" data-label="Bob" role="option" aria-selected="true"><span class="ui-select-option-text">Bob</span> <span class="ui-select-option-check" aria-hidden="true"></span></button><button type="button" class="ui-select-option" data-ui-select-option data-value="charlie" data-label="Charlie" role="option" aria-selected="false"><span class="ui-select-option-text">Charlie</span> <span class="ui-select-option-check" aria-hidden="true"></span></button></div><script>
(function () {
if (!window.__uiSelectInitAll) {
window.__uiSelectSetOpen = function (root, open) {
var trigger = root.querySelector("[data-ui-select-trigger]");
var menu = root.querySelector("[data-ui-select-menu]");
if (!trigger || !menu) {
return;
}
root.classList.toggle("is-open", open);
trigger.setAttribute("aria-expanded", open ? "true" : "false");
menu.hidden = !open;
};
window.__uiSelectCloseAll = function (exceptRoot) {
document.querySelectorAll("[data-ui-select-root].is-open").forEach(function (root) {
if (root !== exceptRoot) {
window.__uiSelectSetOpen(root, false);
}
});
};
window.__uiSelectSync = function (root) {
var nativeSelect = root.querySelector("[data-ui-select-native]");
var outlet = root.querySelector("[data-ui-select-label]");
var placeholder = root.getAttribute("data-placeholder") || "";
var multiple = root.getAttribute("data-ui-select-multiple") === "true";
if (!nativeSelect || !outlet) {
return;
}
var labels = Array.from(nativeSelect.options).filter(function (option) {
return option.selected && option.value !== "";
}).map(function (option) {
return option.textContent;
});
root.setAttribute("data-selected-label", labels.join(", "));
outlet.innerHTML = "";
if (labels.length === 0) {
var placeholderNode = document.createElement("span");
placeholderNode.className = "ui-select-placeholder";
placeholderNode.textContent = placeholder;
outlet.appendChild(placeholderNode);
} else if (multiple) {
labels.forEach(function (label) {
var chip = document.createElement("span");
chip.className = "ui-select-chip";
chip.textContent = label;
outlet.appendChild(chip);
});
} else {
outlet.textContent = labels[0];
}
root.querySelectorAll("[data-ui-select-option]").forEach(function (optionButton) {
var selected = Array.from(nativeSelect.options).some(function (option) {
return option.value === optionButton.getAttribute("data-value") && option.selected;
});
optionButton.classList.toggle("is-selected", selected);
optionButton.setAttribute("aria-selected", selected ? "true" : "false");
});
};
window.__uiSelectToggleValue = function (root, optionButton) {
var nativeSelect = root.querySelector("[data-ui-select-native]");
var multiple = root.getAttribute("data-ui-select-multiple") === "true";
var value = optionButton.getAttribute("data-value");
if (!nativeSelect || value === null) {
return;
}
Array.from(nativeSelect.options).forEach(function (option) {
if (option.value !== value && !multiple) {
option.selected = false;
}
if (option.value === value) {
option.selected = multiple ? !option.selected : true;
}
});
window.__uiSelectSync(root);
nativeSelect.dispatchEvent(new Event("change", { bubbles: true }));
if (!multiple) {
window.__uiSelectSetOpen(root, false);
}
};
window.__uiSelectInitAll = function (scope) {
(scope || document).querySelectorAll("[data-ui-select-root]").forEach(function (root) {
window.__uiSelectSync(root);
});
};
document.addEventListener("click", function (event) {
var optionButton = event.target.closest("[data-ui-select-option]");
if (optionButton) {
var optionRoot = optionButton.closest("[data-ui-select-root]");
if (optionRoot) {
window.__uiSelectToggleValue(optionRoot, optionButton);
}
return;
}
var trigger = event.target.closest("[data-ui-select-trigger]");
if (trigger) {
var root = trigger.closest("[data-ui-select-root]");
var shouldOpen = root && !root.classList.contains("is-open");
window.__uiSelectCloseAll(root);
if (root) {
window.__uiSelectSetOpen(root, shouldOpen);
}
return;
}
if (!event.target.closest("[data-ui-select-root]")) {
window.__uiSelectCloseAll(null);
}
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
window.__uiSelectCloseAll(null);
}
});
document.addEventListener("htmx:afterSwap", function (event) {
window.__uiSelectInitAll(event.target);
});
}
window.__uiSelectInitAll(document);
})();
</script></div></div><pre class="catalog-example-snippet"><code>@ui.Select(ui.SelectProps{
Name: &#34;assignee_ids&#34;,
Placeholder: &#34;Select multiple values&#34;,
Multiple: true,
Values: []string{&#34;alice&#34;, &#34;bob&#34;},
Options: []ui.SelectOption{
{Value: &#34;alice&#34;, Label: &#34;Alice&#34;},
{Value: &#34;bob&#34;, Label: &#34;Bob&#34;},
{Value: &#34;charlie&#34;, Label: &#34;Charlie&#34;},
},
})</code></pre></section></div></main>
</body>
</html>