Add full shell, replaceable Omics adapter and bilingual interaction
Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useI18n } from "../../i18n";
|
||||
import {
|
||||
useEffect,
|
||||
useId,
|
||||
@@ -50,6 +51,7 @@ export function FleetActionSelector<ActionId extends string>({
|
||||
renderContext,
|
||||
label = "Batch action",
|
||||
}: FleetActionSelectorProps<ActionId>) {
|
||||
const { t } = useI18n();
|
||||
const [selectedActionId, setSelectedActionId] = useState<ActionId | "">("");
|
||||
const hintId = useId();
|
||||
const groups = useMemo(() => {
|
||||
@@ -63,10 +65,10 @@ export function FleetActionSelector<ActionId extends string>({
|
||||
grouped.get(action.group)!.push(action);
|
||||
}
|
||||
return order.map((group) => ({ group, actions: grouped.get(group)! }));
|
||||
}, [actions]);
|
||||
}, [actions, t]);
|
||||
const selectedAction = actions.find((action) => action.id === selectedActionId);
|
||||
const unavailableReason = selectedAction?.disabled
|
||||
? selectedAction.disabledReason ?? "This action is not available for the current selection."
|
||||
? selectedAction.disabledReason ?? t("This action is not available for the current selection.")
|
||||
: null;
|
||||
const actionScope = selectedAction?.scopeLabel;
|
||||
|
||||
@@ -85,33 +87,33 @@ export function FleetActionSelector<ActionId extends string>({
|
||||
return (
|
||||
<div className="thot-fleet-action-selector">
|
||||
<div className="thot-fleet-action-selector__scope" aria-live="polite">
|
||||
<strong>{actionScope ?? `${selectedCount} selected`}</strong>
|
||||
<strong>{actionScope ? t(actionScope) : t("{count} selected", { count: selectedCount })}</strong>
|
||||
{actionScope ? (
|
||||
<span>Action scope</span>
|
||||
<span>{t("Action scope")}</span>
|
||||
) : hiddenSelectedCount > 0 ? (
|
||||
<span>{hiddenSelectedCount} hidden by filter</span>
|
||||
<span>{hiddenSelectedCount} {t("hidden by filter")}</span>
|
||||
) : (
|
||||
<span>Selected rows</span>
|
||||
<span>{t("Selected rows")}</span>
|
||||
)}
|
||||
</div>
|
||||
<label className="sr-only" htmlFor={hintId}>{label}</label>
|
||||
<label className="sr-only" htmlFor={hintId}>{t(label)}</label>
|
||||
<select
|
||||
ref={selectRef}
|
||||
id={hintId}
|
||||
className="thot-fleet-action-selector__select"
|
||||
aria-label={label}
|
||||
aria-label={t(label)}
|
||||
value={selectedActionId}
|
||||
disabled={busy || selectedCount === 0}
|
||||
onChange={(event) => setSelectedActionId(event.target.value as ActionId | "")}
|
||||
>
|
||||
<option value="">Choose an action…</option>
|
||||
<option value="">{t("Choose an action…")}</option>
|
||||
{groups.map(({ group, actions: groupActions }) => (
|
||||
<optgroup key={group} label={group}>
|
||||
<optgroup key={group} label={t(group)}>
|
||||
{groupActions.map((action) => (
|
||||
<option key={action.id} value={action.id}>
|
||||
{action.label}
|
||||
{t(action.label)}
|
||||
{action.disabled && action.disabledReason
|
||||
? `, unavailable: ${action.disabledReason}`
|
||||
? t(", unavailable: {reason}", { reason: t(action.disabledReason) })
|
||||
: ""}
|
||||
</option>
|
||||
))}
|
||||
@@ -129,14 +131,12 @@ export function FleetActionSelector<ActionId extends string>({
|
||||
onClick={() => void run()}
|
||||
>
|
||||
<Play aria-hidden="true" />
|
||||
Run action
|
||||
</Button>
|
||||
{t("Run action")}</Button>
|
||||
<Button type="button" size="sm" variant="ghost" disabled={busy} onClick={onClear}>
|
||||
<X aria-hidden="true" /> Clear
|
||||
</Button>
|
||||
<X aria-hidden="true" /> {t("Clear")}</Button>
|
||||
{unavailableReason ? (
|
||||
<span id={`${hintId}-reason`} className="thot-fleet-action-selector__reason" role="status">
|
||||
{unavailableReason}
|
||||
{t(unavailableReason)}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user