Appearance
Assets: Labels and Icons
This section introduces two utilities provided by scalux that make it easy to centrally manage multilingual text content (labels) and icons adapted to different themes. These tools integrate seamlessly with your state logic and guarantee a consistent user experience.
Labels
The Labels utility lets you define and use labels according to the language currently active in the application, either through connected React components, reactive hooks, or direct string access.
With Labels, you can:
- Centralise textual labels.
- Easily handle several languages.
- Guarantee a consistent interface when a language is undefined (by falling back to the default language).
- Seamlessly integrate labels into React components (
connectLabels,useLabel). - Retrieve label strings directly when needed (
getLabel).
Syntax and configuration
tsx
import { State } from "scalux";
import { LabelComponentProps } from "scalux/helpers";
import React from "react";
const { Labels } = State({ language: "fr" });
// Labels configuration
const { connectLabels, mkUseLabel, getLabel } = Labels({
options: ["fr", "en"], // Supported languages
fallBack: "en", // Default language
items: {
Tool: {
fr: "Outil",
en: "Tool",
},
Edit: "Edit",
Design: "Design",
SearchPlaceholder: {
fr: "Rechercher...",
en: "Search...",
},
},
});
// --- Example with connectLabels ---
const Title = ({ text }: LabelComponentProps) => <h1>{text}</h1>;
const AppTitle = connectLabels({
language: (state) => state.language, // Language selector
render: Title,
});
const Titles = () => (
<div>
<AppTitle item="Tool" /> {/* <h1>Outil</h1> (if language="fr") */}
<AppTitle item="Edit" /> {/* <h1>Edit</h1> */}
</div>
);
// --- Example with useLabel ---
const useLabel = mkUseLabel((state) => state.language); // Create the hook with the selector
const MyInputComponent = () => {
const placeholder = useLabel("SearchPlaceholder"); // Hook to get reactive text
const tooltip = useLabel("Tool");
return (
<input
type="text"
placeholder={placeholder} // Use as a prop
title={tooltip} // Use as a prop
/>
);
// If state.language changes from "fr" to "en",
// placeholder will become "Search..." and tooltip will become "Tool" automatically.
};
// --- Example with getLabel ---
const logLabels = () => {
const toolLabelDefault = getLabel("Tool"); // Uses fallback language ("en") -> "Tool"
const toolLabelFrench = getLabel("Tool", "fr"); // Forces French -> "Outil"
const editLabel = getLabel("Edit"); // Static label -> "Edit"
console.log(toolLabelDefault, toolLabelFrench, editLabel);
};Code walkthrough
The Labels function (returned by State) receives a configuration object containing:
- options – the tuple of supported languages.
- fallback – the default language (must be one of
options). - items – an object where each key maps to either:
- a static string (used for all languages), or
- an object that assigns a specific label for each language.
Labels returns an object containing connectLabels, mkUseLabel, and getLabel:
connectLabels: A Higher-Order Component (HOC) connector that expects:- language – a selector function that returns the active language from the state.
- render – a
React.FC<LabelComponentProps>wheretype LabelComponentProps = { text: string }. It injects the correct label as thetextprop.
mkUseLabel: A factory function that creates a React hook.- It expects the same
languageselector asconnectLabels. - It returns the
useLabelhook. This hook takes anitemkey as an argument (useLabel("itemKey")) and returns the corresponding label string. The hook is reactive: if the language changes in the state, the component using the hook will re-render with the new label. Ideal for props likeplaceholder,aria-label,title, etc.
- It expects the same
getLabel: A simple function for direct access.- Syntax:
getLabel(item: keyof items, language?: string): string. - It takes the item key and optionally a language.
- If
languageis provided and valid, it returns the label for that language. - If
languageis omitted or invalid, it uses the language defined infallback. - This function is not reactive to state changes and can be used outside of React or when reactivity is not needed.
- Syntax:
Behaviour and edge-case handling
Language selection
- For
connectLabelsanduseLabel(viamkUseLabel), the selector(state: RootState) => stringdetermines the active language. If the returned language is not listed inoptions, the text automatically falls back to the language defined infallback, ensuring the UI always displays a valid label and reacts to state changes. - For
getLabel, if thelanguageargument is not provided or is not inoptions, thefallbacklanguage is used.
- For
Extensibility
- To add a new language, simply add its key to
optionsand define the corresponding labels for every item initemsthat requires a specific translation.
- To add a new language, simply add its key to
Icons
The Icons utility is designed to manage icons tailored to different themes (e.g. light and dark) by centralising their configuration.
With Icons, you can:
- Define theme-specific icons.
- Control icon properties (size and colour) via dedicated types.
- Ensure that the displayed icon always matches the application’s active theme.
Syntax and configuration
Valid icon properties are:
ts
type IconColors =
| "error"
| "disabled"
| "action"
| "inherit"
| "primary"
| "secondary"
| "info"
| "success"
| "warning";
type IconSize = "small" | "medium" | "large";
/** If both are set, customSize wins (explicit > implicit). */
type IconComponentProps = {
size?: IconSizePreset;
/** CSS length (px|em|rem|%, etc.). */
customSize?: string;
color?: IconColors;
};tsx
import { State } from "scalux";
import { IconComponentProps } from "scalux/helpers";
import { EditIconDark, EditIconLight, DeleteIcon } from "./icons";
const { Icons } = State({ theme: "light" });
const { connectIcons } = Icons({
options: ["light", "dark"], // Supported themes
default: "light", // Default theme
items: {
Edit: {
light: EditIconLight, // Component for light theme
dark: EditIconDark, // Component for dark theme
},
Delete: DeleteIcon, // Same icon for every theme
},
});
const AppIcon = connectIcons({
theme: (state) => state.theme,
});
const IconsDisplay = () => (
<div>
<AppIcon item="Edit" />
<AppIcon item="Delete" />
</div>
);Code walkthrough
The Icons constructor (returned by State) receives a configuration object containing:
- options – the tuple of supported themes.
- default – the default theme (must be one of
options). - items – a dictionary where each key maps to either
- a
React.FC<IconComponentProps>, or - an object that assigns a theme-specific icon component for each theme.
- a
Icons returns connectIcons, which expects:
- theme – a selector function that returns the active theme from the state.
Behaviour and edge-case handling
Theme selection
The selector(state: RootState) => stringdetermines the active theme. If the returned theme is not listed inoptions, the icon automatically falls back to the theme specified indefault, ensuring the UI always displays a valid icon.Extensibility
To add a new theme, just include its key inoptionsand define icons for every item initems.
Generating custom icons from SVG files
To simplify creating and using icons from static SVG files, scalux offers the svgIconBuilder utility. This tool converts your SVG files into React components that conform to React.FC<IconComponentProps>, making integration straightforward.
How it works
svgIconBuilder takes the relative path to the directory containing your static SVG files.
It then combines with the .useIcons method, which expects an object mapping each icon name to its corresponding SVG filename (without the extension).
Practical example
With Vite, static files usually reside in /public. To create icons from /public/svg/save.svg and /public/svg/open.svg:
ts
import { svgIconBuilder } from "scalux/helpers";
const { Save, Open } = svgIconBuilder("/svg").useIcons({
Save: "save",
Open: "open",
});Save and Open are now of type React.FC<IconComponentProps>.
