Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 67 additions & 11 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,6 @@ oder auf den Token aus `whoami` um. Beim Deploy werden alle Nutzenden einmal
abgemeldet (per Silent-SSO meist sofort wieder angemeldet). Bereits
übernommen: ausleihbar (virtUOS/ausleihbar#105), kalkulierbar.

### @basicbar/ui (→ wird `ui/v0.7.2`)

**Sicherheits-Fix `stripHtml` / `isEmptyHtml`** (#20): Beide parsten über
`innerHTML` auf einem Element des Live-Dokuments — dort lädt der Browser
Bilder und führt Inline-Handler aus, `<img src=x onerror=…>` lief also, auch
ohne Einhängen. Jetzt wird in einem inerten `DOMParser`-Dokument geparst
(nichts wird ausgeführt oder geladen); `isEmptyHtml` erkennt Bilder über den
geparsten Body statt per Regex. Verhalten sonst unverändert.

Migration: Tarball auf `ui/v0.7.2`. Keine Code-Änderung nötig.

### basicbar-integrations (→ wird `integrations/v0.3.0`)

**`CapabilitiesView` entfernt** (Framework-Review: kein Tool hat
Expand Down Expand Up @@ -69,6 +58,73 @@ Migration.
- Doku-Drift zum Capabilities-Endpunkt in CLAUDE.md und EXTRAKTIONSPLAN
bereinigt.

## ui/v0.8.0 — 2026-10-10

### @basicbar/ui

Anlass: Auf Smartphones verdeckte die schwebende Übersetzungsleiste
(„DE · EN · Alle Felder übersetzen“) Formularinhalte (abstimmbar #179);
abstimmbar umging das mit einem verschachtelten Provider und zwei
CSS-Overrides auf Paket-Markup. Das wird jetzt Paket-Funktion.

- **Verschiebbare Übersetzungsleiste:** Die schwebende Leiste von
`TranslationFormProvider` hat links einen Griff (`GripVertical`,
Name/Tooltip `"Move translation controls"`). Ziehen mit Maus oder Touch
verschiebt die ganze Leiste per CSS-`translate` (setzt sich mit
Tailwind-Transform-Utilities in `controlsClassName` zusammen) – die Position aus
`controlsClassName` bleibt der Anker. Die Leiste bleibt vollständig im
Viewport (auch nach Fenstergrößenänderung); der Versatz wird pro Browser in
`localStorage` (`basicbar_translation_controls_offset`) gemerkt.
Tastatur: Pfeiltasten 16 px, mit Umschalt 64 px; `Pos1`/`Esc` oder
Doppelklick auf den Griff setzen zurück. Ein bloßer Klick tut nichts. Der
Griff erscheint nur, solange die Leiste `fixed`/`absolute` positioniert ist.
Abschalten: `movable={false}`.
- **Andockplatz `TranslationControlsSlot`** (`{ className?, media? }`): ein
Platzhalter, den eine Seite dorthin setzt, wo die Leiste im Fluss stehen
soll (z. B. oben im Formular). Solange ein Slot gemountet ist und seine
Media Query passt (Default `"(max-width: 767px)"`, exportiert als
`TRANSLATION_SLOT_MEDIA`, = unter Tailwind `md`), rendert der nächste
Provider die Leiste per Portal in den Slot – ohne Fixed-Positionierung,
Griff und Versatz. Sonst schwebt sie wie bisher; der leere Slot ist per
`:empty` ausgeblendet. Bei mehreren Slots gewinnt der zuletzt gemountete.
Neue Provider-Prop `slotControlsClassName` (Default `flex justify-end`,
also rechtsbündig) für die angedockte Variante; `controlsClassName` gilt nur
für die schwebende.
- **Rich-Text-Toolbar bricht um** (`flex-wrap`) statt horizontal zu scrollen
– auf schmalen Screens waren die versteckten Buttons nicht zu erahnen; wo
die Zeile passt, ändert sich nichts.
- **Sprach-Tabs von `TranslatableField`:** Trefferfläche per `::after` auf
≥ 24 px Höhe (WCAG 2.5.8), Optik unverändert.

Migration:

1. Tarball auf `ui/v0.8.0`, Vite-Dep-Cache leeren.
2. Neuen Übersetzungs-Key `"Move translation controls"` in den deutschen
Katalog aufnehmen (Vorschlag: `"Übersetzungsleiste verschieben"`).
3. Optional: `<TranslationControlsSlot />` oben in Formulare mit
übersetzbaren Feldern setzen (innerhalb des `TranslationFormProvider`), damit
die Leiste auf Smartphones im Fluss steht statt Inhalte zu verdecken. Wer
die Leiste in Tests über eine Hook-Klasse findet, gibt sie auch in
`slotControlsClassName` mit (z. B. `"my-hook flex justify-end"`).
Workarounds wie ein verschachtelter Provider pro Formular entfallen.
4. Lokale Overrides für Toolbar-Umbruch oder Tab-Trefferfläche (falls
vorhanden) entfernen.
5. Die schwebende Leiste hat jetzt einen Griff – wer das nicht will:
`movable={false}`.

## ui/v0.7.2 — 2026-10-07

### @basicbar/ui

**Sicherheits-Fix `stripHtml` / `isEmptyHtml`** (#20): Beide parsten über
`innerHTML` auf einem Element des Live-Dokuments — dort lädt der Browser
Bilder und führt Inline-Handler aus, `<img src=x onerror=…>` lief also, auch
ohne Einhängen. Jetzt wird in einem inerten `DOMParser`-Dokument geparst
(nichts wird ausgeführt oder geladen); `isEmptyHtml` erkennt Bilder über den
geparsten Body statt per Regex. Verhalten sonst unverändert.

Migration: Tarball auf `ui/v0.7.2`. Keine Code-Änderung nötig.

## ui/v0.7.1 — 2026-10-02

### @basicbar/ui
Expand Down
37 changes: 34 additions & 3 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ Tools identisch aussehen, übergeben sie dieselbe Ramp.
`<html lang>`-Sync nach WCAG 3.1.1). Die Kataloge bleiben im Tool.
- **`contentLang`** — `localizedText`/`localizedMap`/`setLocalizedLang` & Co.
für `{ lang: text }`-Inhalte (Spiegel des Backend-`resolve_translated_text`).
- **`TranslatableField` / `TranslationFormProvider`** — Sprach-Tabs pro
Feld plus „alle Felder übersetzen“ (siehe `TranslatableField.tsx`).
- **`TranslatableField` / `TranslationFormProvider` / `TranslationControlsSlot`**
— Sprach-Tabs pro Feld plus verschiebbare Leiste „alle Felder übersetzen“,
optional im Formular angedockt (siehe `TranslatableField.tsx`).
- **`RichText` / `stripHtml` / `isEmptyHtml`** — Rendern und Prüfen des
gespeicherten Rich-HTML; **`RichTextEditor`** (TipTap) als **eigener Entry**
`@basicbar/ui/rich-text-editor`, damit TipTap/ProseMirror nur in Bundles
Expand Down Expand Up @@ -225,6 +226,35 @@ unteren Rand auf schmalen Screens), setzt das Tool die Position per
<TranslationFormProvider translate={…} controlsClassName="fixed bottom-6 right-6 z-40 max-md:bottom-[5.5rem]">
```

**Verschieben:** Die schwebende Leiste hat links einen Griff (Ziehen mit
Maus/Touch, Pfeiltasten 16 px bzw. mit Umschalt 64 px; `Pos1`/`Esc` oder
Doppelklick setzen zurück). Der Versatz liegt als CSS-`translate` über der
Position aus `controlsClassName` und wird pro Browser in `localStorage`
(`basicbar_translation_controls_offset`) gemerkt. Abschalten mit
`movable={false}`.

**Andocken im Formular:** Wo die Leiste Inhalte verdecken würde (v. a. auf
Smartphones), setzt die Seite einen Platzhalter in den Fluss. Solange er
gemountet ist und `media` passt (Default `"(max-width: 767px)"`), rendert der
Provider die Leiste dort hinein – ohne Fixed-Positionierung und Griff; sonst
schwebt sie wie gewohnt. Bei mehreren Slots gewinnt der zuletzt gemountete –
auch wenn dessen `media` gerade nicht passt und ein älterer Slot passen würde
(dann schwebt die Leiste).

```tsx
<TranslationFormProvider
translate={…}
controlsClassName="fixed bottom-6 right-6 z-40"
slotControlsClassName="flex justify-end" // Default: rechtsbündig
>
…
<form className="grid gap-6">
<TranslationControlsSlot /> {/* optional: className, media */}
<TranslatableField … />
</form>
</TranslationFormProvider>
```

## Übersetzungs-Keys

Alle Strings des Pakets laufen über `t()` mit Englisch als Key (siehe
Expand All @@ -244,7 +274,8 @@ seinem Katalog bereit. Fehlende Keys fallen auf den englischen Text zurück.
`"A value in {{language}} is required."`.
- TranslationFormProvider: `"Show all fields in one language"`,
`"Show all fields in {{language}}"`, `"Translate all fields"`,
`"Translating…"`, `"Some fields could not be translated."`.
`"Translating…"`, `"Some fields could not be translated."`,
`"Move translation controls"`.
- Preferences: `"Preferences"`, `"Language"`, `"Appearance"`, `"Auto"`,
`"(follows your system)"`, `"Light"`, `"Dark"`.

Expand Down
4 changes: 2 additions & 2 deletions packages/ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@basicbar/ui",
"version": "0.7.2",
"version": "0.8.0",
"description": "Design-System-Basis der virtUOS -bar-Tools: Tailwind-Preset, Basis-Styles, Theme (Dark Mode), i18n-Bootstrap, contentLang, TranslatableField, Preferences, RichText; RichTextEditor (TipTap) als eigener Entry",
"license": "Apache-2.0",
"author": "Universität Osnabrück (virtUOS)",
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/src/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,9 @@ export function RichTextEditor({
}`}
>
{editable && (
<div className="flex flex-nowrap gap-1 overflow-x-auto border-b border-slate-200 dark:border-slate-800 px-2 py-1">
// Wraps onto further rows on narrow screens instead of a horizontal
// scroller whose hidden buttons had no affordance.
<div className="flex flex-wrap gap-1 border-b border-slate-200 dark:border-slate-800 px-2 py-1">
<ToolbarButton
label={t("Bold")}
active={editor.isActive("bold")}
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/src/TranslatableField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,16 @@ export function TranslatableField({
: missingRequired
? "border-amber-500"
: "border-current opacity-40";
// The ::after box extends the ~21px pill to a ≥24px hit area
// (WCAG 2.5.8) without changing its look.
return (
<button
key={lang.code}
type="button"
role="tab"
aria-selected={isActive}
onClick={() => setActive(lang.code)}
className={`flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-medium uppercase transition-colors ${
className={`relative flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] after:absolute after:inset-x-0 after:-inset-y-1 after:content-[''] font-medium uppercase transition-colors ${
isActive
? "bg-brand-400 text-slate-900"
: "bg-slate-100 text-slate-500 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
Expand Down
Loading
Loading