diff --git a/packages/ui/PROJEKT.md b/packages/ui/PROJEKT.md index 874b40636..4ff7c5493 100644 --- a/packages/ui/PROJEKT.md +++ b/packages/ui/PROJEKT.md @@ -81,10 +81,10 @@ Phase 0: ██████████ 100% (6/6 Aufgaben) ✅ Phase 1: ██████████ 100% (6/6 Aufgaben) ✅ Phase 2: ██████████ 100% (26/26 Aufgaben) ✅ Phase 3: ██████████ 100% (24/24 Aufgaben) ✅ - Webapp-Integration komplett -Phase 4: ██░░░░░░░░ 24% (4/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅, BaseIcon→OsIcon Migration ✅ +Phase 4: ████░░░░░░ 35% (6/17 Aufgaben) - OsButton ✅, OsIcon ✅, System-Icons ✅, BaseIcon→OsIcon Migration ✅, OsSpinner ✅, Spinner Webapp-Migration ✅ Phase 5: ░░░░░░░░░░ 0% (0/7 Aufgaben) ─────────────────────────────────────── -Gesamt: ████████░░ 77% (66/86 Aufgaben) +Gesamt: ████████░░ 79% (68/86 Aufgaben) ``` ### Katalogisierung (Details in KATALOG.md) @@ -133,6 +133,25 @@ System-Icons: Ocelot-Icons (separates Entry-Point): └─ 82 Icons (Feature-Icons + Kategorie-Icons aus Webapp migriert) +OsSpinner: +├─ size: ✅ xs, sm, md, lg, xl, 2xl (em-basiert) +├─ color: ✅ currentColor (erbt von Parent) +├─ a11y: ✅ role="status", aria-label="Loading" (customizable) +├─ decorative: ✅ aria-hidden="true" suppresses role/aria-label +├─ os-button: ✅ OsButton nutzt OsSpinner als Komponente (decorative) +├─ vue-compat: ✅ h() Render-Function mit isVue2 +└─ webapp: ✅ 4 Spinner migriert (DsSpinner + LoadingSpinner → OsSpinner) + +DsSpinner/LoadingSpinner → OsSpinner Webapp-Migration: ✅ +├─ ImageUploader.vue: LoadingSpinner → OsSpinner (size="lg") +├─ pages/profile: ds-spinner → os-spinner (size="lg") +├─ pages/groups: ds-spinner → os-spinner (size="lg") +├─ pages/admin: ds-spinner → os-spinner (size="xl") + ApolloQuery→apollo Option +├─ LoadingSpinner Komponente gelöscht +├─ ds-space centered → div+padding (Bugfix in 3 Seiten) +├─ Admin: ApolloQuery→$apollo.loading (Spinner war wg. SSR-Prefetch unsichtbar) +└─ infinite-loading: OsSpinner im spinner-Slot (index, profile, groups) + BaseIcon → OsIcon Webapp-Migration: ✅ ├─ 131 in 70+ Dateien → ├─ 82 SVGs in ocelot/icons/svgs/ (inkl. 17 Kategorie-Icons) @@ -147,11 +166,35 @@ BaseIcon → OsIcon Webapp-Migration: ✅ ## Aktueller Stand -**Letzte Aktualisierung:** 2026-02-15 (Session 22) +**Letzte Aktualisierung:** 2026-02-18 (Session 24) -**Aktuelle Phase:** Phase 4 - OsIcon ✅, BaseIcon → OsIcon Webapp-Migration ✅ +**Aktuelle Phase:** Phase 4 - OsIcon ✅, BaseIcon → OsIcon Migration ✅, OsSpinner ✅, Spinner Webapp-Migration ✅ -**Zuletzt abgeschlossen (Session 22 - BaseIcon → OsIcon Webapp-Migration):** +**Zuletzt abgeschlossen (Session 24 - OsSpinner Webapp-Migration + Refactoring):** +- [x] OsButton refactored: nutzt `h(OsSpinner, { 'aria-hidden': 'true' })` statt Inline-SVG +- [x] OsSpinner: Decorative-Modus (`aria-hidden="true"` unterdrückt role/aria-label) +- [x] `ButtonSize` Type exportiert (sm/md/lg/xl), `types.d.ts` Kommentar aktualisiert +- [x] `createSpinnerSvg.ts` wieder in OsSpinner.vue inlined (nur noch 1 Nutzer) +- [x] Webapp-Migration: 4 Spinner ersetzt (ImageUploader, profile, groups, admin) +- [x] LoadingSpinner Komponente gelöscht (ersetzt durch OsSpinner) +- [x] `` → `
` Bugfix in 3 Seiten +- [x] Admin-Seite: `` → `apollo`-Option + `$apollo.loading` (Spinner war wg. SSR-Prefetch unsichtbar) +- [x] `filterStatistics()` mutiert nicht mehr Originalobjekt (Destructuring statt `delete`) +- [x] `infinite-loading` Spinner-Slot: OsSpinner in allen 3 Nutzungen (index, profile, groups) +- [x] Einheitliches Spinner-Design: vue-infinite-loading Default-Spinner → OsSpinner + +**Zuvor abgeschlossen (Session 23 - OsSpinner Komponente):** +- [x] OsSpinner Komponente implementiert (size prop, currentColor, role="status", aria-label) +- [x] Vue 2/3 kompatibel via `h()` Render-Function mit `isVue2` +- [x] Storybook Stories: Playground, AllSizes, InheritColor, InlineWithText +- [x] Unit Tests: 23 Tests (rendering, size, accessibility, decorative mode, css, keyboard) +- [x] Visual Tests: 4 Tests (all-sizes, inherit-color, inline-text, keyboard a11y) +- [x] Accessibility: `role="status"`, `aria-label="Loading"` (customizable), axe-core checks +- [x] 100% Test-Coverage (Statements, Branches, Functions, Lines) +- [x] Completeness Check bestanden +- [x] OsButton Visual Tests: 19/19 bestanden (kein Regression durch Refactoring) + +**Zuvor abgeschlossen (Session 22 - BaseIcon → OsIcon Webapp-Migration):** - [x] 131 `` Nutzungen in 70+ Dateien → `` migriert - [x] 82 Ocelot-Icons in `packages/ui/src/ocelot/icons/svgs/` (von 1 auf 82) - [x] 17 Kategorie-Icons aus Webapp kopiert (networking, energy, psyche, movement, finance, child, mobility, shopping-cart, peace, politics, nature, science, health, media, spirituality, culture, miscellaneous) @@ -219,7 +262,7 @@ BaseIcon → OsIcon Webapp-Migration: ✅ - [x] Session 11: Wasserfarben-Farbschema, Stories konsolidiert, Keyboard A11y **Nächste Schritte:** -- [ ] OsSpinner Komponente (vereint DsSpinner + LoadingSpinner) +- [x] OsSpinner Webapp-Migration (DsSpinner + LoadingSpinner → OsSpinner) ✅ - [ ] OsCard Komponente (vereint DsCard + BaseCard) - [ ] Weitere Tier 1 Komponenten - [ ] Browser-Fehler untersuchen: `TypeError: Cannot read properties of undefined (reading 'heartO')` (ocelotIcons undefined im Browser trotz korrekter Webpack-Aliase) @@ -473,7 +516,8 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ **Tier 1: Kern-Komponenten** - [x] OsIcon (vereint DsIcon + BaseIcon) ✅ System-Icons + vite-svg-icon Plugin -- [ ] OsSpinner (vereint DsSpinner + LoadingSpinner) +- [x] OsSpinner (vereint DsSpinner + LoadingSpinner) ✅ OsButton nutzt OsSpinner als Komponente +- [x] OsSpinner Webapp-Migration ✅ 4 Spinner migriert, LoadingSpinner gelöscht, Admin ApolloQuery→$apollo.loading - [x] OsButton (vereint DsButton + BaseButton) ✅ Entwickelt in Phase 2 - [ ] OsCard (vereint DsCard + BaseCard) @@ -1577,6 +1621,14 @@ Bei der Migration werden: | 2026-02-15 | **8 Snapshots gelöscht** | Stale Snapshot-Dateien entfernt nach BaseIcon → OsIcon Migration | | 2026-02-15 | **CSS Migration** | `.base-icon` → `.os-icon` in main.scss und Category/index.vue | | 2026-02-15 | **Jest Mock ocelot** | `test/__mocks__/@ocelot-social/ui/ocelot.js` für ocelotIcons in Jest-Umgebung | +| 2026-02-18 | **OsSpinner Komponente** | Neue Komponente: size (xs-2xl, em-basiert), currentColor, role="status", aria-label; Vue 2/3 via h() + isVue2 | +| 2026-02-18 | **OsSpinner Decorative** | `aria-hidden="true"` unterdrückt role/aria-label; OsButton nutzt OsSpinner als Komponente (decorative) | +| 2026-02-18 | **ButtonSize Type** | `ButtonSize` (sm/md/lg/xl) exportiert aus button.variants.ts; types.d.ts Kommentar: Size ist Vokabular, nicht Pflicht | +| 2026-02-18 | **OsSpinner Webapp-Migration** | 4 Stellen migriert: ImageUploader, profile, groups, admin; LoadingSpinner gelöscht | +| 2026-02-18 | **ds-space centered Bugfix** | `` → `
` in 3 Seiten (Styleguide-Bug) | +| 2026-02-18 | **Admin Spinner Fix** | `` → `apollo`-Option + `$apollo.loading`; SSR-Prefetch verhinderte Loading-State im Client | +| 2026-02-18 | **filterStatistics Fix** | `delete data.__typename` → Destructuring `{ __typename, ...rest }` (keine Mutation des Originalobjekts) | +| 2026-02-18 | **infinite-loading Spinner-Slot** | OsSpinner im `spinner`-Slot von vue-infinite-loading in 3 Seiten (index, profile, groups); einheitliches Spinner-Design | --- diff --git a/packages/ui/src/components/OsButton/OsButton.vue b/packages/ui/src/components/OsButton/OsButton.vue index e85c268bd..98899ff98 100644 --- a/packages/ui/src/components/OsButton/OsButton.vue +++ b/packages/ui/src/components/OsButton/OsButton.vue @@ -1,11 +1,12 @@ diff --git a/packages/ui/src/components/OsSpinner/__screenshots__/chromium/all-sizes.png b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/all-sizes.png new file mode 100644 index 000000000..8b7a63823 Binary files /dev/null and b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/all-sizes.png differ diff --git a/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inherit-color.png b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inherit-color.png new file mode 100644 index 000000000..5476d1e68 Binary files /dev/null and b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inherit-color.png differ diff --git a/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inline-text.png b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inline-text.png new file mode 100644 index 000000000..7fdbe2ad4 Binary files /dev/null and b/packages/ui/src/components/OsSpinner/__screenshots__/chromium/inline-text.png differ diff --git a/packages/ui/src/components/OsSpinner/index.ts b/packages/ui/src/components/OsSpinner/index.ts new file mode 100644 index 000000000..9eb190167 --- /dev/null +++ b/packages/ui/src/components/OsSpinner/index.ts @@ -0,0 +1,2 @@ +export { default as OsSpinner } from './OsSpinner.vue' +export { SPINNER_SIZES } from './spinner.variants' diff --git a/packages/ui/src/components/OsSpinner/spinner.variants.ts b/packages/ui/src/components/OsSpinner/spinner.variants.ts new file mode 100644 index 000000000..7b3e78cc0 --- /dev/null +++ b/packages/ui/src/components/OsSpinner/spinner.variants.ts @@ -0,0 +1,13 @@ +import type { Size } from '#src/types' + +/** + * Spinner size classes (em-based, scales with parent font-size) + */ +export const SPINNER_SIZES: Record = { + xs: 'h-[0.75em] w-[0.75em]', + sm: 'h-[1em] w-[1em]', + md: 'h-[1.5em] w-[1.5em]', + lg: 'h-[2em] w-[2em]', + xl: 'h-[2.5em] w-[2.5em]', + '2xl': 'h-[3em] w-[3em]', +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 3cea2d04d..f416e68f3 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -7,7 +7,7 @@ * - Registered globally when using the plugin: app.use(OcelotUI) */ -export { OsButton, buttonVariants, type ButtonVariants } from './OsButton' +export { OsButton, buttonVariants, type ButtonSize, type ButtonVariants } from './OsButton' export { OsIcon, ICON_SIZES, @@ -17,3 +17,4 @@ export { SYSTEM_ICONS, type SystemIconName, } from './OsIcon' +export { OsSpinner, SPINNER_SIZES } from './OsSpinner' diff --git a/packages/ui/src/types.d.ts b/packages/ui/src/types.d.ts index dbd3bf433..8e30fe21d 100644 --- a/packages/ui/src/types.d.ts +++ b/packages/ui/src/types.d.ts @@ -1,13 +1,15 @@ /** - * Component prop types based on Tailwind CSS scales + * Shared vocabulary types for component props. * - * These types ensure consistency across all components. - * When a component supports a prop, it must support all values of that scale. + * These types define a common naming convention across components. + * Components pick the subset that makes sense for them: + * - OsIcon/OsSpinner: all 6 sizes (inline elements, em-based) + * - OsButton: sm–xl (interactive element, pixel-based touch targets) */ /** - * Size scale for components (buttons, inputs, avatars, etc.) - * Maps to Tailwind's text/spacing scale + * Size vocabulary shared across components. + * Each component supports the subset that makes sense for its context. */ export type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' diff --git a/webapp/Dockerfile b/webapp/Dockerfile index 3f2110311..e192d33f1 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -13,9 +13,8 @@ RUN apk --no-cache add git python3 make g++ RUN mkdir -p /app WORKDIR /app COPY packages/ui . -RUN --mount=type=cache,target=/yarn-cache,sharing=locked \ - yarn install --production=false --frozen-lockfile --non-interactive --cache-folder /yarn-cache -RUN yarn run build +RUN npm ci +RUN npm run build FROM node:25.6.1-alpine AS base LABEL org.label-schema.name="ocelot.social:webapp" diff --git a/webapp/Dockerfile.maintenance b/webapp/Dockerfile.maintenance index da2d12173..f673c91ef 100644 --- a/webapp/Dockerfile.maintenance +++ b/webapp/Dockerfile.maintenance @@ -23,9 +23,8 @@ RUN apk --no-cache add git python3 make g++ RUN mkdir -p /app WORKDIR /app COPY packages/ui . -RUN --mount=type=cache,target=/yarn-cache,sharing=locked \ - yarn install --production=false --frozen-lockfile --non-interactive --cache-folder /yarn-cache -RUN yarn run build +RUN npm ci +RUN npm run build FROM node:25.6.1-alpine AS build ENV NODE_ENV="production" diff --git a/webapp/components/Uploader/ImageUploader.vue b/webapp/components/Uploader/ImageUploader.vue index 2a6241ea0..9de81a605 100644 --- a/webapp/components/Uploader/ImageUploader.vue +++ b/webapp/components/Uploader/ImageUploader.vue @@ -7,7 +7,7 @@ :use-custom-slot="true" @vdropzone-file-added="fileAdded" > - +
{{ $t('contribution.teaserImage.supportedFormats') }} @@ -58,20 +58,19 @@ - - diff --git a/webapp/pages/admin/index.vue b/webapp/pages/admin/index.vue index 84cc85199..91657da8f 100644 --- a/webapp/pages/admin/index.vue +++ b/webapp/pages/admin/index.vue @@ -1,69 +1,70 @@