mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-03-01 12:44:28 +00:00
refactor(webapp): vue3 migration os button as prop, remove obsolete buttons & inline single user buttons (#9214)
This commit is contained in:
parent
8b22235b30
commit
b60e270f4c
@ -1,6 +1,6 @@
|
||||
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||
|
||||
defineStep('the unread counter is removed', () => {
|
||||
cy.get('.notifications-menu .counter-icon')
|
||||
cy.get('.notifications-menu .counter-icon .count')
|
||||
.should('not.exist')
|
||||
})
|
||||
|
||||
@ -106,114 +106,47 @@ OsButton Features:
|
||||
├─ variant: ✅ primary, secondary, danger, warning, success, info, default
|
||||
├─ appearance: ✅ filled, outline, ghost
|
||||
├─ size: ✅ sm, md, lg, xl
|
||||
├─ disabled: ✅ mit hover/active-Override
|
||||
├─ disabled: ✅ mit hover/active-Override (nur as="button")
|
||||
├─ icon: ✅ slot-basiert (icon-system-agnostisch)
|
||||
├─ circle: ✅ rounded-full, größenabhängig (p-1.5 bis p-3)
|
||||
└─ loading: ✅ animated SVG spinner, aria-busy (Milestone 4b)
|
||||
├─ loading: ✅ animated SVG spinner, aria-busy (Milestone 4b)
|
||||
└─ as: ✅ polymorphes Rendering (button/a/NuxtLink/RouterLink)
|
||||
|
||||
as-Prop Migration: 15 <nuxt-link>/<a>-Wrapper in 15 Webapp-Dateien → as="nuxt-link"/as="a"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aktueller Stand
|
||||
|
||||
**Letzte Aktualisierung:** 2026-02-13 (Session 19)
|
||||
**Letzte Aktualisierung:** 2026-02-14 (Session 20)
|
||||
|
||||
**Aktuelle Phase:** Phase 3 ✅ ABGESCHLOSSEN + Code-Review-Feedback eingearbeitet
|
||||
**Aktuelle Phase:** Phase 3 ✅ ABGESCHLOSSEN + Code-Review-Feedback + `as`-Prop-Migration
|
||||
|
||||
**Zuletzt abgeschlossen:**
|
||||
- [x] Projektordner erstellt
|
||||
- [x] Planungsdokument erstellt
|
||||
- [x] Tech-Stack entschieden
|
||||
- [x] Branding-Architektur definiert
|
||||
- [x] Migrationsstrategie definiert
|
||||
- [x] **Phase 0: Komponenten-Analyse** (177 Komponenten katalogisiert)
|
||||
- [x] Button-Familie detailiert analysiert (Props, Styles, Konsolidierung)
|
||||
- [x] Modal-Familie detailiert analysiert (Architektur erkannt)
|
||||
- [x] Menu-Familie detailiert analysiert (3 Patterns identifiziert)
|
||||
- [x] Priorisierung erstellt (15 Komponenten in 4 Tiers)
|
||||
- [x] Konsolidierungsplan finalisiert
|
||||
- [x] **Phase 1: Vue 2.7 Upgrade** ✅
|
||||
- Vue 2.6.14 → 2.7.16
|
||||
- vue-template-compiler entfernt
|
||||
- @vue/composition-api entfernt
|
||||
- @nuxtjs/composition-api entfernt
|
||||
- Webpack-Alias für @vue/composition-api → vue
|
||||
- Webpack-Regel für ESM .mjs Module
|
||||
- **Unit-Tests: 157 Suites, 979 passed, 87 Snapshots** ✅
|
||||
- **Integrationstests: bestanden** ✅
|
||||
- [x] **Phase 2: Projekt-Setup** ✅
|
||||
- Vite + Vue 3 Projekt initialisiert
|
||||
- vue-demi für Vue 2/3 Kompatibilität
|
||||
- Vitest konfiguriert (integriert in vite.config.ts)
|
||||
- npm Package-Struktur mit korrekten exports
|
||||
- README.md Grundgerüst
|
||||
- LICENSE (Apache 2.0)
|
||||
- Plugin-Tests geschrieben
|
||||
- Tailwind CSS v4 mit @tailwindcss/vite
|
||||
- Dual-Build (style.css + tailwind.preset)
|
||||
- Dark Mode Grundstruktur (via Tailwind dark: Prefix)
|
||||
- Prop-Types definiert (Size, Rounded, Shadow, Variant)
|
||||
- Branding-Architektur (keine Defaults, validateCssVariables)
|
||||
- eslint-config-it4c eingerichtet (v0.8.0)
|
||||
- ESLint Flat Config mit Vue 3 + Vitest Modulen
|
||||
- Prettier-Integration via eslint-plugin-prettier
|
||||
- GitHub Workflows (ui-lint.yml, ui-test.yml, ui-build.yml)
|
||||
- 100% Test-Coverage Requirement
|
||||
- .tool-versions (Node 25.5.0, konsistent mit Dockerfiles)
|
||||
- Example Apps für Kompatibilitätstests (4er-Matrix)
|
||||
- GitHub Workflow ui-compatibility.yml für Vue 2/3 Tests (inkl. Lint)
|
||||
- Eigene ESLint + Prettier Configs für Example Apps
|
||||
- Type Assertions für CI-Kompatibilität (`as unknown as Plugin`)
|
||||
- Bundle Size Check (size-limit) mit ui-size.yml Workflow
|
||||
- Package-Validierung (publint, arethetypeswrong) mit CJS/ESM Types
|
||||
- Kompatibilitätstest-Workflow mit 4 Example Apps (Vue 2/3 × Tailwind/CSS)
|
||||
- release-please Manifest-Konfiguration (Monorepo-Setup)
|
||||
- npm Publish Workflow (ui-release.yml)
|
||||
- CONTRIBUTING.md (Entwickler-Leitfaden)
|
||||
- Dependabot für UI-Package und Example Apps konfiguriert
|
||||
- CSS-Build separat via Tailwind CLI (closeBundle Hook)
|
||||
- CVA (class-variance-authority) für typsichere Varianten
|
||||
- cn() Utility für Tailwind-Klassen-Merge (clsx + tailwind-merge)
|
||||
- OsButton Komponente mit CVA-Varianten implementiert
|
||||
- ESLint-Konfiguration angepasst (vue/max-attributes-per-line, import-x/no-relative-parent-imports)
|
||||
- Storybook 10 für Dokumentation eingerichtet (Wasserfarben-Theme)
|
||||
- OsButton.stories.ts mit Playground + allen Varianten/Appearances/Sizes
|
||||
- Storybook Build-Konfiguration (viteFinal entfernt Library-Plugins)
|
||||
- Docker Setup (Dockerfile, docker-compose, ui-docker.yml)
|
||||
- Visual Regression Tests (Playwright, colocated) mit integriertem A11y-Check
|
||||
- Completeness Check (verify Script prüft Story, Visual, checkA11y, Keyboard, Varianten)
|
||||
- ESLint Plugins: vuejs-accessibility, playwright, storybook, jsdoc
|
||||
**Abgeschlossene Phasen:**
|
||||
- [x] Phase 0: Analyse (177 Komponenten katalogisiert)
|
||||
- [x] Phase 1: Vue 2.7 Upgrade (2.6.14 → 2.7.16, 979 Tests ✅)
|
||||
- [x] Phase 2: Projekt-Setup (Vite, vue-demi, Tailwind v4, CVA, Storybook 10, CI/CD, 100% Coverage)
|
||||
- [x] Phase 3: Webapp-Integration — 133 os-button in 79 Dateien, 0 base-button/ds-button verbleibend
|
||||
|
||||
**Zuvor abgeschlossen (Session 18 - CodeRabbit Review Feedback: data-test Selektoren, Accessibility, Bugfixes):**
|
||||
- [x] Cypress-Selektoren: `.user-content-menu button` → `[data-test="content-menu-button"]` (2 Step-Definitions)
|
||||
- [x] Cypress-Selektoren: `.content-menu button` → `[data-test="content-menu-button"]` (Admin.PinPost + ReportContent)
|
||||
- [x] muted-users.vue: `data-test="unmute-btn"` + `aria-label` auf Unmute-Button
|
||||
- [x] blocked-users.vue: `data-test="unblock-btn"` + `aria-label` auf Unblock-Button
|
||||
- [x] ProfileList.vue: `data-test="load-all-connections-btn"` + FollowList.spec.js Selektoren aktualisiert
|
||||
- [x] FollowButton.vue: `data-test="follow-btn"` + Spec-Selektoren aktualisiert
|
||||
- [x] JoinLeaveButton.vue: `data-test="join-leave-btn"` + `.native` von `@mouseenter`/`@mouseleave` entfernt
|
||||
- [x] LoginButton.vue: `data-test="login-btn"` + `aria-label="$t('login.login')"` + Spec-Selektoren aktualisiert
|
||||
- [x] ReportRow.spec.js: `button[data-variant="danger"]` → `[data-test="confirm"]`
|
||||
- [x] CtaJoinLeaveGroup.spec.js: Selektor auf `[data-test="join-leave-btn"]` aktualisiert
|
||||
- [x] DisableModal.vue: `finally { this.loading = false }` für Loading-State-Reset
|
||||
- [x] ReleaseModal.vue: `:loading="loading"` + `this.loading = true` + `finally { this.loading = false }`
|
||||
- [x] ChangePassword.vue: `:disabled="errors"` → `:disabled="!!errors"` (Boolean-Cast)
|
||||
- [x] Password/Change.vue: Unbenutzte `disabled: true` aus data() entfernt + 2 tote Tests entfernt
|
||||
- [x] MenuBar.vue: Unbenutztes `ref="linkButton"` entfernt
|
||||
- [x] GroupForm.vue: Cancel-Button `variant="default" appearance="filled"` (per User-Anweisung)
|
||||
- [x] `appearance="filled"` ergänzt: donations.vue, LoginForm.vue, EnterNonce.vue
|
||||
- [x] LoginForm.vue: CSS `.login-form button` → `.login-form button[type='submit']`
|
||||
- [x] pages/index.vue: Redundantes `class="my-filter-button"` von `<base-icon>` entfernt
|
||||
- [x] MySomethingList.vue: `:title` + `:aria-label` auf Edit/Delete-Buttons (Tooltip beibehalten)
|
||||
- [x] A11y aria-label auf icon-only Buttons: admin/users (search + edit), AddChatRoomByUserSearch (close), EmbedComponent (close), groups/index (create), profile/_id/_slug (new post), groups/_id/_slug (new post), CustomButton (2x tooltip), HeaderMenu (hamburger), ImageUploader (crop-cancel), ContentMenu (menu), HeaderButton (filter-remove), InviteButton (invite)
|
||||
- [x] post/_id/_slug/index.vue: Zustandsabhängiges `aria-label` (`post.sensitiveContent.show/hide`)
|
||||
- [x] ComponentSlider.vue: `aria-label` mit Interpolation (`component-slider.step`)
|
||||
- [x] i18n: `actions.search`, `actions.close`, `actions.menu` in allen 9 Sprachdateien
|
||||
- [x] i18n: `site.navigation` in allen 9 Sprachdateien
|
||||
- [x] i18n: `post.sensitiveContent.show/hide` in allen 9 Sprachdateien
|
||||
- [x] i18n: `component-slider.step` in allen 9 Sprachdateien
|
||||
**Zuletzt abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):**
|
||||
- [x] OsButton: `as` Prop implementiert (polymorphe Komponente: `button`, `a`, `nuxt-link`, `router-link`, Custom-Komponenten)
|
||||
- [x] Naming-Konvention: `tag` → `as` (moderner Standard: Headless UI, Radix Vue, Chakra UI, PrimeVue)
|
||||
- [x] `disabled`/`type`/`loading` nur bei `as="button"` (Links haben kein natives `disabled`-Attribut)
|
||||
- [x] Stories: `Polymorphic` Story + Playground `as`-Selektor (button/a)
|
||||
- [x] Visual Test: `polymorphic` Screenshot + a11y-Check
|
||||
- [x] 15 `<nuxt-link>`/`<a>`-Wrapper in 15 Webapp-Dateien → `as="nuxt-link"` / `as="a"` migriert:
|
||||
- GroupButton.vue, CtaUnblockAuthor.vue, terms-and-conditions-confirm.vue
|
||||
- CustomButton.vue (v-if/v-else → computed `linkTag`/`linkProps` konsolidiert)
|
||||
- groups/index.vue, GroupForm.vue, admin/users/index.vue
|
||||
- pages/index.vue (CSS `button.post-add-button-*` → `.post-add-button-*`)
|
||||
- profile/_id/_slug.vue (v-if auf ds-grid-item, symmetrisches Padding `$space-x-small`)
|
||||
- groups/_id/_slug.vue, MapButton.vue
|
||||
- ChatNotificationMenu.vue, Chat.vue, UserTeaserPopover.vue
|
||||
- NotificationMenu.vue (3 Instanzen, 2 zu einem Button konsolidiert via counter-icon)
|
||||
- [x] Verifiziert: 0 verbleibende `<nuxt-link>`/`<a>`-Wrapper um `<os-button>` in Webapp
|
||||
|
||||
**Zuletzt abgeschlossen (Session 19 - CodeRabbit Review Feedback: Cleanup, Accessibility, Bugfixes):**
|
||||
**Zuvor abgeschlossen (Session 19 - CodeRabbit Review Feedback: Cleanup, Accessibility, Bugfixes):**
|
||||
- [x] donations.vue: Redundantes `:checked="showDonations"` entfernt (v-model setzt checked bereits)
|
||||
- [x] MySomethingList.vue: Disabled-Logik vereinfacht `!(!isEditing || (isEditing && !disabled))` → `isEditing && disabled`
|
||||
- [x] button.variants.ts: Hardcoded Fallback `#e5e3e8` entfernt → `var(--color-disabled)` (konsistent mit filled/index.css)
|
||||
@ -253,7 +186,36 @@ OsButton Features:
|
||||
- [x] HeaderMenu.vue: `beforeDestroy`-Hook ergänzt — Scroll-Listener wird jetzt entfernt (Memory-Leak)
|
||||
- [x] MenuLegend.vue: `variant="primary"` auf Trigger-Button (konsistent mit Toolbar-Buttons in MenuBar.vue)
|
||||
|
||||
**Zuvor abgeschlossen (Session 18 - Code-Review Feedback, OsButton Refactoring, Accessibility):**
|
||||
**Zuvor abgeschlossen (Session 18 - CodeRabbit Review Feedback: data-test Selektoren, Accessibility, Bugfixes):**
|
||||
- [x] Cypress-Selektoren: `.user-content-menu button` → `[data-test="content-menu-button"]` (2 Step-Definitions)
|
||||
- [x] Cypress-Selektoren: `.content-menu button` → `[data-test="content-menu-button"]` (Admin.PinPost + ReportContent)
|
||||
- [x] muted-users.vue: `data-test="unmute-btn"` + `aria-label` auf Unmute-Button
|
||||
- [x] blocked-users.vue: `data-test="unblock-btn"` + `aria-label` auf Unblock-Button
|
||||
- [x] ProfileList.vue: `data-test="load-all-connections-btn"` + FollowList.spec.js Selektoren aktualisiert
|
||||
- [x] FollowButton.vue: `data-test="follow-btn"` + Spec-Selektoren aktualisiert
|
||||
- [x] JoinLeaveButton.vue: `data-test="join-leave-btn"` + `.native` von `@mouseenter`/`@mouseleave` entfernt
|
||||
- [x] LoginButton.vue: `data-test="login-btn"` + `aria-label="$t('login.login')"` + Spec-Selektoren aktualisiert
|
||||
- [x] ReportRow.spec.js: `button[data-variant="danger"]` → `[data-test="confirm"]`
|
||||
- [x] CtaJoinLeaveGroup.spec.js: Selektor auf `[data-test="join-leave-btn"]` aktualisiert
|
||||
- [x] DisableModal.vue: `finally { this.loading = false }` für Loading-State-Reset
|
||||
- [x] ReleaseModal.vue: `:loading="loading"` + `this.loading = true` + `finally { this.loading = false }`
|
||||
- [x] ChangePassword.vue: `:disabled="errors"` → `:disabled="!!errors"` (Boolean-Cast)
|
||||
- [x] Password/Change.vue: Unbenutzte `disabled: true` aus data() entfernt + 2 tote Tests entfernt
|
||||
- [x] MenuBar.vue: Unbenutztes `ref="linkButton"` entfernt
|
||||
- [x] GroupForm.vue: Cancel-Button `variant="default" appearance="filled"` (per User-Anweisung)
|
||||
- [x] `appearance="filled"` ergänzt: donations.vue, LoginForm.vue, EnterNonce.vue
|
||||
- [x] LoginForm.vue: CSS `.login-form button` → `.login-form button[type='submit']`
|
||||
- [x] pages/index.vue: Redundantes `class="my-filter-button"` von `<base-icon>` entfernt
|
||||
- [x] MySomethingList.vue: `:title` + `:aria-label` auf Edit/Delete-Buttons (Tooltip beibehalten)
|
||||
- [x] A11y aria-label auf icon-only Buttons: admin/users (search + edit), AddChatRoomByUserSearch (close), EmbedComponent (close), groups/index (create), profile/_id/_slug (new post), groups/_id/_slug (new post), CustomButton (2x tooltip), HeaderMenu (hamburger), ImageUploader (crop-cancel), ContentMenu (menu), HeaderButton (filter-remove), InviteButton (invite)
|
||||
- [x] post/_id/_slug/index.vue: Zustandsabhängiges `aria-label` (`post.sensitiveContent.show/hide`)
|
||||
- [x] ComponentSlider.vue: `aria-label` mit Interpolation (`component-slider.step`)
|
||||
- [x] i18n: `actions.search`, `actions.close`, `actions.menu` in allen 9 Sprachdateien
|
||||
- [x] i18n: `site.navigation` in allen 9 Sprachdateien
|
||||
- [x] i18n: `post.sensitiveContent.show/hide` in allen 9 Sprachdateien
|
||||
- [x] i18n: `component-slider.step` in allen 9 Sprachdateien
|
||||
|
||||
**Zuvor abgeschlossen (Session 17 - Code-Review Feedback, OsButton Refactoring, Accessibility):**
|
||||
- [x] OsButton.vue vereinfacht: `vueAttrs()` Helper, Einmal-Variablen durch `cn()` ersetzt, `children` Array inline (217→227 Zeilen, aber lesbarer)
|
||||
- [x] OsButton: `@import "./animations.css"` vor `@source`-Direktiven verschoben (CSS-Spec-Konformität)
|
||||
- [x] CustomButton.vue: `isEmpty` aus `data()` entfernt → direkter Import im Computed
|
||||
@ -368,13 +330,13 @@ OsButton Features:
|
||||
- [x] `data-appearance` Attribut: robuste CSS-Selektoren statt fragile escaped Tailwind-Klassen
|
||||
- [x] Code-Review Feedback eingearbeitet (Unit-Tests, Testnamen, CSS-Selektoren)
|
||||
|
||||
**Zuvor abgeschlossen (Milestone 5 + Analyse):**
|
||||
**Zuvor abgeschlossen (Sessions 9-10 - Milestone 5, Analyse, Disabled-Styles):**
|
||||
- [x] Visuelle Validierung: 16/16 Buttons validiert ✅
|
||||
- [x] OsButton Features: `appearance` (outline, ghost), `xs` size, focus/active states
|
||||
- [x] Disabled-Styles: CSS-Variablen, hover/active-Override, Border-Fix
|
||||
- [x] Codebase-Analyse: 14 weitere migrierbare Buttons identifiziert (Scope: 16/35)
|
||||
|
||||
**Zuletzt erledigt (Phase 3):**
|
||||
**Zuvor abgeschlossen (Sessions 1-8 - Phase 3 Webapp-Integration):**
|
||||
- [x] vue-demi zur Webapp hinzugefügt (Vue 2.7 Kompatibilität)
|
||||
- [x] Webpack-Alias für vue-demi (nutzt Webapp's Vue 2.7 statt UI-Library's Vue 3)
|
||||
- [x] Webpack-Alias für @ocelot-social/ui (dist Pfade mit $ für exakten Match)
|
||||
@ -419,23 +381,11 @@ OsButton Features:
|
||||
- Verhindert Layout-Sprung wenn Button disabled wird
|
||||
|
||||
**Nächste Schritte:**
|
||||
1. ~~Phase 0: Komponenten-Analyse~~ ✅
|
||||
2. ~~Phase 1: Vue 2.7 Upgrade~~ ✅
|
||||
3. ~~**Phase 2: Projekt-Setup**~~ ✅ ABGESCHLOSSEN
|
||||
4. ~~**Phase 3: Webapp-Integration**~~ ✅ ABGESCHLOSSEN — 133 Buttons in 79 Dateien
|
||||
- [x] yarn link / Webpack-Alias in Webapp
|
||||
- [x] CSS-Variablen definieren (ocelot-ui-variables.scss)
|
||||
- [x] 16 Buttons migriert & validiert ✅
|
||||
- [x] Docker Build + CI-Kompatibilität
|
||||
- [x] **Milestone 4a:** 14 weitere Buttons (ohne neue Props) ✅
|
||||
- [x] **Milestone 4b:** icon/circle/loading Props implementieren ✅
|
||||
- [x] **Milestone 4c:** Alle verbleibenden Buttons migriert ✅
|
||||
- [x] **Code-Review Feedback:** Refactoring, A11y, Vue 3 Compat, CSS-Scoping ✅
|
||||
5. **Nächstes:**
|
||||
- [ ] GroupButton + MapButton in HeaderMenu inlinen (keine eigene Komponente nötig)
|
||||
- [ ] `compat/` Verzeichnis in packages/ui anlegen (temporäre Migrations-Wrapper)
|
||||
- [ ] BaseIcon nach `compat/` verschieben (131 Nutzungen, Voraussetzung für weitere Migrationen)
|
||||
- [ ] Snapshots/Tests aktualisieren, BaseButton-Komponente ggf. entfernen
|
||||
- [ ] Snapshots/Tests aktualisieren (nach `as`-Prop-Migration)
|
||||
- [ ] GroupButton + MapButton in HeaderMenu inlinen (keine eigene Komponente nötig)
|
||||
- [ ] `compat/` Verzeichnis in packages/ui anlegen (temporäre Migrations-Wrapper)
|
||||
- [ ] BaseIcon nach `compat/` verschieben (131 Nutzungen, Voraussetzung für weitere Migrationen)
|
||||
- [ ] BaseButton-Komponente ggf. entfernen
|
||||
|
||||
**Manuelle Setup-Aufgaben (außerhalb Code):**
|
||||
- [ ] `NPM_TOKEN` als GitHub Secret einrichten (für npm publish in ui-release.yml)
|
||||
@ -722,8 +672,6 @@ Jeder migrierte Button muss manuell geprüft werden: Normal, Hover, Focus, Activ
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
# VISION
|
||||
|
||||
## 1. Projektziel & Vision
|
||||
@ -782,7 +730,7 @@ Migration vorbereiten - schrittweise neue Komponenten in Vue 3 entwickeln, die d
|
||||
| Dateinamen | **PascalCase** | OsButton.vue, OsCard.vue |
|
||||
| i18n | **Nur Props** | Keine Default-Texte in Komponenten |
|
||||
| Breakpoints | **Tailwind Standard** | sm:640, md:768, lg:1024, xl:1280, 2xl:1536 |
|
||||
| Size Props | **Tailwind-Skala (vollständig)** | xs, sm, md, lg, xl, 2xl |
|
||||
| Size Props | **Tailwind-Skala** | sm, md, lg, xl (komponentenspezifisch) |
|
||||
| Rounded Props | **Tailwind-Skala (vollständig)** | none, sm, md, lg, xl, 2xl, 3xl, full |
|
||||
| Shadow Props | **Tailwind-Skala (vollständig)** | none, sm, md, lg, xl, 2xl |
|
||||
| Variant Props | **Semantisch (vollständig)** | primary, secondary, danger, warning, success, info |
|
||||
@ -1217,8 +1165,8 @@ Alle grün → Merge erlaubt
|
||||
**Storybook Deploy (Webhook):**
|
||||
1. GitHub sendet Webhook bei Release-Event
|
||||
2. Server empfängt Webhook
|
||||
3. Server führt `scripts/deploy-histoire.sh` aus (Teil des Repos)
|
||||
4. Script: git pull → npm ci → histoire build → copy to webroot
|
||||
3. Server führt `scripts/deploy-storybook.sh` aus (Teil des Repos)
|
||||
4. Script: git pull → npm ci → storybook build → copy to webroot
|
||||
|
||||
### GitHub Workflows (vollständige Liste)
|
||||
|
||||
@ -1230,7 +1178,7 @@ Alle grün → Merge erlaubt
|
||||
| **test-a11y** | Push/PR | axe-core | Accessibility-Tests |
|
||||
| **test-visual** | Push/PR | Playwright | Visual Regression Screenshots |
|
||||
| **build** | Push/PR | Vite | Build verifizieren |
|
||||
| **build-histoire** | Push/PR | Storybook | Dokumentation bauen |
|
||||
| **build-storybook** | Push/PR | Storybook | Dokumentation bauen |
|
||||
| **size-check** | Push/PR | size-limit | Bundle-Größe prüfen |
|
||||
| **release** | Push main | release-please | Release-PR erstellen |
|
||||
| **publish** | Release | npm | Auf npm veröffentlichen |
|
||||
@ -1288,7 +1236,7 @@ Die Komponenten werden über Storybook dokumentiert und auf einer öffentlichen
|
||||
|
||||
**Hosting:**
|
||||
- Eigener Server (öffentlich zugänglich)
|
||||
- Static Build via `histoire build`
|
||||
- Static Build via `storybook build`
|
||||
- Deployment bei jedem Release
|
||||
|
||||
**Workflow:**
|
||||
@ -1403,7 +1351,7 @@ Bei der Migration werden:
|
||||
|
||||
## 11. Entscheidungen
|
||||
|
||||
> 70 Entscheidungen in 9 Kategorien
|
||||
> 73 Entscheidungen in 9 Kategorien
|
||||
|
||||
### Vision & Ziele
|
||||
|
||||
@ -1472,7 +1420,7 @@ Bei der Migration werden:
|
||||
| 29 | Dark Mode | Ja, von Anfang an | Alle Komponenten mit Light/Dark |
|
||||
| 30 | Icons | Hybrid-Architektur | System-Icons in Library, Feature-Icons in App |
|
||||
| 51 | Icon-Architektur | Hybrid | ~10 System-Icons in Library, Rest in App (siehe §4) |
|
||||
| 59 | Size Props | Tailwind-Skala (xs, sm, md, lg, xl, 2xl) | Konsistenz mit Tailwind, intuitive Benennung |
|
||||
| 59 | Size Props | Tailwind-Skala (sm, md, lg, xl) | Komponentenspezifisch, OsButton nutzt sm-xl |
|
||||
| 60 | Rounded Props | Tailwind-Skala (none, sm, md, lg, xl, 2xl, 3xl, full) | Konsistenz mit Tailwind border-radius |
|
||||
| 61 | Shadow Props | Tailwind-Skala (none, sm, md, lg, xl, 2xl) | Konsistenz mit Tailwind box-shadow |
|
||||
| 62 | Variant Props | Semantisch (primary, secondary, danger, warning, success, info) | Übliche UI-Farbvarianten |
|
||||
@ -1482,8 +1430,8 @@ Bei der Migration werden:
|
||||
| 66 | Branding-Hierarchie | Webapp → Spezialisiertes Branding | Default-Branding in Webapp, Overrides pro Instanz |
|
||||
| 67 | Variable-Validierung | Runtime-Check in Development | `validateCssVariables()` warnt bei fehlenden Variablen |
|
||||
| 68 | Branding-Test (Webapp) | CI-Test in Webapp | Webapp testet, dass Default-Branding alle Library-Variablen definiert |
|
||||
| 69 | Webapp ↔ Maintenance Sharing | Webapp als Source of Truth | Kein separates "shared" Package, maintenance importiert aus webapp/ (siehe §16a) |
|
||||
| 70 | Daten-Entkopplung | ViewModel/Mapper Pattern | Komponenten kennen nur ViewModels, Mapper transformieren API-Daten (siehe §16b) |
|
||||
| 72 | Webapp ↔ Maintenance Sharing | Webapp als Source of Truth | Kein separates "shared" Package, maintenance importiert aus webapp/ (siehe §16a) |
|
||||
| 73 | Daten-Entkopplung | ViewModel/Mapper Pattern | Komponenten kennen nur ViewModels, Mapper transformieren API-Daten (siehe §16b) |
|
||||
|
||||
### Komponenten-API & Konventionen
|
||||
|
||||
@ -1748,6 +1696,15 @@ Bei der Migration werden:
|
||||
| 2026-02-13 | **CSS-Selektoren** | LoginForm: `.login-form button` → `.login-form button[type='submit']`; pages/index: redundante Klasse auf BaseIcon entfernt |
|
||||
| 2026-02-13 | **JoinLeaveButton** | `.native` von `@mouseenter`/`@mouseleave` entfernt (Vue 3 Kompatibilität) |
|
||||
| 2026-02-13 | **MySomethingList** | `:title` + `:aria-label` auf Edit/Delete-Buttons (Tooltip beibehalten neben Accessibility) |
|
||||
| 2026-02-14 | **`as` Prop** | Polymorphe OsButton-Komponente: `as` Prop für dynamischen Tag/Komponente (`button`, `a`, `nuxt-link`, `router-link`); moderner Standard (Headless UI, Radix Vue) |
|
||||
| 2026-02-14 | **Naming: tag → as** | `tag` → `as` umbenannt nach Recherche moderner UI-Libraries (Headless UI, Radix Vue, Chakra UI, PrimeVue nutzen `as`) |
|
||||
| 2026-02-14 | **Disabled nur für button** | `disabled`/`type`/`loading` nur bei `as="button"` (Links haben kein natives `disabled`); `aria-disabled`/`tabindex` Logik entfernt |
|
||||
| 2026-02-14 | **Polymorphic Story** | Neue Story `Polymorphic` mit Varianten, Icons, disabled-Vergleich; Playground mit `as`-Selektor (button/a) |
|
||||
| 2026-02-14 | **nuxt-link Migration** | 15 `<nuxt-link>`/`<a>`-Wrapper → `as="nuxt-link"`/`as="a"` in 15 Webapp-Dateien; invalides HTML (`<button>` in `<a>`) eliminiert |
|
||||
| 2026-02-14 | **CustomButton konsolidiert** | v-if/v-else für `<a>`/`<nuxt-link>` Wrapper → einzelner `<os-button :as="linkTag" v-bind="linkProps">` mit Computed Properties |
|
||||
| 2026-02-14 | **NotificationMenu konsolidiert** | 2 separate Buttons (kein Badge / mit Badge) zu einem zusammengeführt — counter-icon zeigt bei `count=0` kein Badge |
|
||||
| 2026-02-14 | **CSS-Selektor Fix** | pages/index.vue: `button.post-add-button-top/bottom` → `.post-add-button-top/bottom` (nuxt-link rendert `<a>`, nicht `<button>`) |
|
||||
| 2026-02-14 | **Profil-Spacing** | profile/_id/_slug.vue: `v-if` auf ds-grid-item (kein leerer Abstand), symmetrisches Padding `$space-x-small` |
|
||||
|
||||
---
|
||||
|
||||
@ -2268,7 +2225,7 @@ Phase 3 (später): @ocelot-social/auth, @ocelot-social/posts, etc.
|
||||
|
||||
| # | Datum | Entscheidung |
|
||||
|---|-------|--------------|
|
||||
| 68 | 2026-02-09 | Webapp als Source of Truth für geteilte Business-Komponenten |
|
||||
| 72 | 2026-02-09 | Webapp als Source of Truth für geteilte Business-Komponenten |
|
||||
|
||||
---
|
||||
|
||||
@ -2462,7 +2419,7 @@ webapp/
|
||||
|
||||
| # | Datum | Entscheidung |
|
||||
|---|-------|--------------|
|
||||
| 70 | 2026-02-09 | ViewModel/Mapper Pattern für Daten-Entkopplung |
|
||||
| 73 | 2026-02-09 | ViewModel/Mapper Pattern für Daten-Entkopplung |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { defineComponent, h } from 'vue-demi'
|
||||
|
||||
import OsButton from './OsButton.vue'
|
||||
|
||||
@ -548,6 +549,64 @@ describe('osButton', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('as prop', () => {
|
||||
it('renders as <button> by default', () => {
|
||||
const wrapper = mount(OsButton)
|
||||
|
||||
expect((wrapper.element as HTMLElement).tagName).toBe('BUTTON')
|
||||
expect(wrapper.attributes('type')).toBe('button')
|
||||
})
|
||||
|
||||
it('renders as <a> when as="a"', () => {
|
||||
const wrapper = mount(OsButton, {
|
||||
props: { as: 'a' },
|
||||
attrs: { href: '/test' },
|
||||
slots: { default: 'Link' },
|
||||
})
|
||||
|
||||
expect((wrapper.element as HTMLElement).tagName).toBe('A')
|
||||
expect(wrapper.attributes('href')).toBe('/test')
|
||||
expect(wrapper.attributes('type')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('renders a component passed as as', () => {
|
||||
const FakeLink = defineComponent({
|
||||
props: { to: { type: String, default: undefined } },
|
||||
setup(props, { slots }) {
|
||||
return () => h('a', { href: props.to }, slots.default?.())
|
||||
},
|
||||
})
|
||||
const wrapper = mount(OsButton, {
|
||||
props: { as: FakeLink },
|
||||
attrs: { to: '/groups' },
|
||||
slots: { default: 'Groups' },
|
||||
})
|
||||
|
||||
expect((wrapper.element as HTMLElement).tagName).toBe('A')
|
||||
expect(wrapper.text()).toBe('Groups')
|
||||
})
|
||||
|
||||
it('ignores disabled prop on non-button tags', () => {
|
||||
const wrapper = mount(OsButton, {
|
||||
props: { as: 'a', disabled: true },
|
||||
attrs: { href: '/test' },
|
||||
})
|
||||
|
||||
expect(wrapper.attributes('disabled')).toBeUndefined()
|
||||
expect(wrapper.attributes('aria-disabled')).toBeUndefined()
|
||||
expect(wrapper.attributes('tabindex')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('applies variant classes regardless of as', () => {
|
||||
const wrapper = mount(OsButton, {
|
||||
props: { as: 'a', variant: 'primary', appearance: 'filled' },
|
||||
})
|
||||
|
||||
expect(wrapper.classes()).toContain('os-button')
|
||||
expect(wrapper.classes()).toContain('bg-[var(--color-primary)]')
|
||||
})
|
||||
})
|
||||
|
||||
describe('keyboard accessibility', () => {
|
||||
it('renders as native button element for keyboard support', () => {
|
||||
const wrapper = mount(OsButton)
|
||||
|
||||
@ -42,6 +42,7 @@ type Story = StoryObj<typeof OsButton>
|
||||
|
||||
/** Custom args for Playground (icon selector + label are not real component props) */
|
||||
interface PlaygroundArgs {
|
||||
as: string
|
||||
variant: string
|
||||
appearance: string
|
||||
size: string
|
||||
@ -62,6 +63,10 @@ const iconMap: Record<string, (() => ReturnType<typeof h>) | null> = {
|
||||
|
||||
export const Playground: StoryObj<PlaygroundArgs> = {
|
||||
argTypes: {
|
||||
as: {
|
||||
control: 'select',
|
||||
options: ['button', 'a'],
|
||||
},
|
||||
variant: {
|
||||
control: 'select',
|
||||
options: ['default', 'primary', 'secondary', 'danger', 'warning', 'success', 'info'],
|
||||
@ -95,6 +100,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
||||
},
|
||||
},
|
||||
args: {
|
||||
as: 'button',
|
||||
variant: 'primary',
|
||||
appearance: 'filled',
|
||||
size: 'md',
|
||||
@ -117,7 +123,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
||||
return { buttonProps, IconComponent, label }
|
||||
},
|
||||
template: `
|
||||
<OsButton v-bind="buttonProps">
|
||||
<OsButton v-bind="buttonProps" :href="buttonProps.as === 'a' ? '#' : undefined">
|
||||
<template v-if="IconComponent" #icon><component :is="IconComponent" /></template>
|
||||
{{ label }}
|
||||
</OsButton>
|
||||
@ -663,6 +669,44 @@ export const CircleAppearances: Story = {
|
||||
}),
|
||||
}
|
||||
|
||||
export const Polymorphic: Story = {
|
||||
render: () => ({
|
||||
components: { OsButton, CheckIcon, PlusIcon },
|
||||
template: `
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<h3 class="text-sm font-bold mb-2">as="a" (anchor element)</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<OsButton as="a" href="#" variant="primary">Primary Link</OsButton>
|
||||
<OsButton as="a" href="#" variant="primary" appearance="outline">Outline Link</OsButton>
|
||||
<OsButton as="a" href="#" variant="primary" appearance="ghost">Ghost Link</OsButton>
|
||||
<OsButton as="a" href="#" variant="danger">Danger Link</OsButton>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold mb-2">as="a" with icon</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<OsButton as="a" href="#" variant="primary">
|
||||
<template #icon><CheckIcon /></template>
|
||||
Save
|
||||
</OsButton>
|
||||
<OsButton as="a" href="#" variant="success" circle aria-label="Add">
|
||||
<template #icon><PlusIcon /></template>
|
||||
</OsButton>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-bold mb-2">as="button" (default)</h3>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<OsButton variant="primary">Button (default)</OsButton>
|
||||
<OsButton as="button" variant="primary">Button (explicit)</OsButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
}
|
||||
|
||||
export const Loading: Story = {
|
||||
render: () => ({
|
||||
components: { OsButton, CheckIcon },
|
||||
|
||||
@ -218,6 +218,15 @@ test.describe('OsButton visual regression', () => {
|
||||
await checkA11y(page)
|
||||
})
|
||||
|
||||
test('polymorphic', async ({ page }) => {
|
||||
await page.goto(`${STORY_URL}--polymorphic&viewMode=story`)
|
||||
const root = page.locator(STORY_ROOT)
|
||||
await root.waitFor()
|
||||
await waitForFonts(page)
|
||||
await expect(root.locator('.flex-col').first()).toHaveScreenshot('polymorphic.png')
|
||||
await checkA11y(page)
|
||||
})
|
||||
|
||||
test('loading', async ({ page }) => {
|
||||
await page.goto(`${STORY_URL}--loading&viewMode=story`)
|
||||
const root = page.locator(STORY_ROOT)
|
||||
|
||||
@ -6,10 +6,19 @@
|
||||
import { buttonVariants } from './button.variants'
|
||||
|
||||
import type { ButtonVariants } from './button.variants'
|
||||
import type { PropType } from 'vue-demi'
|
||||
import type { Component, PropType } from 'vue-demi'
|
||||
|
||||
/**
|
||||
* Flexible button component with optional icon slot.
|
||||
*
|
||||
* Use the `as` prop to render as a different element or component (e.g. nuxt-link)
|
||||
* without creating a dependency on any router library:
|
||||
*
|
||||
* ```vue
|
||||
* <os-button :as="NuxtLink" to="/groups">Groups</os-button>
|
||||
* <os-button as="a" href="/external">External</os-button>
|
||||
* ```
|
||||
*
|
||||
* @slot default - Button content (text or HTML)
|
||||
* @slot icon - Optional icon (rendered left of text). Use aria-label for icon-only buttons.
|
||||
*/
|
||||
@ -68,6 +77,15 @@
|
||||
name: 'OsButton',
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
/**
|
||||
* Element or component to render as (e.g. 'a', NuxtLink, RouterLink).
|
||||
* Note: `disabled` and `type` apply only when as="button" (default).
|
||||
* `loading` shows the spinner for any tag, but only disables native buttons.
|
||||
*/
|
||||
as: {
|
||||
type: [String, Object, Function] as PropType<string | Component>,
|
||||
default: 'button',
|
||||
},
|
||||
variant: {
|
||||
type: String as PropType<ButtonVariants['variant']>,
|
||||
default: 'default',
|
||||
@ -184,14 +202,20 @@
|
||||
props.circle && CIRCLE_WIDTHS[size], // eslint-disable-line security/detect-object-injection
|
||||
)
|
||||
|
||||
const buttonData = {
|
||||
type: props.type,
|
||||
disabled: isDisabled || undefined,
|
||||
const tag = props.as
|
||||
const isNativeButton = tag === 'button'
|
||||
|
||||
const buttonData: Record<string, unknown> = {
|
||||
'data-variant': props.variant,
|
||||
'data-appearance': props.appearance,
|
||||
'aria-busy': isLoading || undefined,
|
||||
}
|
||||
|
||||
if (isNativeButton) {
|
||||
buttonData.type = props.type
|
||||
buttonData.disabled = isDisabled || undefined
|
||||
}
|
||||
|
||||
/* v8 ignore start -- Vue 2 branch tested in webapp Jest tests */
|
||||
if (isVue2) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -201,7 +225,7 @@
|
||||
const parentDynClass = proxy?.$vnode?.data?.class
|
||||
|
||||
return h(
|
||||
'button',
|
||||
tag,
|
||||
{
|
||||
class: [buttonClass, parentClass, parentDynClass].filter(Boolean),
|
||||
attrs: { ...buttonData, ...attrs },
|
||||
@ -214,7 +238,7 @@
|
||||
|
||||
const { class: attrClass, ...restAttrs } = attrs as Record<string, unknown>
|
||||
return h(
|
||||
'button',
|
||||
tag,
|
||||
{
|
||||
...buttonData,
|
||||
class: cn(buttonClass, attrClass || ''),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@ -1,7 +1,7 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`JoinLeaveButton.vue shallowMount renders 1`] = `
|
||||
<os-button-stub variant="primary" appearance="outline" size="md" fullwidth="true" type="button" data-original-title="null" class=" has-tooltip">
|
||||
<os-button-stub as="button" variant="primary" appearance="outline" size="md" fullwidth="true" type="button" data-original-title="null" class=" has-tooltip">
|
||||
group.joinLeaveButton.join
|
||||
</os-button-stub>
|
||||
`;
|
||||
|
||||
@ -34,19 +34,19 @@
|
||||
<div v-if="selectedRoom && selectedRoom.roomId" slot="room-options" class="chat-room-options">
|
||||
<ds-flex v-if="singleRoom">
|
||||
<ds-flex-item centered class="single-chat-bubble">
|
||||
<nuxt-link :to="{ name: 'chat' }">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
size="sm"
|
||||
:aria-label="$t('chat.expandChat')"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="expand" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'chat' }"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
size="sm"
|
||||
:aria-label="$t('chat.expandChat')"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="expand" />
|
||||
</template>
|
||||
</os-button>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item centered>
|
||||
<div class="vac-svg-button vac-room-options">
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
<template>
|
||||
<nuxt-link class="chat-notification-menu" :to="{ name: 'chat' }">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.chats.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.chats.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<counter-icon icon="chat-bubble" :count="unreadRoomCount" danger />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'chat' }"
|
||||
class="chat-notification-menu"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.chats.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.chats.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<counter-icon icon="chat-bubble" :count="unreadRoomCount" danger />
|
||||
</template>
|
||||
</os-button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -1,45 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<a v-if="settings.url" :href="settings.url" :target="settings.target">
|
||||
<os-button
|
||||
class="custom-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t(settings.toolTipIdent)"
|
||||
v-tooltip="{
|
||||
content: $t(settings.toolTipIdent),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<img
|
||||
class="logo-svg"
|
||||
:src="settings.iconPath"
|
||||
:alt="settings.iconAltText"
|
||||
:style="logoWidthStyle"
|
||||
/>
|
||||
</os-button>
|
||||
</a>
|
||||
<nuxt-link v-else :to="settings.path">
|
||||
<os-button
|
||||
class="custom-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t(settings.toolTipIdent)"
|
||||
v-tooltip="{
|
||||
content: $t(settings.toolTipIdent),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<img
|
||||
class="logo-svg"
|
||||
:src="settings.iconPath"
|
||||
:alt="settings.iconAltText"
|
||||
:style="logoWidthStyle"
|
||||
/>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
:as="linkTag"
|
||||
v-bind="linkProps"
|
||||
class="custom-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t(settings.toolTipIdent)"
|
||||
v-tooltip="{
|
||||
content: $t(settings.toolTipIdent),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<img
|
||||
class="logo-svg"
|
||||
:src="settings.iconPath"
|
||||
:alt="settings.iconAltText"
|
||||
:style="logoWidthStyle"
|
||||
/>
|
||||
</os-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -54,6 +34,14 @@ export default {
|
||||
settings: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
linkTag() {
|
||||
return this.settings.url ? 'a' : 'nuxt-link'
|
||||
},
|
||||
linkProps() {
|
||||
return this.settings.url
|
||||
? { href: this.settings.url, target: this.settings.target }
|
||||
: { to: this.settings.path }
|
||||
},
|
||||
logoWidthStyle() {
|
||||
const width = isEmpty(this.settings.iconWidth) ? '26px' : this.settings.iconWidth
|
||||
return `width: ${width};`
|
||||
|
||||
@ -11,12 +11,17 @@
|
||||
:query="query"
|
||||
:select-item="selectItem"
|
||||
/>
|
||||
<link-input
|
||||
v-show="isLinkInputActive"
|
||||
ref="linkInput"
|
||||
:toggle-link-input="toggleLinkInput"
|
||||
:set-link-url="setLinkUrl"
|
||||
/>
|
||||
<div v-show="isLinkInputActive" ref="linkInput">
|
||||
<ds-input
|
||||
id="linkInputId"
|
||||
v-model="linkUrl"
|
||||
class="editor-menu-link-input"
|
||||
placeholder="https://"
|
||||
@blur.native.capture="toggleLinkInput()"
|
||||
@keydown.native.esc.prevent="toggleLinkInput()"
|
||||
@keydown.native.enter.prevent="enterLink()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -35,7 +40,6 @@ import Mention from './nodes/Mention'
|
||||
import MenuBar from './MenuBar'
|
||||
import ContextMenu from './ContextMenu'
|
||||
import SuggestionList from './SuggestionList'
|
||||
import LinkInput from './LinkInput'
|
||||
|
||||
let throttleInputEvent
|
||||
|
||||
@ -43,7 +47,6 @@ export default {
|
||||
components: {
|
||||
ContextMenu,
|
||||
EditorContent,
|
||||
LinkInput,
|
||||
MenuBar,
|
||||
SuggestionList,
|
||||
},
|
||||
@ -58,6 +61,7 @@ export default {
|
||||
lastValueHash: null,
|
||||
editor: null,
|
||||
isLinkInputActive: false,
|
||||
linkUrl: null,
|
||||
suggestionType: '',
|
||||
query: null,
|
||||
suggestionRange: null,
|
||||
@ -247,16 +251,20 @@ export default {
|
||||
insertReply(message) {
|
||||
this.editor.commands.mention({ id: message.id, label: message.slug })
|
||||
},
|
||||
enterLink() {
|
||||
this.setLinkUrl(this.linkUrl)
|
||||
this.linkUrl = null
|
||||
},
|
||||
toggleLinkInput(attrs, element) {
|
||||
if (this.$refs.contextMenu.menu) {
|
||||
this.$refs.contextMenu.hideContextMenu()
|
||||
this.isLinkInputActive = false
|
||||
this.editor.focus()
|
||||
} else if (attrs && element) {
|
||||
this.$refs.linkInput.linkUrl = attrs.href
|
||||
this.linkUrl = attrs.href
|
||||
this.isLinkInputActive = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.contextMenu.displayContextMenu(element, this.$refs.linkInput.$el, 'link')
|
||||
this.$refs.contextMenu.displayContextMenu(element, this.$refs.linkInput, 'link')
|
||||
})
|
||||
} else {
|
||||
this.isLinkInputActive = false
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-input
|
||||
id="linkInputId"
|
||||
v-model="linkUrl"
|
||||
class="editor-menu-link-input"
|
||||
placeholder="https://"
|
||||
@blur.native.capture="toggleLinkInput()"
|
||||
@keydown.native.esc.prevent="toggleLinkInput()"
|
||||
@keydown.native.enter.prevent="enterLink()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
toggleLinkInput: Function,
|
||||
setLinkUrl: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
linkUrl: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enterLink() {
|
||||
this.setLinkUrl(this.linkUrl)
|
||||
this.linkUrl = null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,124 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import Emotions from './Emotions.vue'
|
||||
|
||||
import Vuex from 'vuex'
|
||||
import PostMutations from '~/graphql/PostMutations.js'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
describe('Emotions.vue', () => {
|
||||
let wrapper
|
||||
let mocks
|
||||
let propsData
|
||||
let getters
|
||||
let funnyButton
|
||||
let funnyImage
|
||||
const funnyImageSrc = '/img/svg/emoji/funny_color.svg'
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$apollo: {
|
||||
mutate: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
AddPostEmotions: {
|
||||
to: { id: 'p143' },
|
||||
data: { emotion: 'happy' },
|
||||
},
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
data: {
|
||||
RemovePostEmotions: {
|
||||
from: { id: 'u176' },
|
||||
to: { id: 'p143' },
|
||||
data: { emotion: 'happy' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
query: jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
PostsEmotionsCountByEmotion: 1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
$t: jest.fn(),
|
||||
}
|
||||
propsData = {
|
||||
post: { id: 'p143' },
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
return { id: 'u176' }
|
||||
},
|
||||
}
|
||||
})
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
})
|
||||
return mount(Emotions, { mocks, propsData, store, localVue })
|
||||
}
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it("queries the post's emotions count for each of the 5 emotions", () => {
|
||||
expect(mocks.$apollo.query).toHaveBeenCalledTimes(5)
|
||||
})
|
||||
|
||||
describe('adding emotions', () => {
|
||||
let expectedParams
|
||||
beforeEach(() => {
|
||||
wrapper.vm.PostsEmotionsCountByEmotion.funny = 0
|
||||
funnyButton = wrapper.findAll('button').at(0)
|
||||
funnyButton.trigger('click')
|
||||
})
|
||||
|
||||
it('shows the colored image when the button is active', () => {
|
||||
funnyImage = wrapper.findAll('img').at(0)
|
||||
expect(funnyImage.attributes().src).toEqual(funnyImageSrc)
|
||||
})
|
||||
|
||||
it('sends the AddPostEmotionsMutation for an emotion when clicked', () => {
|
||||
expectedParams = {
|
||||
mutation: PostMutations().AddPostEmotionsMutation,
|
||||
variables: { to: { id: 'p143' }, data: { emotion: 'funny' } },
|
||||
}
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
|
||||
})
|
||||
|
||||
it('increases the PostsEmotionsCountByEmotion for the emotion clicked', () => {
|
||||
expect(wrapper.vm.PostsEmotionsCountByEmotion.funny).toEqual(1)
|
||||
})
|
||||
|
||||
it('adds an emotion to selectedEmotions to show the colored image when the button is active', () => {
|
||||
expect(wrapper.vm.selectedEmotions).toEqual(['funny'])
|
||||
})
|
||||
|
||||
describe('removing emotions', () => {
|
||||
beforeEach(() => {
|
||||
funnyButton.trigger('click')
|
||||
})
|
||||
|
||||
it('sends the RemovePostEmotionsMutation when a user clicks on an active emotion', () => {
|
||||
expectedParams = {
|
||||
mutation: PostMutations().RemovePostEmotionsMutation,
|
||||
variables: { to: { id: 'p143' }, data: { emotion: 'funny' } },
|
||||
}
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedParams))
|
||||
})
|
||||
|
||||
it('decreases the PostsEmotionsCountByEmotion for the emotion clicked', async () => {
|
||||
expect(wrapper.vm.PostsEmotionsCountByEmotion.funny).toEqual(0)
|
||||
})
|
||||
|
||||
it('removes an emotion from selectedEmotions to show the default image', async () => {
|
||||
expect(wrapper.vm.selectedEmotions).toEqual([])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<div class="emotions-button-group">
|
||||
<emotion-button
|
||||
v-for="emotion in Object.keys(PostsEmotionsCountByEmotion)"
|
||||
:key="emotion"
|
||||
:emojiPath="iconPath(emotion)"
|
||||
:emotion="emotion"
|
||||
:emotionCount="PostsEmotionsCountByEmotion[emotion]"
|
||||
@toggleEmotion="toggleEmotion"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { mapGetters } from 'vuex'
|
||||
import EmotionButton from '~/components/EmotionButton/EmotionButton'
|
||||
import { PostsEmotionsByCurrentUser } from '~/graphql/PostQuery.js'
|
||||
import PostMutations from '~/graphql/PostMutations.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EmotionButton,
|
||||
},
|
||||
props: {
|
||||
post: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedEmotions: [],
|
||||
PostsEmotionsCountByEmotion: { funny: 0, happy: 0, surprised: 0, cry: 0, angry: 0 },
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
},
|
||||
created() {
|
||||
Object.keys(this.PostsEmotionsCountByEmotion).map((emotion) => {
|
||||
this.emotionsCount(emotion)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
iconPath(emotion) {
|
||||
if (this.isActive(emotion)) {
|
||||
return `/img/svg/emoji/${emotion}_color.svg`
|
||||
}
|
||||
return `/img/svg/emoji/${emotion}.svg`
|
||||
},
|
||||
toggleEmotion(emotion) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: this.isActive(emotion)
|
||||
? PostMutations().RemovePostEmotionsMutation
|
||||
: PostMutations().AddPostEmotionsMutation,
|
||||
variables: {
|
||||
to: { id: this.post.id },
|
||||
data: { emotion },
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.isActive(emotion)
|
||||
? this.PostsEmotionsCountByEmotion[emotion]--
|
||||
: this.PostsEmotionsCountByEmotion[emotion]++
|
||||
|
||||
const index = this.selectedEmotions.indexOf(emotion)
|
||||
if (index > -1) {
|
||||
this.selectedEmotions.splice(index, 1)
|
||||
} else {
|
||||
this.selectedEmotions.push(emotion)
|
||||
}
|
||||
})
|
||||
},
|
||||
isActive(emotion) {
|
||||
const index = this.selectedEmotions.indexOf(emotion)
|
||||
if (index > -1) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
emotionsCount(emotion) {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: gql`
|
||||
query ($postId: ID!, $data: _EMOTEDInput!) {
|
||||
PostsEmotionsCountByEmotion(postId: $postId, data: $data)
|
||||
}
|
||||
`,
|
||||
variables: { postId: this.post.id, data: { emotion } },
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
.then(({ data: { PostsEmotionsCountByEmotion } }) => {
|
||||
this.PostsEmotionsCountByEmotion[emotion] = PostsEmotionsCountByEmotion
|
||||
})
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
PostsEmotionsByCurrentUser: {
|
||||
query() {
|
||||
return PostsEmotionsByCurrentUser()
|
||||
},
|
||||
variables() {
|
||||
return {
|
||||
postId: this.post.id,
|
||||
}
|
||||
},
|
||||
update({ PostsEmotionsByCurrentUser }) {
|
||||
this.selectedEmotions = PostsEmotionsByCurrentUser
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.emotions-button-group {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
@ -7,16 +7,14 @@
|
||||
<ds-text>
|
||||
{{ $t('contribution.comment.commenting-disabled.blocked-author.call-to-action') }}
|
||||
</ds-text>
|
||||
<nuxt-link :to="authorLink">
|
||||
<os-button variant="primary" appearance="filled">
|
||||
<template #icon><base-icon name="arrow-right" /></template>
|
||||
{{
|
||||
$t('contribution.comment.commenting-disabled.blocked-author.button-label', {
|
||||
name: author.name,
|
||||
})
|
||||
}}
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button as="nuxt-link" :to="authorLink" variant="primary" appearance="filled">
|
||||
<template #icon><base-icon name="arrow-right" /></template>
|
||||
{{
|
||||
$t('contribution.comment.commenting-disabled.blocked-author.button-label', {
|
||||
name: author.name,
|
||||
})
|
||||
}}
|
||||
</os-button>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ exports[`CtaJoinLeaveGroup.vue mount renders 1`] = `
|
||||
</h4>
|
||||
<p class="ds-text">
|
||||
contribution.comment.commenting-disabled.no-group-member.call-to-action
|
||||
</p> <button type="button" data-variant="primary" data-appearance="filled" class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] px-[16px] py-0 text-[15px] leading-[normal] rounded-[5px] align-middle w-full bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] has-tooltip" data-original-title="null"><span class="inline-flex items-center gap-2"><span class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current -ml-1"><span class="base-icon"><!----></span></span>
|
||||
</p> <button data-variant="primary" data-appearance="filled" type="button" class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] px-[16px] py-0 text-[15px] leading-[normal] rounded-[5px] align-middle w-full bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] has-tooltip" data-original-title="null"><span class="inline-flex items-center gap-2"><span class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current -ml-1"><span class="base-icon"><!----></span></span>
|
||||
group.joinLeaveButton.join
|
||||
</span></button>
|
||||
</div>
|
||||
|
||||
@ -9,10 +9,8 @@ exports[`CtaUnblockAuthor.vue shallowMount renders 1`] = `
|
||||
<ds-text-stub tag="p">
|
||||
contribution.comment.commenting-disabled.blocked-author.call-to-action
|
||||
</ds-text-stub>
|
||||
<nuxt-link-stub to="[object Object]">
|
||||
<os-button-stub variant="primary" appearance="filled" size="md" type="button">
|
||||
contribution.comment.commenting-disabled.blocked-author.button-label
|
||||
</os-button-stub>
|
||||
</nuxt-link-stub>
|
||||
<os-button-stub as="nuxt-link" variant="primary" appearance="filled" size="md" type="button" to="[object Object]">
|
||||
contribution.comment.commenting-disabled.blocked-author.button-label
|
||||
</os-button-stub>
|
||||
</ds-space-stub>
|
||||
`;
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<dropdown ref="category-menu" placement="top-start" :offset="8" class="category-menu">
|
||||
<a href="#" slot="default" slot-scope="{ toggleMenu }" @click.prevent="toggleMenu()">
|
||||
<ds-text bold size="large">{{ $t('admin.categories.name') }}</ds-text>
|
||||
</a>
|
||||
<template #popover>
|
||||
<div class="category-menu-options">
|
||||
<h2 class="title">{{ $t('filter-menu.filter-by') }}</h2>
|
||||
<categories-filter v-if="categoriesActive" />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from '~/components/Dropdown'
|
||||
import { mapGetters } from 'vuex'
|
||||
import CategoriesFilter from './CategoriesFilter'
|
||||
import GetCategories from '~/mixins/getCategoriesMixin.js'
|
||||
|
||||
export default {
|
||||
name: 'CategoriesMenu',
|
||||
mixins: [GetCategories],
|
||||
components: {
|
||||
Dropdown,
|
||||
CategoriesFilter,
|
||||
},
|
||||
props: {
|
||||
placement: { type: String },
|
||||
offset: { type: [String, Number] },
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
// TODO: implement visibility of active filter later on
|
||||
filterActive: 'posts/isActive',
|
||||
}),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.category-menu-options {
|
||||
max-width: $size-max-width-filter-menu;
|
||||
padding: $space-small $space-x-small;
|
||||
|
||||
> .title {
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<nuxt-link to="/groups">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.groups.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.groups.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="users" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton } from '@ocelot-social/ui'
|
||||
|
||||
export default {
|
||||
components: { OsButton },
|
||||
}
|
||||
</script>
|
||||
@ -132,9 +132,9 @@
|
||||
</div>
|
||||
<!-- submit -->
|
||||
<ds-space margin-top="large">
|
||||
<nuxt-link to="/groups">
|
||||
<os-button variant="default" appearance="filled">{{ $t('actions.cancel') }}</os-button>
|
||||
</nuxt-link>
|
||||
<os-button as="nuxt-link" to="/groups" variant="default" appearance="filled">
|
||||
{{ $t('actions.cancel') }}
|
||||
</os-button>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<ds-space><h3>Link zur Gruppe</h3></ds-space>
|
||||
<ds-space>
|
||||
<ds-copy-field>Copy Link for Invite Member please!</ds-copy-field>
|
||||
</ds-space>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'GroupLink',
|
||||
}
|
||||
</script>
|
||||
@ -84,11 +84,42 @@
|
||||
</div>
|
||||
<!-- group button -->
|
||||
<client-only v-if="SHOW_GROUP_BUTTON_IN_HEADER">
|
||||
<group-button />
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
to="/groups"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.groups.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.groups.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="users" />
|
||||
</template>
|
||||
</os-button>
|
||||
</client-only>
|
||||
<!-- map button -->
|
||||
<client-only v-if="!isEmpty(this.$env.MAPBOX_TOKEN)">
|
||||
<map-button />
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
to="/map"
|
||||
class="map-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.map.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.map.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="globe-detailed" size="large" />
|
||||
</template>
|
||||
</os-button>
|
||||
</client-only>
|
||||
<!-- custom button -->
|
||||
<client-only v-if="!isEmpty(customButton)">
|
||||
@ -202,7 +233,22 @@
|
||||
>
|
||||
<client-only>
|
||||
<div @click="toggleMobileMenuView">
|
||||
<group-button />
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
to="/groups"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.groups.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.groups.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="users" />
|
||||
</template>
|
||||
</os-button>
|
||||
</div>
|
||||
</client-only>
|
||||
</ds-flex-item>
|
||||
@ -214,7 +260,23 @@
|
||||
>
|
||||
<client-only>
|
||||
<div @click="toggleMobileMenuView">
|
||||
<map-button />
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
to="/map"
|
||||
class="map-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.map.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.map.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="globe-detailed" size="large" />
|
||||
</template>
|
||||
</os-button>
|
||||
</div>
|
||||
</client-only>
|
||||
</ds-flex-item>
|
||||
@ -290,12 +352,10 @@ import AvatarMenu from '~/components/AvatarMenu/AvatarMenu'
|
||||
import ChatNotificationMenu from '~/components/ChatNotificationMenu/ChatNotificationMenu'
|
||||
import CustomButton from '~/components/CustomButton/CustomButton'
|
||||
import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
|
||||
import GroupButton from '~/components/Group/GroupButton'
|
||||
import headerMenuBranded from '~/constants/headerMenuBranded.js'
|
||||
import InviteButton from '~/components/InviteButton/InviteButton'
|
||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||
import Logo from '~/components/Logo/Logo'
|
||||
import MapButton from '~/components/Map/MapButton'
|
||||
import SearchField from '~/components/features/SearchField/SearchField.vue'
|
||||
import NotificationMenu from '~/components/NotificationMenu/NotificationMenu'
|
||||
import links from '~/constants/links.js'
|
||||
@ -310,11 +370,9 @@ export default {
|
||||
ChatNotificationMenu,
|
||||
CustomButton,
|
||||
FilterMenu,
|
||||
GroupButton,
|
||||
InviteButton,
|
||||
LocaleSwitch,
|
||||
Logo,
|
||||
MapButton,
|
||||
NotificationMenu,
|
||||
PageParamsLink,
|
||||
SearchField,
|
||||
@ -473,4 +531,12 @@ export default {
|
||||
.hide-mobile-menu {
|
||||
display: none;
|
||||
}
|
||||
.map-button {
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
|
||||
.base-icon > .svg.--large {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<nuxt-link to="/map">
|
||||
<os-button
|
||||
class="map-button"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.map.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.map.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="globe-detailed" size="large" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { OsButton } from '@ocelot-social/ui'
|
||||
|
||||
export default {
|
||||
name: 'MapButton',
|
||||
components: { OsButton },
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.map-button {
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
|
||||
.base-icon > .svg.--large {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,40 +1,22 @@
|
||||
<template>
|
||||
<nuxt-link
|
||||
v-if="!unreadNotificationsCount"
|
||||
class="notifications-menu"
|
||||
<os-button
|
||||
v-if="!unreadNotificationsCount || noMenu"
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'notifications' }"
|
||||
class="notifications-menu"
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.notifications.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.notifications.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.notifications.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.notifications.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="bell" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<nuxt-link v-else-if="noMenu" class="notifications-menu" :to="{ name: 'notifications' }">
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="ghost"
|
||||
circle
|
||||
:aria-label="$t('header.notifications.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('header.notifications.tooltip'),
|
||||
placement: 'bottom-start',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<counter-icon icon="bell" :count="unreadNotificationsCount" danger />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<template #icon>
|
||||
<counter-icon icon="bell" :count="unreadNotificationsCount" danger />
|
||||
</template>
|
||||
</os-button>
|
||||
<dropdown
|
||||
v-else
|
||||
class="notifications-menu"
|
||||
@ -63,11 +45,14 @@
|
||||
<template #popover="{ closeMenu }">
|
||||
<ds-flex class="notifications-link-container">
|
||||
<ds-flex-item>
|
||||
<nuxt-link :to="{ name: 'notifications' }">
|
||||
<os-button appearance="ghost" variant="primary">
|
||||
{{ $t('notifications.pageLink') }}
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'notifications' }"
|
||||
appearance="ghost"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t('notifications.pageLink') }}
|
||||
</os-button>
|
||||
<os-button
|
||||
appearance="ghost"
|
||||
variant="primary"
|
||||
|
||||
@ -27,9 +27,15 @@
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<nuxt-link v-if="isTouchDevice && userLink" :to="userLink" class="link">
|
||||
<os-button variant="primary">{{ $t('user-teaser.popover.open-profile') }}</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
v-if="isTouchDevice && userLink"
|
||||
as="nuxt-link"
|
||||
:to="userLink"
|
||||
class="link"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t('user-teaser.popover.open-profile') }}
|
||||
</os-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -739,29 +739,25 @@ exports[`Users given badges are enabled renders 1`] = `
|
||||
class="ds-table-col ds-table-col-right"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="actions.edit"
|
||||
class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px]"
|
||||
data-appearance="filled"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="actions.edit"
|
||||
class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px]"
|
||||
data-appearance="filled"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</td>
|
||||
</tr>
|
||||
@ -841,29 +837,25 @@ exports[`Users given badges are enabled renders 1`] = `
|
||||
class="ds-table-col ds-table-col-right"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="actions.edit"
|
||||
class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px]"
|
||||
data-appearance="filled"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="actions.edit"
|
||||
class="os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px]"
|
||||
data-appearance="filled"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -73,21 +73,19 @@
|
||||
</template>
|
||||
</template>
|
||||
<template #badges="scope">
|
||||
<nuxt-link
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{
|
||||
name: 'admin-users-id',
|
||||
params: { id: scope.row.id },
|
||||
}"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('actions.edit')"
|
||||
>
|
||||
<os-button
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('actions.edit')"
|
||||
>
|
||||
<template #icon><base-icon name="pencil" /></template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<template #icon><base-icon name="pencil" /></template>
|
||||
</os-button>
|
||||
</template>
|
||||
</ds-table>
|
||||
<pagination-buttons :hasNext="hasNext" :hasPrevious="hasPrevious" @next="next" @back="back" />
|
||||
|
||||
@ -934,30 +934,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
@ -2930,30 +2926,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
@ -3945,30 +3937,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
@ -6543,30 +6531,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
@ -7623,30 +7607,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
@ -8644,30 +8624,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
|
||||
|
||||
@ -218,28 +218,26 @@
|
||||
</base-card>
|
||||
</ds-space>
|
||||
<ds-space v-if="isGroupMemberNonePending" centered>
|
||||
<nuxt-link
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{
|
||||
name: 'post-create-type',
|
||||
query: { groupId: group.id },
|
||||
}"
|
||||
class="profile-post-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('contribution.newPost')"
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
>
|
||||
<os-button
|
||||
class="profile-post-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('contribution.newPost')"
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</ds-space>
|
||||
<masonry-grid>
|
||||
<!-- TapNavigation -->
|
||||
|
||||
@ -7,24 +7,24 @@
|
||||
<ds-space>
|
||||
<!-- create group -->
|
||||
<ds-space centered>
|
||||
<nuxt-link :to="{ name: 'groups-create' }">
|
||||
<os-button
|
||||
class="group-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
size="xl"
|
||||
:aria-label="$t('group.createNewGroup.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('group.createNewGroup.tooltip'),
|
||||
placement: 'left',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'groups-create' }"
|
||||
class="group-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
size="xl"
|
||||
:aria-label="$t('group.createNewGroup.tooltip')"
|
||||
v-tooltip="{
|
||||
content: $t('group.createNewGroup.tooltip'),
|
||||
placement: 'left',
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</ds-space>
|
||||
<!-- group list -->
|
||||
<ds-space centered v-if="showPagination">
|
||||
|
||||
@ -3,24 +3,27 @@
|
||||
<!-- create post -->
|
||||
<div :class="POST_ADD_BUTTON_POSITION_TOP ? 'box-add-button-top' : ''">
|
||||
<client-only>
|
||||
<nuxt-link :to="{ name: 'post-create-type' }" :class="{ 'hide-filter': hideByScroll }">
|
||||
<os-button
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
class="post-add-button"
|
||||
:class="POST_ADD_BUTTON_POSITION_TOP ? 'post-add-button-top' : 'post-add-button-bottom'"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
size="xl"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'post-create-type' }"
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
class="post-add-button"
|
||||
:class="[
|
||||
POST_ADD_BUTTON_POSITION_TOP ? 'post-add-button-top' : 'post-add-button-bottom',
|
||||
{ 'hide-filter': hideByScroll },
|
||||
]"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
size="xl"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</client-only>
|
||||
</div>
|
||||
<div>
|
||||
@ -356,7 +359,7 @@ export default {
|
||||
float: right;
|
||||
}
|
||||
|
||||
button.post-add-button-bottom {
|
||||
.post-add-button-bottom {
|
||||
height: 54px !important;
|
||||
width: 54px !important;
|
||||
min-height: 54px !important;
|
||||
@ -370,7 +373,7 @@ button.post-add-button-bottom {
|
||||
box-shadow: $box-shadow-x-large !important;
|
||||
}
|
||||
|
||||
button.post-add-button-top {
|
||||
.post-add-button-top {
|
||||
height: 54px !important;
|
||||
width: 54px !important;
|
||||
min-height: 54px !important;
|
||||
@ -467,7 +470,7 @@ button.post-add-button-top {
|
||||
.box-add-button-top {
|
||||
padding-right: 40px;
|
||||
}
|
||||
button.post-add-button-top {
|
||||
.post-add-button-top {
|
||||
height: 44px !important;
|
||||
width: 44px !important;
|
||||
min-height: 44px !important;
|
||||
|
||||
@ -558,20 +558,7 @@ exports[`ProfileSlug given an authenticated user given another profile user and
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||
>
|
||||
<div
|
||||
class="ds-space ds-space-centered"
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
to="[object Object]"
|
||||
>
|
||||
<!---->
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
style="grid-row-end: span 4; grid-column-start: 1; grid-column-end: -1;"
|
||||
@ -1264,20 +1251,7 @@ exports[`ProfileSlug given an authenticated user given another profile user and
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||
>
|
||||
<div
|
||||
class="ds-space ds-space-centered"
|
||||
style="margin-bottom: 32px;"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
to="[object Object]"
|
||||
>
|
||||
<!---->
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
</div>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
style="grid-row-end: span 4; grid-column-start: 1; grid-column-end: -1;"
|
||||
@ -1845,34 +1819,29 @@ exports[`ProfileSlug given an authenticated user given the logged in user as pro
|
||||
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||
>
|
||||
<div
|
||||
class="ds-space ds-space-centered"
|
||||
style="margin-bottom: 32px;"
|
||||
class="profile-post-add-button-container"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
</div>
|
||||
@ -2485,34 +2454,29 @@ exports[`ProfileSlug given an authenticated user given the logged in user as pro
|
||||
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||
>
|
||||
<div
|
||||
class="ds-space ds-space-centered"
|
||||
style="margin-bottom: 32px;"
|
||||
class="profile-post-add-button-container"
|
||||
>
|
||||
<nuxt-link-stub
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
to="[object Object]"
|
||||
>
|
||||
<button
|
||||
aria-label="contribution.newPost"
|
||||
class="profile-post-add-button os-button inline-flex items-center justify-center [white-space-collapse:collapse] relative appearance-none font-semibold tracking-[0.05em] transition-[color,background-color] duration-[80ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] cursor-pointer select-none border-[0.8px] border-solid focus:outline-1 disabled:pointer-events-none disabled:cursor-default focus:outline-dashed focus:outline-[var(--color-primary)] shadow-[inset_0_0_0_1px_rgba(0,0,0,0.05)] disabled:bg-[var(--color-disabled)] disabled:text-[var(--color-disabled-contrast)] disabled:border-[var(--color-disabled)] disabled:shadow-[inset_0_0_0_1px_transparent] h-[36px] min-w-[36px] text-[15px] leading-[normal] align-middle bg-[var(--color-primary)] text-[var(--color-primary-contrast)] border-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] hover:border-[var(--color-primary-hover)] hover:text-[var(--color-primary-contrast)] active:bg-[var(--color-primary-active)] active:border-[var(--color-primary-active)] active:text-[var(--color-primary-contrast)] rounded-full p-0 w-[36px] profile-post-add-button has-tooltip"
|
||||
data-appearance="filled"
|
||||
data-original-title="null"
|
||||
data-variant="primary"
|
||||
type="button"
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center"
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
>
|
||||
<span
|
||||
class="os-button__icon inline-flex items-center shrink-0 h-[1.2em] [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-current"
|
||||
class="base-icon"
|
||||
>
|
||||
<span
|
||||
class="base-icon"
|
||||
>
|
||||
<!---->
|
||||
</span>
|
||||
<!---->
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</nuxt-link-stub>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -145,27 +145,26 @@
|
||||
<tab-navigation :tabs="tabOptions" :activeTab="tabActive" @switch-tab="handleTab" />
|
||||
|
||||
<!-- feed -->
|
||||
<ds-grid-item :row-span="2" column-span="fullWidth">
|
||||
<ds-space centered>
|
||||
<nuxt-link :to="{ name: 'post-create-type' }">
|
||||
<os-button
|
||||
v-if="myProfile"
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
class="profile-post-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('contribution.newPost')"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
</ds-space>
|
||||
<ds-grid-item v-if="myProfile" :row-span="2" column-span="fullWidth">
|
||||
<div class="profile-post-add-button-container">
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'post-create-type' }"
|
||||
v-tooltip="{
|
||||
content: $t('contribution.newPost'),
|
||||
placement: 'left',
|
||||
}"
|
||||
class="profile-post-add-button"
|
||||
variant="primary"
|
||||
appearance="filled"
|
||||
circle
|
||||
:aria-label="$t('contribution.newPost')"
|
||||
>
|
||||
<template #icon>
|
||||
<base-icon name="plus" />
|
||||
</template>
|
||||
</os-button>
|
||||
</div>
|
||||
</ds-grid-item>
|
||||
|
||||
<template v-if="posts.length">
|
||||
@ -505,6 +504,11 @@ export default {
|
||||
right: $space-x-small;
|
||||
}
|
||||
}
|
||||
.profile-post-add-button-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: $space-x-small 0;
|
||||
}
|
||||
.profile-post-add-button {
|
||||
box-shadow: $box-shadow-x-large !important;
|
||||
}
|
||||
|
||||
@ -3,11 +3,15 @@
|
||||
<base-card>
|
||||
<base-icon name="balance-scale" />
|
||||
<h2 class="title">{{ $t(`termsAndConditions.newTermsAndConditions`) }}</h2>
|
||||
<nuxt-link :to="{ name: 'terms-and-conditions' }" target="_blank">
|
||||
<os-button appearance="outline" variant="primary">
|
||||
{{ $t(`termsAndConditions.termsAndConditionsNewConfirmText`) }}
|
||||
</os-button>
|
||||
</nuxt-link>
|
||||
<os-button
|
||||
as="nuxt-link"
|
||||
:to="{ name: 'terms-and-conditions' }"
|
||||
target="_blank"
|
||||
appearance="outline"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t(`termsAndConditions.termsAndConditionsNewConfirmText`) }}
|
||||
</os-button>
|
||||
<label for="checkbox">
|
||||
<input id="checkbox" type="checkbox" v-model="checked" :checked="checked" />
|
||||
{{ $t('termsAndConditions.termsAndConditionsNewConfirm') }}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user