mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-04-06 01:25:31 +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'
|
import { defineStep } from '@badeball/cypress-cucumber-preprocessor'
|
||||||
|
|
||||||
defineStep('the unread counter is removed', () => {
|
defineStep('the unread counter is removed', () => {
|
||||||
cy.get('.notifications-menu .counter-icon')
|
cy.get('.notifications-menu .counter-icon .count')
|
||||||
.should('not.exist')
|
.should('not.exist')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -106,114 +106,47 @@ OsButton Features:
|
|||||||
├─ variant: ✅ primary, secondary, danger, warning, success, info, default
|
├─ variant: ✅ primary, secondary, danger, warning, success, info, default
|
||||||
├─ appearance: ✅ filled, outline, ghost
|
├─ appearance: ✅ filled, outline, ghost
|
||||||
├─ size: ✅ sm, md, lg, xl
|
├─ size: ✅ sm, md, lg, xl
|
||||||
├─ disabled: ✅ mit hover/active-Override
|
├─ disabled: ✅ mit hover/active-Override (nur as="button")
|
||||||
├─ icon: ✅ slot-basiert (icon-system-agnostisch)
|
├─ icon: ✅ slot-basiert (icon-system-agnostisch)
|
||||||
├─ circle: ✅ rounded-full, größenabhängig (p-1.5 bis p-3)
|
├─ 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
|
## 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:**
|
**Abgeschlossene Phasen:**
|
||||||
- [x] Projektordner erstellt
|
- [x] Phase 0: Analyse (177 Komponenten katalogisiert)
|
||||||
- [x] Planungsdokument erstellt
|
- [x] Phase 1: Vue 2.7 Upgrade (2.6.14 → 2.7.16, 979 Tests ✅)
|
||||||
- [x] Tech-Stack entschieden
|
- [x] Phase 2: Projekt-Setup (Vite, vue-demi, Tailwind v4, CVA, Storybook 10, CI/CD, 100% Coverage)
|
||||||
- [x] Branding-Architektur definiert
|
- [x] Phase 3: Webapp-Integration — 133 os-button in 79 Dateien, 0 base-button/ds-button verbleibend
|
||||||
- [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
|
|
||||||
|
|
||||||
**Zuvor abgeschlossen (Session 18 - CodeRabbit Review Feedback: data-test Selektoren, Accessibility, Bugfixes):**
|
**Zuletzt abgeschlossen (Session 20 - `as`-Prop + nuxt-link Migration):**
|
||||||
- [x] Cypress-Selektoren: `.user-content-menu button` → `[data-test="content-menu-button"]` (2 Step-Definitions)
|
- [x] OsButton: `as` Prop implementiert (polymorphe Komponente: `button`, `a`, `nuxt-link`, `router-link`, Custom-Komponenten)
|
||||||
- [x] Cypress-Selektoren: `.content-menu button` → `[data-test="content-menu-button"]` (Admin.PinPost + ReportContent)
|
- [x] Naming-Konvention: `tag` → `as` (moderner Standard: Headless UI, Radix Vue, Chakra UI, PrimeVue)
|
||||||
- [x] muted-users.vue: `data-test="unmute-btn"` + `aria-label` auf Unmute-Button
|
- [x] `disabled`/`type`/`loading` nur bei `as="button"` (Links haben kein natives `disabled`-Attribut)
|
||||||
- [x] blocked-users.vue: `data-test="unblock-btn"` + `aria-label` auf Unblock-Button
|
- [x] Stories: `Polymorphic` Story + Playground `as`-Selektor (button/a)
|
||||||
- [x] ProfileList.vue: `data-test="load-all-connections-btn"` + FollowList.spec.js Selektoren aktualisiert
|
- [x] Visual Test: `polymorphic` Screenshot + a11y-Check
|
||||||
- [x] FollowButton.vue: `data-test="follow-btn"` + Spec-Selektoren aktualisiert
|
- [x] 15 `<nuxt-link>`/`<a>`-Wrapper in 15 Webapp-Dateien → `as="nuxt-link"` / `as="a"` migriert:
|
||||||
- [x] JoinLeaveButton.vue: `data-test="join-leave-btn"` + `.native` von `@mouseenter`/`@mouseleave` entfernt
|
- GroupButton.vue, CtaUnblockAuthor.vue, terms-and-conditions-confirm.vue
|
||||||
- [x] LoginButton.vue: `data-test="login-btn"` + `aria-label="$t('login.login')"` + Spec-Selektoren aktualisiert
|
- CustomButton.vue (v-if/v-else → computed `linkTag`/`linkProps` konsolidiert)
|
||||||
- [x] ReportRow.spec.js: `button[data-variant="danger"]` → `[data-test="confirm"]`
|
- groups/index.vue, GroupForm.vue, admin/users/index.vue
|
||||||
- [x] CtaJoinLeaveGroup.spec.js: Selektor auf `[data-test="join-leave-btn"]` aktualisiert
|
- pages/index.vue (CSS `button.post-add-button-*` → `.post-add-button-*`)
|
||||||
- [x] DisableModal.vue: `finally { this.loading = false }` für Loading-State-Reset
|
- profile/_id/_slug.vue (v-if auf ds-grid-item, symmetrisches Padding `$space-x-small`)
|
||||||
- [x] ReleaseModal.vue: `:loading="loading"` + `this.loading = true` + `finally { this.loading = false }`
|
- groups/_id/_slug.vue, MapButton.vue
|
||||||
- [x] ChangePassword.vue: `:disabled="errors"` → `:disabled="!!errors"` (Boolean-Cast)
|
- ChatNotificationMenu.vue, Chat.vue, UserTeaserPopover.vue
|
||||||
- [x] Password/Change.vue: Unbenutzte `disabled: true` aus data() entfernt + 2 tote Tests entfernt
|
- NotificationMenu.vue (3 Instanzen, 2 zu einem Button konsolidiert via counter-icon)
|
||||||
- [x] MenuBar.vue: Unbenutztes `ref="linkButton"` entfernt
|
- [x] Verifiziert: 0 verbleibende `<nuxt-link>`/`<a>`-Wrapper um `<os-button>` in Webapp
|
||||||
- [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 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] donations.vue: Redundantes `:checked="showDonations"` entfernt (v-model setzt checked bereits)
|
||||||
- [x] MySomethingList.vue: Disabled-Logik vereinfacht `!(!isEditing || (isEditing && !disabled))` → `isEditing && disabled`
|
- [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)
|
- [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] 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)
|
- [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.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] OsButton: `@import "./animations.css"` vor `@source`-Direktiven verschoben (CSS-Spec-Konformität)
|
||||||
- [x] CustomButton.vue: `isEmpty` aus `data()` entfernt → direkter Import im Computed
|
- [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] `data-appearance` Attribut: robuste CSS-Selektoren statt fragile escaped Tailwind-Klassen
|
||||||
- [x] Code-Review Feedback eingearbeitet (Unit-Tests, Testnamen, CSS-Selektoren)
|
- [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] Visuelle Validierung: 16/16 Buttons validiert ✅
|
||||||
- [x] OsButton Features: `appearance` (outline, ghost), `xs` size, focus/active states
|
- [x] OsButton Features: `appearance` (outline, ghost), `xs` size, focus/active states
|
||||||
- [x] Disabled-Styles: CSS-Variablen, hover/active-Override, Border-Fix
|
- [x] Disabled-Styles: CSS-Variablen, hover/active-Override, Border-Fix
|
||||||
- [x] Codebase-Analyse: 14 weitere migrierbare Buttons identifiziert (Scope: 16/35)
|
- [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] 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 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)
|
- [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
|
- Verhindert Layout-Sprung wenn Button disabled wird
|
||||||
|
|
||||||
**Nächste Schritte:**
|
**Nächste Schritte:**
|
||||||
1. ~~Phase 0: Komponenten-Analyse~~ ✅
|
- [ ] Snapshots/Tests aktualisieren (nach `as`-Prop-Migration)
|
||||||
2. ~~Phase 1: Vue 2.7 Upgrade~~ ✅
|
- [ ] GroupButton + MapButton in HeaderMenu inlinen (keine eigene Komponente nötig)
|
||||||
3. ~~**Phase 2: Projekt-Setup**~~ ✅ ABGESCHLOSSEN
|
- [ ] `compat/` Verzeichnis in packages/ui anlegen (temporäre Migrations-Wrapper)
|
||||||
4. ~~**Phase 3: Webapp-Integration**~~ ✅ ABGESCHLOSSEN — 133 Buttons in 79 Dateien
|
- [ ] BaseIcon nach `compat/` verschieben (131 Nutzungen, Voraussetzung für weitere Migrationen)
|
||||||
- [x] yarn link / Webpack-Alias in Webapp
|
- [ ] BaseButton-Komponente ggf. entfernen
|
||||||
- [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
|
|
||||||
|
|
||||||
**Manuelle Setup-Aufgaben (außerhalb Code):**
|
**Manuelle Setup-Aufgaben (außerhalb Code):**
|
||||||
- [ ] `NPM_TOKEN` als GitHub Secret einrichten (für npm publish in ui-release.yml)
|
- [ ] `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
|
# VISION
|
||||||
|
|
||||||
## 1. Projektziel & 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 |
|
| Dateinamen | **PascalCase** | OsButton.vue, OsCard.vue |
|
||||||
| i18n | **Nur Props** | Keine Default-Texte in Komponenten |
|
| i18n | **Nur Props** | Keine Default-Texte in Komponenten |
|
||||||
| Breakpoints | **Tailwind Standard** | sm:640, md:768, lg:1024, xl:1280, 2xl:1536 |
|
| 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 |
|
| 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 |
|
| Shadow Props | **Tailwind-Skala (vollständig)** | none, sm, md, lg, xl, 2xl |
|
||||||
| Variant Props | **Semantisch (vollständig)** | primary, secondary, danger, warning, success, info |
|
| Variant Props | **Semantisch (vollständig)** | primary, secondary, danger, warning, success, info |
|
||||||
@ -1217,8 +1165,8 @@ Alle grün → Merge erlaubt
|
|||||||
**Storybook Deploy (Webhook):**
|
**Storybook Deploy (Webhook):**
|
||||||
1. GitHub sendet Webhook bei Release-Event
|
1. GitHub sendet Webhook bei Release-Event
|
||||||
2. Server empfängt Webhook
|
2. Server empfängt Webhook
|
||||||
3. Server führt `scripts/deploy-histoire.sh` aus (Teil des Repos)
|
3. Server führt `scripts/deploy-storybook.sh` aus (Teil des Repos)
|
||||||
4. Script: git pull → npm ci → histoire build → copy to webroot
|
4. Script: git pull → npm ci → storybook build → copy to webroot
|
||||||
|
|
||||||
### GitHub Workflows (vollständige Liste)
|
### GitHub Workflows (vollständige Liste)
|
||||||
|
|
||||||
@ -1230,7 +1178,7 @@ Alle grün → Merge erlaubt
|
|||||||
| **test-a11y** | Push/PR | axe-core | Accessibility-Tests |
|
| **test-a11y** | Push/PR | axe-core | Accessibility-Tests |
|
||||||
| **test-visual** | Push/PR | Playwright | Visual Regression Screenshots |
|
| **test-visual** | Push/PR | Playwright | Visual Regression Screenshots |
|
||||||
| **build** | Push/PR | Vite | Build verifizieren |
|
| **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 |
|
| **size-check** | Push/PR | size-limit | Bundle-Größe prüfen |
|
||||||
| **release** | Push main | release-please | Release-PR erstellen |
|
| **release** | Push main | release-please | Release-PR erstellen |
|
||||||
| **publish** | Release | npm | Auf npm veröffentlichen |
|
| **publish** | Release | npm | Auf npm veröffentlichen |
|
||||||
@ -1288,7 +1236,7 @@ Die Komponenten werden über Storybook dokumentiert und auf einer öffentlichen
|
|||||||
|
|
||||||
**Hosting:**
|
**Hosting:**
|
||||||
- Eigener Server (öffentlich zugänglich)
|
- Eigener Server (öffentlich zugänglich)
|
||||||
- Static Build via `histoire build`
|
- Static Build via `storybook build`
|
||||||
- Deployment bei jedem Release
|
- Deployment bei jedem Release
|
||||||
|
|
||||||
**Workflow:**
|
**Workflow:**
|
||||||
@ -1403,7 +1351,7 @@ Bei der Migration werden:
|
|||||||
|
|
||||||
## 11. Entscheidungen
|
## 11. Entscheidungen
|
||||||
|
|
||||||
> 70 Entscheidungen in 9 Kategorien
|
> 73 Entscheidungen in 9 Kategorien
|
||||||
|
|
||||||
### Vision & Ziele
|
### Vision & Ziele
|
||||||
|
|
||||||
@ -1472,7 +1420,7 @@ Bei der Migration werden:
|
|||||||
| 29 | Dark Mode | Ja, von Anfang an | Alle Komponenten mit Light/Dark |
|
| 29 | Dark Mode | Ja, von Anfang an | Alle Komponenten mit Light/Dark |
|
||||||
| 30 | Icons | Hybrid-Architektur | System-Icons in Library, Feature-Icons in App |
|
| 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) |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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) |
|
| 72 | 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) |
|
| 73 | Daten-Entkopplung | ViewModel/Mapper Pattern | Komponenten kennen nur ViewModels, Mapper transformieren API-Daten (siehe §16b) |
|
||||||
|
|
||||||
### Komponenten-API & Konventionen
|
### 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 | **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 | **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-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 |
|
| # | 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 |
|
| # | 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 { mount } from '@vue/test-utils'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { defineComponent, h } from 'vue-demi'
|
||||||
|
|
||||||
import OsButton from './OsButton.vue'
|
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', () => {
|
describe('keyboard accessibility', () => {
|
||||||
it('renders as native button element for keyboard support', () => {
|
it('renders as native button element for keyboard support', () => {
|
||||||
const wrapper = mount(OsButton)
|
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) */
|
/** Custom args for Playground (icon selector + label are not real component props) */
|
||||||
interface PlaygroundArgs {
|
interface PlaygroundArgs {
|
||||||
|
as: string
|
||||||
variant: string
|
variant: string
|
||||||
appearance: string
|
appearance: string
|
||||||
size: string
|
size: string
|
||||||
@ -62,6 +63,10 @@ const iconMap: Record<string, (() => ReturnType<typeof h>) | null> = {
|
|||||||
|
|
||||||
export const Playground: StoryObj<PlaygroundArgs> = {
|
export const Playground: StoryObj<PlaygroundArgs> = {
|
||||||
argTypes: {
|
argTypes: {
|
||||||
|
as: {
|
||||||
|
control: 'select',
|
||||||
|
options: ['button', 'a'],
|
||||||
|
},
|
||||||
variant: {
|
variant: {
|
||||||
control: 'select',
|
control: 'select',
|
||||||
options: ['default', 'primary', 'secondary', 'danger', 'warning', 'success', 'info'],
|
options: ['default', 'primary', 'secondary', 'danger', 'warning', 'success', 'info'],
|
||||||
@ -95,6 +100,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
|
as: 'button',
|
||||||
variant: 'primary',
|
variant: 'primary',
|
||||||
appearance: 'filled',
|
appearance: 'filled',
|
||||||
size: 'md',
|
size: 'md',
|
||||||
@ -117,7 +123,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
|||||||
return { buttonProps, IconComponent, label }
|
return { buttonProps, IconComponent, label }
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<OsButton v-bind="buttonProps">
|
<OsButton v-bind="buttonProps" :href="buttonProps.as === 'a' ? '#' : undefined">
|
||||||
<template v-if="IconComponent" #icon><component :is="IconComponent" /></template>
|
<template v-if="IconComponent" #icon><component :is="IconComponent" /></template>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
</OsButton>
|
</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 = {
|
export const Loading: Story = {
|
||||||
render: () => ({
|
render: () => ({
|
||||||
components: { OsButton, CheckIcon },
|
components: { OsButton, CheckIcon },
|
||||||
|
|||||||
@ -218,6 +218,15 @@ test.describe('OsButton visual regression', () => {
|
|||||||
await checkA11y(page)
|
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 }) => {
|
test('loading', async ({ page }) => {
|
||||||
await page.goto(`${STORY_URL}--loading&viewMode=story`)
|
await page.goto(`${STORY_URL}--loading&viewMode=story`)
|
||||||
const root = page.locator(STORY_ROOT)
|
const root = page.locator(STORY_ROOT)
|
||||||
|
|||||||
@ -6,10 +6,19 @@
|
|||||||
import { buttonVariants } from './button.variants'
|
import { buttonVariants } from './button.variants'
|
||||||
|
|
||||||
import type { 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.
|
* 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 default - Button content (text or HTML)
|
||||||
* @slot icon - Optional icon (rendered left of text). Use aria-label for icon-only buttons.
|
* @slot icon - Optional icon (rendered left of text). Use aria-label for icon-only buttons.
|
||||||
*/
|
*/
|
||||||
@ -68,6 +77,15 @@
|
|||||||
name: 'OsButton',
|
name: 'OsButton',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
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: {
|
variant: {
|
||||||
type: String as PropType<ButtonVariants['variant']>,
|
type: String as PropType<ButtonVariants['variant']>,
|
||||||
default: 'default',
|
default: 'default',
|
||||||
@ -184,14 +202,20 @@
|
|||||||
props.circle && CIRCLE_WIDTHS[size], // eslint-disable-line security/detect-object-injection
|
props.circle && CIRCLE_WIDTHS[size], // eslint-disable-line security/detect-object-injection
|
||||||
)
|
)
|
||||||
|
|
||||||
const buttonData = {
|
const tag = props.as
|
||||||
type: props.type,
|
const isNativeButton = tag === 'button'
|
||||||
disabled: isDisabled || undefined,
|
|
||||||
|
const buttonData: Record<string, unknown> = {
|
||||||
'data-variant': props.variant,
|
'data-variant': props.variant,
|
||||||
'data-appearance': props.appearance,
|
'data-appearance': props.appearance,
|
||||||
'aria-busy': isLoading || undefined,
|
'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 */
|
/* v8 ignore start -- Vue 2 branch tested in webapp Jest tests */
|
||||||
if (isVue2) {
|
if (isVue2) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -201,7 +225,7 @@
|
|||||||
const parentDynClass = proxy?.$vnode?.data?.class
|
const parentDynClass = proxy?.$vnode?.data?.class
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
'button',
|
tag,
|
||||||
{
|
{
|
||||||
class: [buttonClass, parentClass, parentDynClass].filter(Boolean),
|
class: [buttonClass, parentClass, parentDynClass].filter(Boolean),
|
||||||
attrs: { ...buttonData, ...attrs },
|
attrs: { ...buttonData, ...attrs },
|
||||||
@ -214,7 +238,7 @@
|
|||||||
|
|
||||||
const { class: attrClass, ...restAttrs } = attrs as Record<string, unknown>
|
const { class: attrClass, ...restAttrs } = attrs as Record<string, unknown>
|
||||||
return h(
|
return h(
|
||||||
'button',
|
tag,
|
||||||
{
|
{
|
||||||
...buttonData,
|
...buttonData,
|
||||||
class: cn(buttonClass, attrClass || ''),
|
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
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`JoinLeaveButton.vue shallowMount renders 1`] = `
|
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
|
group.joinLeaveButton.join
|
||||||
</os-button-stub>
|
</os-button-stub>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -34,19 +34,19 @@
|
|||||||
<div v-if="selectedRoom && selectedRoom.roomId" slot="room-options" class="chat-room-options">
|
<div v-if="selectedRoom && selectedRoom.roomId" slot="room-options" class="chat-room-options">
|
||||||
<ds-flex v-if="singleRoom">
|
<ds-flex v-if="singleRoom">
|
||||||
<ds-flex-item centered class="single-chat-bubble">
|
<ds-flex-item centered class="single-chat-bubble">
|
||||||
<nuxt-link :to="{ name: 'chat' }">
|
<os-button
|
||||||
<os-button
|
as="nuxt-link"
|
||||||
variant="primary"
|
:to="{ name: 'chat' }"
|
||||||
appearance="ghost"
|
variant="primary"
|
||||||
circle
|
appearance="ghost"
|
||||||
size="sm"
|
circle
|
||||||
:aria-label="$t('chat.expandChat')"
|
size="sm"
|
||||||
>
|
:aria-label="$t('chat.expandChat')"
|
||||||
<template #icon>
|
>
|
||||||
<base-icon name="expand" />
|
<template #icon>
|
||||||
</template>
|
<base-icon name="expand" />
|
||||||
</os-button>
|
</template>
|
||||||
</nuxt-link>
|
</os-button>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
<ds-flex-item centered>
|
<ds-flex-item centered>
|
||||||
<div class="vac-svg-button vac-room-options">
|
<div class="vac-svg-button vac-room-options">
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<nuxt-link class="chat-notification-menu" :to="{ name: 'chat' }">
|
<os-button
|
||||||
<os-button
|
as="nuxt-link"
|
||||||
variant="primary"
|
:to="{ name: 'chat' }"
|
||||||
appearance="ghost"
|
class="chat-notification-menu"
|
||||||
circle
|
variant="primary"
|
||||||
:aria-label="$t('header.chats.tooltip')"
|
appearance="ghost"
|
||||||
v-tooltip="{
|
circle
|
||||||
content: $t('header.chats.tooltip'),
|
:aria-label="$t('header.chats.tooltip')"
|
||||||
placement: 'bottom-start',
|
v-tooltip="{
|
||||||
}"
|
content: $t('header.chats.tooltip'),
|
||||||
>
|
placement: 'bottom-start',
|
||||||
<template #icon>
|
}"
|
||||||
<counter-icon icon="chat-bubble" :count="unreadRoomCount" danger />
|
>
|
||||||
</template>
|
<template #icon>
|
||||||
</os-button>
|
<counter-icon icon="chat-bubble" :count="unreadRoomCount" danger />
|
||||||
</nuxt-link>
|
</template>
|
||||||
|
</os-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@ -1,45 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a v-if="settings.url" :href="settings.url" :target="settings.target">
|
<os-button
|
||||||
<os-button
|
:as="linkTag"
|
||||||
class="custom-button"
|
v-bind="linkProps"
|
||||||
variant="primary"
|
class="custom-button"
|
||||||
appearance="ghost"
|
variant="primary"
|
||||||
circle
|
appearance="ghost"
|
||||||
:aria-label="$t(settings.toolTipIdent)"
|
circle
|
||||||
v-tooltip="{
|
:aria-label="$t(settings.toolTipIdent)"
|
||||||
content: $t(settings.toolTipIdent),
|
v-tooltip="{
|
||||||
placement: 'bottom-start',
|
content: $t(settings.toolTipIdent),
|
||||||
}"
|
placement: 'bottom-start',
|
||||||
>
|
}"
|
||||||
<img
|
>
|
||||||
class="logo-svg"
|
<img
|
||||||
:src="settings.iconPath"
|
class="logo-svg"
|
||||||
:alt="settings.iconAltText"
|
:src="settings.iconPath"
|
||||||
:style="logoWidthStyle"
|
:alt="settings.iconAltText"
|
||||||
/>
|
:style="logoWidthStyle"
|
||||||
</os-button>
|
/>
|
||||||
</a>
|
</os-button>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -54,6 +34,14 @@ export default {
|
|||||||
settings: { type: Object, required: true },
|
settings: { type: Object, required: true },
|
||||||
},
|
},
|
||||||
computed: {
|
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() {
|
logoWidthStyle() {
|
||||||
const width = isEmpty(this.settings.iconWidth) ? '26px' : this.settings.iconWidth
|
const width = isEmpty(this.settings.iconWidth) ? '26px' : this.settings.iconWidth
|
||||||
return `width: ${width};`
|
return `width: ${width};`
|
||||||
|
|||||||
@ -11,12 +11,17 @@
|
|||||||
:query="query"
|
:query="query"
|
||||||
:select-item="selectItem"
|
:select-item="selectItem"
|
||||||
/>
|
/>
|
||||||
<link-input
|
<div v-show="isLinkInputActive" ref="linkInput">
|
||||||
v-show="isLinkInputActive"
|
<ds-input
|
||||||
ref="linkInput"
|
id="linkInputId"
|
||||||
:toggle-link-input="toggleLinkInput"
|
v-model="linkUrl"
|
||||||
:set-link-url="setLinkUrl"
|
class="editor-menu-link-input"
|
||||||
/>
|
placeholder="https://"
|
||||||
|
@blur.native.capture="toggleLinkInput()"
|
||||||
|
@keydown.native.esc.prevent="toggleLinkInput()"
|
||||||
|
@keydown.native.enter.prevent="enterLink()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -35,7 +40,6 @@ import Mention from './nodes/Mention'
|
|||||||
import MenuBar from './MenuBar'
|
import MenuBar from './MenuBar'
|
||||||
import ContextMenu from './ContextMenu'
|
import ContextMenu from './ContextMenu'
|
||||||
import SuggestionList from './SuggestionList'
|
import SuggestionList from './SuggestionList'
|
||||||
import LinkInput from './LinkInput'
|
|
||||||
|
|
||||||
let throttleInputEvent
|
let throttleInputEvent
|
||||||
|
|
||||||
@ -43,7 +47,6 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
EditorContent,
|
EditorContent,
|
||||||
LinkInput,
|
|
||||||
MenuBar,
|
MenuBar,
|
||||||
SuggestionList,
|
SuggestionList,
|
||||||
},
|
},
|
||||||
@ -58,6 +61,7 @@ export default {
|
|||||||
lastValueHash: null,
|
lastValueHash: null,
|
||||||
editor: null,
|
editor: null,
|
||||||
isLinkInputActive: false,
|
isLinkInputActive: false,
|
||||||
|
linkUrl: null,
|
||||||
suggestionType: '',
|
suggestionType: '',
|
||||||
query: null,
|
query: null,
|
||||||
suggestionRange: null,
|
suggestionRange: null,
|
||||||
@ -247,16 +251,20 @@ export default {
|
|||||||
insertReply(message) {
|
insertReply(message) {
|
||||||
this.editor.commands.mention({ id: message.id, label: message.slug })
|
this.editor.commands.mention({ id: message.id, label: message.slug })
|
||||||
},
|
},
|
||||||
|
enterLink() {
|
||||||
|
this.setLinkUrl(this.linkUrl)
|
||||||
|
this.linkUrl = null
|
||||||
|
},
|
||||||
toggleLinkInput(attrs, element) {
|
toggleLinkInput(attrs, element) {
|
||||||
if (this.$refs.contextMenu.menu) {
|
if (this.$refs.contextMenu.menu) {
|
||||||
this.$refs.contextMenu.hideContextMenu()
|
this.$refs.contextMenu.hideContextMenu()
|
||||||
this.isLinkInputActive = false
|
this.isLinkInputActive = false
|
||||||
this.editor.focus()
|
this.editor.focus()
|
||||||
} else if (attrs && element) {
|
} else if (attrs && element) {
|
||||||
this.$refs.linkInput.linkUrl = attrs.href
|
this.linkUrl = attrs.href
|
||||||
this.isLinkInputActive = true
|
this.isLinkInputActive = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.contextMenu.displayContextMenu(element, this.$refs.linkInput.$el, 'link')
|
this.$refs.contextMenu.displayContextMenu(element, this.$refs.linkInput, 'link')
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.isLinkInputActive = false
|
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>
|
<ds-text>
|
||||||
{{ $t('contribution.comment.commenting-disabled.blocked-author.call-to-action') }}
|
{{ $t('contribution.comment.commenting-disabled.blocked-author.call-to-action') }}
|
||||||
</ds-text>
|
</ds-text>
|
||||||
<nuxt-link :to="authorLink">
|
<os-button as="nuxt-link" :to="authorLink" variant="primary" appearance="filled">
|
||||||
<os-button variant="primary" appearance="filled">
|
<template #icon><base-icon name="arrow-right" /></template>
|
||||||
<template #icon><base-icon name="arrow-right" /></template>
|
{{
|
||||||
{{
|
$t('contribution.comment.commenting-disabled.blocked-author.button-label', {
|
||||||
$t('contribution.comment.commenting-disabled.blocked-author.button-label', {
|
name: author.name,
|
||||||
name: author.name,
|
})
|
||||||
})
|
}}
|
||||||
}}
|
</os-button>
|
||||||
</os-button>
|
|
||||||
</nuxt-link>
|
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ exports[`CtaJoinLeaveGroup.vue mount renders 1`] = `
|
|||||||
</h4>
|
</h4>
|
||||||
<p class="ds-text">
|
<p class="ds-text">
|
||||||
contribution.comment.commenting-disabled.no-group-member.call-to-action
|
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
|
group.joinLeaveButton.join
|
||||||
</span></button>
|
</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,10 +9,8 @@ exports[`CtaUnblockAuthor.vue shallowMount renders 1`] = `
|
|||||||
<ds-text-stub tag="p">
|
<ds-text-stub tag="p">
|
||||||
contribution.comment.commenting-disabled.blocked-author.call-to-action
|
contribution.comment.commenting-disabled.blocked-author.call-to-action
|
||||||
</ds-text-stub>
|
</ds-text-stub>
|
||||||
<nuxt-link-stub to="[object Object]">
|
<os-button-stub as="nuxt-link" variant="primary" appearance="filled" size="md" type="button" to="[object Object]">
|
||||||
<os-button-stub variant="primary" appearance="filled" size="md" type="button">
|
contribution.comment.commenting-disabled.blocked-author.button-label
|
||||||
contribution.comment.commenting-disabled.blocked-author.button-label
|
</os-button-stub>
|
||||||
</os-button-stub>
|
|
||||||
</nuxt-link-stub>
|
|
||||||
</ds-space-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>
|
</div>
|
||||||
<!-- submit -->
|
<!-- submit -->
|
||||||
<ds-space margin-top="large">
|
<ds-space margin-top="large">
|
||||||
<nuxt-link to="/groups">
|
<os-button as="nuxt-link" to="/groups" variant="default" appearance="filled">
|
||||||
<os-button variant="default" appearance="filled">{{ $t('actions.cancel') }}</os-button>
|
{{ $t('actions.cancel') }}
|
||||||
</nuxt-link>
|
</os-button>
|
||||||
<os-button
|
<os-button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
appearance="filled"
|
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>
|
</div>
|
||||||
<!-- group button -->
|
<!-- group button -->
|
||||||
<client-only v-if="SHOW_GROUP_BUTTON_IN_HEADER">
|
<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>
|
</client-only>
|
||||||
<!-- map button -->
|
<!-- map button -->
|
||||||
<client-only v-if="!isEmpty(this.$env.MAPBOX_TOKEN)">
|
<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>
|
</client-only>
|
||||||
<!-- custom button -->
|
<!-- custom button -->
|
||||||
<client-only v-if="!isEmpty(customButton)">
|
<client-only v-if="!isEmpty(customButton)">
|
||||||
@ -202,7 +233,22 @@
|
|||||||
>
|
>
|
||||||
<client-only>
|
<client-only>
|
||||||
<div @click="toggleMobileMenuView">
|
<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>
|
</div>
|
||||||
</client-only>
|
</client-only>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
@ -214,7 +260,23 @@
|
|||||||
>
|
>
|
||||||
<client-only>
|
<client-only>
|
||||||
<div @click="toggleMobileMenuView">
|
<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>
|
</div>
|
||||||
</client-only>
|
</client-only>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
@ -290,12 +352,10 @@ import AvatarMenu from '~/components/AvatarMenu/AvatarMenu'
|
|||||||
import ChatNotificationMenu from '~/components/ChatNotificationMenu/ChatNotificationMenu'
|
import ChatNotificationMenu from '~/components/ChatNotificationMenu/ChatNotificationMenu'
|
||||||
import CustomButton from '~/components/CustomButton/CustomButton'
|
import CustomButton from '~/components/CustomButton/CustomButton'
|
||||||
import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
|
import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
|
||||||
import GroupButton from '~/components/Group/GroupButton'
|
|
||||||
import headerMenuBranded from '~/constants/headerMenuBranded.js'
|
import headerMenuBranded from '~/constants/headerMenuBranded.js'
|
||||||
import InviteButton from '~/components/InviteButton/InviteButton'
|
import InviteButton from '~/components/InviteButton/InviteButton'
|
||||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||||
import Logo from '~/components/Logo/Logo'
|
import Logo from '~/components/Logo/Logo'
|
||||||
import MapButton from '~/components/Map/MapButton'
|
|
||||||
import SearchField from '~/components/features/SearchField/SearchField.vue'
|
import SearchField from '~/components/features/SearchField/SearchField.vue'
|
||||||
import NotificationMenu from '~/components/NotificationMenu/NotificationMenu'
|
import NotificationMenu from '~/components/NotificationMenu/NotificationMenu'
|
||||||
import links from '~/constants/links.js'
|
import links from '~/constants/links.js'
|
||||||
@ -310,11 +370,9 @@ export default {
|
|||||||
ChatNotificationMenu,
|
ChatNotificationMenu,
|
||||||
CustomButton,
|
CustomButton,
|
||||||
FilterMenu,
|
FilterMenu,
|
||||||
GroupButton,
|
|
||||||
InviteButton,
|
InviteButton,
|
||||||
LocaleSwitch,
|
LocaleSwitch,
|
||||||
Logo,
|
Logo,
|
||||||
MapButton,
|
|
||||||
NotificationMenu,
|
NotificationMenu,
|
||||||
PageParamsLink,
|
PageParamsLink,
|
||||||
SearchField,
|
SearchField,
|
||||||
@ -473,4 +531,12 @@ export default {
|
|||||||
.hide-mobile-menu {
|
.hide-mobile-menu {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.map-button {
|
||||||
|
margin-left: 3px;
|
||||||
|
margin-right: 3px;
|
||||||
|
|
||||||
|
.base-icon > .svg.--large {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<nuxt-link
|
<os-button
|
||||||
v-if="!unreadNotificationsCount"
|
v-if="!unreadNotificationsCount || noMenu"
|
||||||
class="notifications-menu"
|
as="nuxt-link"
|
||||||
:to="{ name: 'notifications' }"
|
: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
|
<template #icon>
|
||||||
variant="primary"
|
<counter-icon icon="bell" :count="unreadNotificationsCount" danger />
|
||||||
appearance="ghost"
|
</template>
|
||||||
circle
|
</os-button>
|
||||||
: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>
|
|
||||||
<dropdown
|
<dropdown
|
||||||
v-else
|
v-else
|
||||||
class="notifications-menu"
|
class="notifications-menu"
|
||||||
@ -63,11 +45,14 @@
|
|||||||
<template #popover="{ closeMenu }">
|
<template #popover="{ closeMenu }">
|
||||||
<ds-flex class="notifications-link-container">
|
<ds-flex class="notifications-link-container">
|
||||||
<ds-flex-item>
|
<ds-flex-item>
|
||||||
<nuxt-link :to="{ name: 'notifications' }">
|
<os-button
|
||||||
<os-button appearance="ghost" variant="primary">
|
as="nuxt-link"
|
||||||
{{ $t('notifications.pageLink') }}
|
:to="{ name: 'notifications' }"
|
||||||
</os-button>
|
appearance="ghost"
|
||||||
</nuxt-link>
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ $t('notifications.pageLink') }}
|
||||||
|
</os-button>
|
||||||
<os-button
|
<os-button
|
||||||
appearance="ghost"
|
appearance="ghost"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
|||||||
@ -27,9 +27,15 @@
|
|||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<nuxt-link v-if="isTouchDevice && userLink" :to="userLink" class="link">
|
<os-button
|
||||||
<os-button variant="primary">{{ $t('user-teaser.popover.open-profile') }}</os-button>
|
v-if="isTouchDevice && userLink"
|
||||||
</nuxt-link>
|
as="nuxt-link"
|
||||||
|
:to="userLink"
|
||||||
|
class="link"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ $t('user-teaser.popover.open-profile') }}
|
||||||
|
</os-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -739,29 +739,25 @@ exports[`Users given badges are enabled renders 1`] = `
|
|||||||
class="ds-table-col ds-table-col-right"
|
class="ds-table-col ds-table-col-right"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="actions.edit"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -841,29 +837,25 @@ exports[`Users given badges are enabled renders 1`] = `
|
|||||||
class="ds-table-col ds-table-col-right"
|
class="ds-table-col ds-table-col-right"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="actions.edit"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -73,21 +73,19 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #badges="scope">
|
<template #badges="scope">
|
||||||
<nuxt-link
|
<os-button
|
||||||
|
as="nuxt-link"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'admin-users-id',
|
name: 'admin-users-id',
|
||||||
params: { id: scope.row.id },
|
params: { id: scope.row.id },
|
||||||
}"
|
}"
|
||||||
|
variant="primary"
|
||||||
|
appearance="filled"
|
||||||
|
circle
|
||||||
|
:aria-label="$t('actions.edit')"
|
||||||
>
|
>
|
||||||
<os-button
|
<template #icon><base-icon name="pencil" /></template>
|
||||||
variant="primary"
|
</os-button>
|
||||||
appearance="filled"
|
|
||||||
circle
|
|
||||||
:aria-label="$t('actions.edit')"
|
|
||||||
>
|
|
||||||
<template #icon><base-icon name="pencil" /></template>
|
|
||||||
</os-button>
|
|
||||||
</nuxt-link>
|
|
||||||
</template>
|
</template>
|
||||||
</ds-table>
|
</ds-table>
|
||||||
<pagination-buttons :hasNext="hasNext" :hasPrevious="hasPrevious" @next="next" @back="back" />
|
<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;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -2930,30 +2926,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close
|
|||||||
style="margin-bottom: 32px;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -3945,30 +3937,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre
|
|||||||
style="margin-bottom: 32px;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -6543,30 +6531,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre
|
|||||||
style="margin-bottom: 32px;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -7623,30 +7607,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde
|
|||||||
style="margin-bottom: 32px;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -8644,30 +8624,26 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde
|
|||||||
style="margin-bottom: 32px;"
|
style="margin-bottom: 32px;"
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -218,28 +218,26 @@
|
|||||||
</base-card>
|
</base-card>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<ds-space v-if="isGroupMemberNonePending" centered>
|
<ds-space v-if="isGroupMemberNonePending" centered>
|
||||||
<nuxt-link
|
<os-button
|
||||||
|
as="nuxt-link"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'post-create-type',
|
name: 'post-create-type',
|
||||||
query: { groupId: group.id },
|
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
|
<template #icon>
|
||||||
class="profile-post-add-button"
|
<base-icon name="plus" />
|
||||||
variant="primary"
|
</template>
|
||||||
appearance="filled"
|
</os-button>
|
||||||
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>
|
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<masonry-grid>
|
<masonry-grid>
|
||||||
<!-- TapNavigation -->
|
<!-- TapNavigation -->
|
||||||
|
|||||||
@ -7,24 +7,24 @@
|
|||||||
<ds-space>
|
<ds-space>
|
||||||
<!-- create group -->
|
<!-- create group -->
|
||||||
<ds-space centered>
|
<ds-space centered>
|
||||||
<nuxt-link :to="{ name: 'groups-create' }">
|
<os-button
|
||||||
<os-button
|
as="nuxt-link"
|
||||||
class="group-add-button"
|
:to="{ name: 'groups-create' }"
|
||||||
variant="primary"
|
class="group-add-button"
|
||||||
appearance="filled"
|
variant="primary"
|
||||||
circle
|
appearance="filled"
|
||||||
size="xl"
|
circle
|
||||||
:aria-label="$t('group.createNewGroup.tooltip')"
|
size="xl"
|
||||||
v-tooltip="{
|
:aria-label="$t('group.createNewGroup.tooltip')"
|
||||||
content: $t('group.createNewGroup.tooltip'),
|
v-tooltip="{
|
||||||
placement: 'left',
|
content: $t('group.createNewGroup.tooltip'),
|
||||||
}"
|
placement: 'left',
|
||||||
>
|
}"
|
||||||
<template #icon>
|
>
|
||||||
<base-icon name="plus" />
|
<template #icon>
|
||||||
</template>
|
<base-icon name="plus" />
|
||||||
</os-button>
|
</template>
|
||||||
</nuxt-link>
|
</os-button>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<!-- group list -->
|
<!-- group list -->
|
||||||
<ds-space centered v-if="showPagination">
|
<ds-space centered v-if="showPagination">
|
||||||
|
|||||||
@ -3,24 +3,27 @@
|
|||||||
<!-- create post -->
|
<!-- create post -->
|
||||||
<div :class="POST_ADD_BUTTON_POSITION_TOP ? 'box-add-button-top' : ''">
|
<div :class="POST_ADD_BUTTON_POSITION_TOP ? 'box-add-button-top' : ''">
|
||||||
<client-only>
|
<client-only>
|
||||||
<nuxt-link :to="{ name: 'post-create-type' }" :class="{ 'hide-filter': hideByScroll }">
|
<os-button
|
||||||
<os-button
|
as="nuxt-link"
|
||||||
v-tooltip="{
|
:to="{ name: 'post-create-type' }"
|
||||||
content: $t('contribution.newPost'),
|
v-tooltip="{
|
||||||
placement: 'left',
|
content: $t('contribution.newPost'),
|
||||||
}"
|
placement: 'left',
|
||||||
class="post-add-button"
|
}"
|
||||||
:class="POST_ADD_BUTTON_POSITION_TOP ? 'post-add-button-top' : 'post-add-button-bottom'"
|
class="post-add-button"
|
||||||
variant="primary"
|
:class="[
|
||||||
appearance="filled"
|
POST_ADD_BUTTON_POSITION_TOP ? 'post-add-button-top' : 'post-add-button-bottom',
|
||||||
circle
|
{ 'hide-filter': hideByScroll },
|
||||||
size="xl"
|
]"
|
||||||
>
|
variant="primary"
|
||||||
<template #icon>
|
appearance="filled"
|
||||||
<base-icon name="plus" />
|
circle
|
||||||
</template>
|
size="xl"
|
||||||
</os-button>
|
>
|
||||||
</nuxt-link>
|
<template #icon>
|
||||||
|
<base-icon name="plus" />
|
||||||
|
</template>
|
||||||
|
</os-button>
|
||||||
</client-only>
|
</client-only>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -356,7 +359,7 @@ export default {
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.post-add-button-bottom {
|
.post-add-button-bottom {
|
||||||
height: 54px !important;
|
height: 54px !important;
|
||||||
width: 54px !important;
|
width: 54px !important;
|
||||||
min-height: 54px !important;
|
min-height: 54px !important;
|
||||||
@ -370,7 +373,7 @@ button.post-add-button-bottom {
|
|||||||
box-shadow: $box-shadow-x-large !important;
|
box-shadow: $box-shadow-x-large !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.post-add-button-top {
|
.post-add-button-top {
|
||||||
height: 54px !important;
|
height: 54px !important;
|
||||||
width: 54px !important;
|
width: 54px !important;
|
||||||
min-height: 54px !important;
|
min-height: 54px !important;
|
||||||
@ -467,7 +470,7 @@ button.post-add-button-top {
|
|||||||
.box-add-button-top {
|
.box-add-button-top {
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
}
|
}
|
||||||
button.post-add-button-top {
|
.post-add-button-top {
|
||||||
height: 44px !important;
|
height: 44px !important;
|
||||||
width: 44px !important;
|
width: 44px !important;
|
||||||
min-height: 44px !important;
|
min-height: 44px !important;
|
||||||
|
|||||||
@ -558,20 +558,7 @@ exports[`ProfileSlug given an authenticated user given another profile user and
|
|||||||
</article>
|
</article>
|
||||||
</div>
|
</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
|
<div
|
||||||
style="grid-row-end: span 4; grid-column-start: 1; grid-column-end: -1;"
|
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>
|
</article>
|
||||||
</div>
|
</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
|
<div
|
||||||
style="grid-row-end: span 4; grid-column-start: 1; grid-column-end: -1;"
|
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;"
|
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ds-space ds-space-centered"
|
class="profile-post-add-button-container"
|
||||||
style="margin-bottom: 32px;"
|
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
</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;"
|
style="grid-row-end: span 2; grid-column-start: 1; grid-column-end: -1;"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ds-space ds-space-centered"
|
class="profile-post-add-button-container"
|
||||||
style="margin-bottom: 32px;"
|
|
||||||
>
|
>
|
||||||
<nuxt-link-stub
|
<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]"
|
to="[object Object]"
|
||||||
>
|
>
|
||||||
<button
|
<span
|
||||||
aria-label="contribution.newPost"
|
class="inline-flex items-center"
|
||||||
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
|
<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
|
<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>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</span>
|
||||||
</nuxt-link-stub>
|
</nuxt-link-stub>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -145,27 +145,26 @@
|
|||||||
<tab-navigation :tabs="tabOptions" :activeTab="tabActive" @switch-tab="handleTab" />
|
<tab-navigation :tabs="tabOptions" :activeTab="tabActive" @switch-tab="handleTab" />
|
||||||
|
|
||||||
<!-- feed -->
|
<!-- feed -->
|
||||||
<ds-grid-item :row-span="2" column-span="fullWidth">
|
<ds-grid-item v-if="myProfile" :row-span="2" column-span="fullWidth">
|
||||||
<ds-space centered>
|
<div class="profile-post-add-button-container">
|
||||||
<nuxt-link :to="{ name: 'post-create-type' }">
|
<os-button
|
||||||
<os-button
|
as="nuxt-link"
|
||||||
v-if="myProfile"
|
:to="{ name: 'post-create-type' }"
|
||||||
v-tooltip="{
|
v-tooltip="{
|
||||||
content: $t('contribution.newPost'),
|
content: $t('contribution.newPost'),
|
||||||
placement: 'left',
|
placement: 'left',
|
||||||
}"
|
}"
|
||||||
class="profile-post-add-button"
|
class="profile-post-add-button"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
appearance="filled"
|
appearance="filled"
|
||||||
circle
|
circle
|
||||||
:aria-label="$t('contribution.newPost')"
|
:aria-label="$t('contribution.newPost')"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<base-icon name="plus" />
|
<base-icon name="plus" />
|
||||||
</template>
|
</template>
|
||||||
</os-button>
|
</os-button>
|
||||||
</nuxt-link>
|
</div>
|
||||||
</ds-space>
|
|
||||||
</ds-grid-item>
|
</ds-grid-item>
|
||||||
|
|
||||||
<template v-if="posts.length">
|
<template v-if="posts.length">
|
||||||
@ -505,6 +504,11 @@ export default {
|
|||||||
right: $space-x-small;
|
right: $space-x-small;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.profile-post-add-button-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: $space-x-small 0;
|
||||||
|
}
|
||||||
.profile-post-add-button {
|
.profile-post-add-button {
|
||||||
box-shadow: $box-shadow-x-large !important;
|
box-shadow: $box-shadow-x-large !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,15 @@
|
|||||||
<base-card>
|
<base-card>
|
||||||
<base-icon name="balance-scale" />
|
<base-icon name="balance-scale" />
|
||||||
<h2 class="title">{{ $t(`termsAndConditions.newTermsAndConditions`) }}</h2>
|
<h2 class="title">{{ $t(`termsAndConditions.newTermsAndConditions`) }}</h2>
|
||||||
<nuxt-link :to="{ name: 'terms-and-conditions' }" target="_blank">
|
<os-button
|
||||||
<os-button appearance="outline" variant="primary">
|
as="nuxt-link"
|
||||||
{{ $t(`termsAndConditions.termsAndConditionsNewConfirmText`) }}
|
:to="{ name: 'terms-and-conditions' }"
|
||||||
</os-button>
|
target="_blank"
|
||||||
</nuxt-link>
|
appearance="outline"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
{{ $t(`termsAndConditions.termsAndConditionsNewConfirmText`) }}
|
||||||
|
</os-button>
|
||||||
<label for="checkbox">
|
<label for="checkbox">
|
||||||
<input id="checkbox" type="checkbox" v-model="checked" :checked="checked" />
|
<input id="checkbox" type="checkbox" v-model="checked" :checked="checked" />
|
||||||
{{ $t('termsAndConditions.termsAndConditionsNewConfirm') }}
|
{{ $t('termsAndConditions.termsAndConditionsNewConfirm') }}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user