settings modal
This commit is contained in:
4
components.d.ts
vendored
4
components.d.ts
vendored
@@ -9,6 +9,7 @@ export {}
|
|||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
AccountSettings: typeof import('./src/components/TopBar/Settings/AccountSettings.vue')['default']
|
||||||
Auth: typeof import('./src/components/Auth.vue')['default']
|
Auth: typeof import('./src/components/Auth.vue')['default']
|
||||||
Autocomplete: typeof import('./src/components/Note/Autocomplete.vue')['default']
|
Autocomplete: typeof import('./src/components/Note/Autocomplete.vue')['default']
|
||||||
Hamburger: typeof import('./src/components/TopBar/Hamburger.vue')['default']
|
Hamburger: typeof import('./src/components/TopBar/Hamburger.vue')['default']
|
||||||
@@ -18,6 +19,7 @@ declare module '@vue/runtime-core' {
|
|||||||
Note: typeof import('./src/components/ViewModes/Note.vue')['default']
|
Note: typeof import('./src/components/ViewModes/Note.vue')['default']
|
||||||
NoteEditor: typeof import('./src/components/Note/NoteEditor.vue')['default']
|
NoteEditor: typeof import('./src/components/Note/NoteEditor.vue')['default']
|
||||||
NoteReferences: typeof import('./src/components/Note/NoteReferences.vue')['default']
|
NoteReferences: typeof import('./src/components/Note/NoteReferences.vue')['default']
|
||||||
|
NotesSourceSwitcher: typeof import('./src/components/TopBar/Settings/NotesSourceSwitcher.vue')['default']
|
||||||
NoteToolbar: typeof import('./src/components/Note/NoteToolbar.vue')['default']
|
NoteToolbar: typeof import('./src/components/Note/NoteToolbar.vue')['default']
|
||||||
SearchBar: typeof import('./src/components/Search/SearchBar.vue')['default']
|
SearchBar: typeof import('./src/components/Search/SearchBar.vue')['default']
|
||||||
SearchResult: typeof import('./src/components/Search/SearchResult.vue')['default']
|
SearchResult: typeof import('./src/components/Search/SearchResult.vue')['default']
|
||||||
@@ -25,6 +27,7 @@ declare module '@vue/runtime-core' {
|
|||||||
SideBar: typeof import('./src/components/SideBar.vue')['default']
|
SideBar: typeof import('./src/components/SideBar.vue')['default']
|
||||||
SideBarMenu: typeof import('./src/components/SideBar/SideBarMenu.vue')['default']
|
SideBarMenu: typeof import('./src/components/SideBar/SideBarMenu.vue')['default']
|
||||||
SideBarMenuItem: typeof import('./src/components/SideBar/SideBarMenuItem.vue')['default']
|
SideBarMenuItem: typeof import('./src/components/SideBar/SideBarMenuItem.vue')['default']
|
||||||
|
SignOut: typeof import('./src/components/TopBar/Settings/SignOut.vue')['default']
|
||||||
SkeletonNote: typeof import('./src/components/Skeleton/SkeletonNote.vue')['default']
|
SkeletonNote: typeof import('./src/components/Skeleton/SkeletonNote.vue')['default']
|
||||||
SkeletonSidebarItem: typeof import('./src/components/Skeleton/SkeletonSidebarItem.vue')['default']
|
SkeletonSidebarItem: typeof import('./src/components/Skeleton/SkeletonSidebarItem.vue')['default']
|
||||||
SkeletonTopBar: typeof import('./src/components/Skeleton/SkeletonTopBar.vue')['default']
|
SkeletonTopBar: typeof import('./src/components/Skeleton/SkeletonTopBar.vue')['default']
|
||||||
@@ -34,6 +37,7 @@ declare module '@vue/runtime-core' {
|
|||||||
UIBadge: typeof import('./src/components/ui/UIBadge.vue')['default']
|
UIBadge: typeof import('./src/components/ui/UIBadge.vue')['default']
|
||||||
UIButton: typeof import('./src/components/ui/UIButton.vue')['default']
|
UIButton: typeof import('./src/components/ui/UIButton.vue')['default']
|
||||||
UIButtonGroup: typeof import('./src/components/ui/UIButtonGroup.vue')['default']
|
UIButtonGroup: typeof import('./src/components/ui/UIButtonGroup.vue')['default']
|
||||||
|
UICard: typeof import('./src/components/ui/UICard.vue')['default']
|
||||||
UIDropdown: typeof import('./src/components/ui/UIDropdown.vue')['default']
|
UIDropdown: typeof import('./src/components/ui/UIDropdown.vue')['default']
|
||||||
UIDropdownItem: typeof import('./src/components/ui/UIDropdownItem.vue')['default']
|
UIDropdownItem: typeof import('./src/components/ui/UIDropdownItem.vue')['default']
|
||||||
UIInputCheckbox: typeof import('./src/components/ui/UIInputCheckbox.vue')['default']
|
UIInputCheckbox: typeof import('./src/components/ui/UIInputCheckbox.vue')['default']
|
||||||
|
|||||||
65
package-lock.json
generated
65
package-lock.json
generated
@@ -41,6 +41,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.2.0",
|
"@rushstack/eslint-patch": "^1.2.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@tsconfig/node18": "^2.0.1",
|
"@tsconfig/node18": "^2.0.1",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
@@ -1990,6 +1991,34 @@
|
|||||||
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@tailwindcss/typography": {
|
||||||
|
"version": "0.5.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||||
|
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.castarray": "^4.4.0",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"tailwindcss": ">=3.0.0 || insiders"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tootallnate/once": {
|
"node_modules/@tootallnate/once": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||||
@@ -8019,6 +8048,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.castarray": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/lodash.defaults": {
|
"node_modules/lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
@@ -13874,6 +13909,30 @@
|
|||||||
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@tailwindcss/typography": {
|
||||||
|
"version": "0.5.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.9.tgz",
|
||||||
|
"integrity": "sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lodash.castarray": "^4.4.0",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.merge": "^4.6.2",
|
||||||
|
"postcss-selector-parser": "6.0.10"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"postcss-selector-parser": {
|
||||||
|
"version": "6.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||||
|
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cssesc": "^3.0.0",
|
||||||
|
"util-deprecate": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@tootallnate/once": {
|
"@tootallnate/once": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||||
@@ -18551,6 +18610,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||||
},
|
},
|
||||||
|
"lodash.castarray": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"lodash.defaults": {
|
"lodash.defaults": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.2.0",
|
"@rushstack/eslint-patch": "^1.2.0",
|
||||||
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"@tsconfig/node18": "^2.0.1",
|
"@tsconfig/node18": "^2.0.1",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
|
|||||||
@@ -1,61 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { activeNotesSource, availableNotesSources } from '@/composables/useNotes'
|
|
||||||
import { signOut as firebaseSignOut } from '@/composables/useFirebase'
|
|
||||||
import { OnClickOutside } from '@vueuse/components'
|
import { OnClickOutside } from '@vueuse/components'
|
||||||
import { preferredNotesSource } from '@/composables/useSettings'
|
|
||||||
|
|
||||||
const sourceLabels: { [source: string]: string } = {
|
|
||||||
local: 'Switch to local notes',
|
|
||||||
firebase: 'Switch to cloud notes'
|
|
||||||
}
|
|
||||||
|
|
||||||
const signOut = async (close: () => Promise<boolean>) => {
|
|
||||||
await firebaseSignOut()
|
|
||||||
preferredNotesSource.value = null
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
|
|
||||||
const blur = () => (document.activeElement as HTMLElement)?.blur()
|
|
||||||
|
|
||||||
const handleClick = (fn: (...args: any[]) => any) => {
|
|
||||||
blur()
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<OnClickOutside>
|
<OnClickOutside>
|
||||||
<UIDropdown class="search-active-hide">
|
<UIDropdown class="search-active-hide">
|
||||||
<template #activator>
|
<template #activator>
|
||||||
<UIButton :dropdown="true" size="sm" variant="outline" class="py-1 text-white topbar-button">
|
<UIButton
|
||||||
|
:dropdown="true"
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
class="topbar-button py-1 text-white"
|
||||||
|
>
|
||||||
<i class="fa-fw fa-solid fa-user-gear" />
|
<i class="fa-fw fa-solid fa-user-gear" />
|
||||||
</UIButton>
|
</UIButton>
|
||||||
</template>
|
</template>
|
||||||
<template #items>
|
<template #items>
|
||||||
<UIDropdownItem
|
<NotesSourceSwitcher />
|
||||||
v-for="source in availableNotesSources.filter((source) => source !== activeNotesSource)"
|
<AccountSettings />
|
||||||
:key="source"
|
<SignOut />
|
||||||
@click="handleClick(() => (preferredNotesSource = source))"
|
|
||||||
>
|
|
||||||
<i class="fa-fw fa-solid fa-database" />
|
|
||||||
{{ sourceLabels[source] }}
|
|
||||||
</UIDropdownItem>
|
|
||||||
<UIModal>
|
|
||||||
<template #activator="{ open }">
|
|
||||||
<UIDropdownItem @click="open">
|
|
||||||
<i class="fa-fw fa-solid fa-right-from-bracket" />
|
|
||||||
Sign out
|
|
||||||
</UIDropdownItem>
|
|
||||||
</template>
|
|
||||||
<template #title>Sign out</template>
|
|
||||||
<template #default>
|
|
||||||
<p>Are you sure want to signout?</p>
|
|
||||||
<p>Your synchronized notes can't be accessed until you sign-in again.</p>
|
|
||||||
</template>
|
|
||||||
<template #actions="{ close }">
|
|
||||||
<UIButton size="sm" @click="close">Cancel</UIButton>
|
|
||||||
<UIButton size="sm" color="primary" @click="signOut(close)">Sign out</UIButton>
|
|
||||||
</template>
|
|
||||||
</UIModal>
|
|
||||||
</template>
|
</template>
|
||||||
</UIDropdown>
|
</UIDropdown>
|
||||||
</OnClickOutside>
|
</OnClickOutside>
|
||||||
|
|||||||
82
src/components/TopBar/Settings/AccountSettings.vue
Normal file
82
src/components/TopBar/Settings/AccountSettings.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { user } from '@/composables/useFirebase'
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
|
||||||
|
const verificationEmailSent = ref(false)
|
||||||
|
const sendVerificationMail = () => {
|
||||||
|
if (!user.value) throw Error("User doesn't exist, can't send verification email")
|
||||||
|
user.value.sendEmailVerification()
|
||||||
|
verificationEmailSent.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<UIModal size="lg">
|
||||||
|
<template #activator="{ open }">
|
||||||
|
<UIDropdownItem @click="open">
|
||||||
|
<i class="fa-fw fa-solid fa-sliders" />
|
||||||
|
Settings
|
||||||
|
</UIDropdownItem>
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<i class="fa-fw fa-solid fa-sliders mr-2" />
|
||||||
|
Settings
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UICard>
|
||||||
|
<template #title>Account</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="w-full flex-row sm:flex">
|
||||||
|
<div class="font-bold sm:w-4/12">E-mail address</div>
|
||||||
|
<div>{{ user?.email }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex-row sm:flex">
|
||||||
|
<div class="font-bold sm:w-4/12">Verified</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<UIBadge :color="user?.emailVerified ? 'success' : 'warning'">
|
||||||
|
{{ user?.emailVerified ? 'Verified' : 'Not yet verified' }}
|
||||||
|
</UIBadge>
|
||||||
|
<UIButton
|
||||||
|
size="sm"
|
||||||
|
class="ml-2"
|
||||||
|
@click="sendVerificationMail"
|
||||||
|
v-if="!user?.emailVerified"
|
||||||
|
:disabled="verificationEmailSent"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
verificationEmailSent
|
||||||
|
? 'Verification email sent'
|
||||||
|
: 'Request new verification email'
|
||||||
|
}}
|
||||||
|
</UIButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex-row sm:flex">
|
||||||
|
<div class="font-bold sm:w-4/12">Account creation date</div>
|
||||||
|
<div>
|
||||||
|
{{ format(Date.parse(user?.metadata?.creationTime || ''), 'dd/MM/yyyy') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UICard>
|
||||||
|
<UICard>
|
||||||
|
<template #title>Notes</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="w-full flex-row sm:flex items-center">
|
||||||
|
<div class="font-bold sm:w-4/12">Export notes</div>
|
||||||
|
<UIButton size="sm" color="secondary"><i class="fa-fw fa-solid fa-file-export mr-2"></i>Export notes</UIButton>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex-row sm:flex items-center">
|
||||||
|
<div class="font-bold sm:w-4/12">Delete account</div>
|
||||||
|
<UIButton size="sm" color="error"><i class="fa-fw fa-solid fa-trash mr-2"></i>Delete account</UIButton>
|
||||||
|
</div>
|
||||||
|
<div class="w-full flex-row sm:flex items-center">
|
||||||
|
<div class="font-bold sm:w-4/12">End-to-end encryption</div>
|
||||||
|
<UIButton size="sm" color="secondary"><i class="fa-fw fa-solid fa-key mr-2"></i>Enable end-to-end encryption</UIButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UICard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UIModal>
|
||||||
|
</template>
|
||||||
26
src/components/TopBar/Settings/NotesSourceSwitcher.vue
Normal file
26
src/components/TopBar/Settings/NotesSourceSwitcher.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { activeNotesSource, availableNotesSources } from '@/composables/useNotes'
|
||||||
|
import { preferredNotesSource } from '@/composables/useSettings'
|
||||||
|
|
||||||
|
const sourceLabels: { [source: string]: string } = {
|
||||||
|
local: 'Switch to local notes',
|
||||||
|
firebase: 'Switch to cloud notes'
|
||||||
|
}
|
||||||
|
|
||||||
|
const blur = () => (document.activeElement as HTMLElement)?.blur()
|
||||||
|
|
||||||
|
const handleClick = (fn: (...args: any[]) => any) => {
|
||||||
|
blur()
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<UIDropdownItem
|
||||||
|
v-for="source in availableNotesSources.filter((source) => source !== activeNotesSource)"
|
||||||
|
:key="source"
|
||||||
|
@click="handleClick(() => (preferredNotesSource = source))"
|
||||||
|
>
|
||||||
|
<i class="fa-fw fa-solid fa-database" />
|
||||||
|
{{ sourceLabels[source] }}
|
||||||
|
</UIDropdownItem>
|
||||||
|
</template>
|
||||||
33
src/components/TopBar/Settings/SignOut.vue
Normal file
33
src/components/TopBar/Settings/SignOut.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { preferredNotesSource } from '@/composables/useSettings'
|
||||||
|
import { signOut as firebaseSignOut } from '@/composables/useFirebase'
|
||||||
|
|
||||||
|
const signOut = async (close: () => Promise<boolean>) => {
|
||||||
|
await firebaseSignOut()
|
||||||
|
preferredNotesSource.value = null
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UIModal>
|
||||||
|
<template #activator="{ open }">
|
||||||
|
<UIDropdownItem @click="open">
|
||||||
|
<i class="fa-fw fa-solid fa-right-from-bracket" />
|
||||||
|
Sign out
|
||||||
|
</UIDropdownItem>
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<i class="fa-fw fa-solid fa-right-from-bracket mr-2" />
|
||||||
|
Sign out
|
||||||
|
</template>
|
||||||
|
<template #default>
|
||||||
|
<p>Are you sure want to signout?</p>
|
||||||
|
<p>Your synchronized notes can't be accessed until you sign-in again.</p>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ close }">
|
||||||
|
<UIButton size="sm" @click="close">Cancel</UIButton>
|
||||||
|
<UIButton size="sm" color="primary" @click="signOut(close)">Sign out</UIButton>
|
||||||
|
</template>
|
||||||
|
</UIModal>
|
||||||
|
</template>
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
variant?: 'regular' | 'outline' | 'ghost'
|
variant?: 'regular' | 'outline' | 'ghost'
|
||||||
|
color?: 'regular' | 'info' | 'success' | 'warning' | 'error'
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
variant: 'regular'
|
variant: 'regular',
|
||||||
|
color: 'regular'
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
@@ -21,11 +23,19 @@ const styleClass = computed(() => {
|
|||||||
outline: 'dui-badge-outline',
|
outline: 'dui-badge-outline',
|
||||||
ghost: 'dui-badge-ghost'
|
ghost: 'dui-badge-ghost'
|
||||||
}
|
}
|
||||||
|
const colorVariants = {
|
||||||
|
regular: '',
|
||||||
|
info: 'dui-badge-info',
|
||||||
|
success: 'dui-badge-success',
|
||||||
|
warning: 'dui-badge-warning',
|
||||||
|
error: 'dui-badge-error'
|
||||||
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
const variantClass = variantVariants[props.variant]
|
const variantClass = variantVariants[props.variant]
|
||||||
return [sizeClass, variantClass]
|
const colorClass = colorVariants[props.color]
|
||||||
|
return [sizeClass, variantClass, colorClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="dui-badge" :class="styleClass"><slot></slot></div>
|
<span class="dui-badge" :class="styleClass"><slot></slot></span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
variant?: 'regular' | 'outline'
|
variant?: 'regular' | 'outline'
|
||||||
color?: 'regular' | 'primary'
|
color?: 'regular' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
||||||
dropdown?: boolean
|
dropdown?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,12 @@ const styleClass = computed(() => {
|
|||||||
}
|
}
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
primary: 'dui-btn-primary'
|
primary: 'dui-btn-primary',
|
||||||
|
secondary: 'dui-btn-secondary',
|
||||||
|
info: 'dui-btn-info',
|
||||||
|
success: 'dui-btn-success',
|
||||||
|
warning: 'dui-btn-warning',
|
||||||
|
error: 'dui-btn-error'
|
||||||
}
|
}
|
||||||
const variantVariants = {
|
const variantVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
|
|||||||
9
src/components/ui/UICard.vue
Normal file
9
src/components/ui/UICard.vue
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dui-card bg-base-100 shadow-sm">
|
||||||
|
<div class="dui-card-body">
|
||||||
|
<h3 class="dui-card-title" v-if="$slots.title"><slot name="title" /></h3>
|
||||||
|
<slot></slot>
|
||||||
|
<div class="dui-card-actions justify-end" v-if="$slots.actions"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -5,10 +5,12 @@ const props = withDefaults(
|
|||||||
defineProps<{
|
defineProps<{
|
||||||
open?: boolean
|
open?: boolean
|
||||||
persistent?: boolean
|
persistent?: boolean
|
||||||
|
size?: 'sm' | 'md' | 'lg'
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
open: false,
|
open: false,
|
||||||
persistent: false
|
persistent: false,
|
||||||
|
size: 'md'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,6 +48,16 @@ const onLeave = (el: Element, done: () => void): void => {
|
|||||||
el.addEventListener('transitionend', () => done())
|
el.addEventListener('transitionend', () => done())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styleClass = computed(() => {
|
||||||
|
const sizeVariants = {
|
||||||
|
'sm': 'max-w-xs',
|
||||||
|
'md': 'max-w-md',
|
||||||
|
'lg': 'max-w-2xl'
|
||||||
|
}
|
||||||
|
const sizeClass = sizeVariants[props.size]
|
||||||
|
return [sizeClass]
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({ open, close })
|
defineExpose({ open, close })
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@@ -53,8 +65,10 @@ defineExpose({ open, close })
|
|||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<Transition @enter="onEnter" @leave="onLeave" appear>
|
<Transition @enter="onEnter" @leave="onLeave" appear>
|
||||||
<div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
|
<div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
|
||||||
<div class="dui-modal-box" ref="modalBox">
|
<div class="dui-modal-box" :class="styleClass" ref="modalBox">
|
||||||
<h3 class="text-lg font-bold" v-if="$slots.title"><slot name="title" /></h3>
|
<h3 class="flex items-center text-xl font-bold" v-if="$slots.title">
|
||||||
|
<slot name="title" />
|
||||||
|
</h3>
|
||||||
<p class="py-4">
|
<p class="py-4">
|
||||||
<slot v-bind="slotProps" />
|
<slot v-bind="slotProps" />
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const colors = require('tailwindcss/colors')
|
const colors = require('tailwindcss/colors')
|
||||||
const appWidth = '1280px'
|
const appWidth = '1280px'
|
||||||
const sideBarWidth = '220px'
|
const sideBarWidth = '220px'
|
||||||
const sideBarWidthMobile ='260px'
|
const sideBarWidthMobile = '260px'
|
||||||
const primary = '#1E4BC4'
|
const primary = '#1E4BC4'
|
||||||
const secondary = colors.gray[500]
|
const secondary = colors.gray[500]
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('@tailwindcss/typography'), require('daisyui')],
|
||||||
daisyui: {
|
daisyui: {
|
||||||
prefix: 'dui-',
|
prefix: 'dui-',
|
||||||
themes: [
|
themes: [
|
||||||
|
|||||||
Reference in New Issue
Block a user