mirror of
https://github.com/hcengineering/platform.git
synced 2025-06-09 09:20:54 +00:00
Use logo for workspaces (#4828)
Signed-off-by: Vyacheslav Tumanov <me@slavatumanov.me>
This commit is contained in:
parent
6cbb497a01
commit
36bc394e7a
@ -28,6 +28,7 @@ import {
|
|||||||
type Integration,
|
type Integration,
|
||||||
type IntegrationType,
|
type IntegrationType,
|
||||||
type InviteSettings,
|
type InviteSettings,
|
||||||
|
type WorkspaceSetting,
|
||||||
type SettingsCategory,
|
type SettingsCategory,
|
||||||
type UserMixin
|
type UserMixin
|
||||||
} from '@hcengineering/setting'
|
} from '@hcengineering/setting'
|
||||||
@ -99,6 +100,11 @@ export class TInviteSettings extends TConfiguration implements InviteSettings {
|
|||||||
limit!: number
|
limit!: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Model(setting.class.WorkspaceSetting, core.class.Doc, DOMAIN_SETTING)
|
||||||
|
export class TWorkspaceSetting extends TDoc implements WorkspaceSetting {
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createModel(
|
builder.createModel(
|
||||||
TIntegration,
|
TIntegration,
|
||||||
@ -107,7 +113,8 @@ export function createModel (builder: Builder): void {
|
|||||||
TWorkspaceSettingCategory,
|
TWorkspaceSettingCategory,
|
||||||
TEditable,
|
TEditable,
|
||||||
TUserMixin,
|
TUserMixin,
|
||||||
TInviteSettings
|
TInviteSettings,
|
||||||
|
TWorkspaceSetting
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.mixin(setting.class.Integration, core.class.Class, notification.mixin.ClassCollaborators, {
|
builder.mixin(setting.class.Integration, core.class.Class, notification.mixin.ClassCollaborators, {
|
||||||
@ -205,6 +212,19 @@ export function createModel (builder: Builder): void {
|
|||||||
},
|
},
|
||||||
setting.ids.Configure
|
setting.ids.Configure
|
||||||
)
|
)
|
||||||
|
builder.createDoc(
|
||||||
|
setting.class.WorkspaceSettingCategory,
|
||||||
|
core.space.Model,
|
||||||
|
{
|
||||||
|
name: 'workspaceSettings',
|
||||||
|
label: setting.string.Branding,
|
||||||
|
icon: setting.icon.AccountSettings,
|
||||||
|
component: setting.component.WorkspaceSetting,
|
||||||
|
order: 1002,
|
||||||
|
secured: true
|
||||||
|
},
|
||||||
|
setting.ids.WorkspaceSetting
|
||||||
|
)
|
||||||
builder.createDoc(
|
builder.createDoc(
|
||||||
setting.class.WorkspaceSettingCategory,
|
setting.class.WorkspaceSettingCategory,
|
||||||
core.space.Model,
|
core.space.Model,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import presentation from '@hcengineering/presentation'
|
import presentation from '@hcengineering/presentation'
|
||||||
|
|
||||||
export let file: Blob
|
export let file: Blob
|
||||||
|
export let lessCrop: boolean = false
|
||||||
let inputRef: HTMLInputElement
|
let inputRef: HTMLInputElement
|
||||||
const targetMimes = ['image/png', 'image/jpg', 'image/jpeg']
|
const targetMimes = ['image/png', 'image/jpg', 'image/jpeg']
|
||||||
|
|
||||||
@ -64,7 +65,7 @@
|
|||||||
<div class="editavatar-container">
|
<div class="editavatar-container">
|
||||||
{#await CropperP then Cropper}
|
{#await CropperP then Cropper}
|
||||||
<div class="cropper">
|
<div class="cropper">
|
||||||
<Cropper bind:this={cropper} image={file} />
|
<Cropper bind:this={cropper} image={file} {lessCrop} />
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<Button label={presentation.string.Save} kind={'primary'} size={'large'} on:click={onCrop} />
|
<Button label={presentation.string.Save} kind={'primary'} size={'large'} on:click={onCrop} />
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
export let direct: Blob | undefined = undefined
|
export let direct: Blob | undefined = undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
export let disabled: boolean = false
|
export let disabled: boolean = false
|
||||||
|
export let imageOnly: boolean = false
|
||||||
|
export let lessCrop: boolean = false
|
||||||
|
|
||||||
$: [schema, uri] = avatar?.split('://') || []
|
$: [schema, uri] = avatar?.split('://') || []
|
||||||
|
|
||||||
@ -91,6 +93,8 @@
|
|||||||
name,
|
name,
|
||||||
file: direct,
|
file: direct,
|
||||||
icon,
|
icon,
|
||||||
|
imageOnly,
|
||||||
|
lessCrop,
|
||||||
onSubmit: handlePopupSubmit
|
onSubmit: handlePopupSubmit
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@
|
|||||||
export let email: string | undefined
|
export let email: string | undefined
|
||||||
export let file: Blob | undefined
|
export let file: Blob | undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
|
export let imageOnly: boolean = false
|
||||||
|
export let lessCrop: boolean = false
|
||||||
export let onSubmit: (avatarType?: AvatarType, avatar?: string, file?: Blob) => void
|
export let onSubmit: (avatarType?: AvatarType, avatar?: string, file?: Blob) => void
|
||||||
|
|
||||||
const [schema, uri] = avatar?.split('://') || []
|
const [schema, uri] = avatar?.split('://') || []
|
||||||
@ -110,18 +112,18 @@
|
|||||||
|
|
||||||
if (selectedFile !== undefined) {
|
if (selectedFile !== undefined) {
|
||||||
editableFile = selectedFile
|
editableFile = selectedFile
|
||||||
} else if (selectedAvatar) {
|
} else if (selectedAvatar && !(imageOnly && selectedAvatar === initialSelectedAvatar)) {
|
||||||
const url = getFileUrl(selectedAvatar, 'full')
|
const url = getFileUrl(selectedAvatar, 'full')
|
||||||
editableFile = await (await fetch(url)).blob()
|
editableFile = await (await fetch(url)).blob()
|
||||||
} else {
|
} else {
|
||||||
inputRef.click()
|
inputRef.click()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
showCropper(editableFile)
|
if (editableFile.size > 0) showCropper(editableFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
function showCropper (editableFile: Blob) {
|
function showCropper (editableFile: Blob) {
|
||||||
showPopup(EditAvatarPopup, { file: editableFile }, undefined, (blob) => {
|
showPopup(EditAvatarPopup, { file: editableFile, lessCrop }, undefined, (blob) => {
|
||||||
if (blob === undefined) {
|
if (blob === undefined) {
|
||||||
if (!selectedFile && (!avatar || avatar.includes('://'))) {
|
if (!selectedFile && (!avatar || avatar.includes('://'))) {
|
||||||
selectedAvatarType = AvatarType.COLOR
|
selectedAvatarType = AvatarType.COLOR
|
||||||
@ -131,7 +133,7 @@
|
|||||||
}
|
}
|
||||||
if (blob === null) {
|
if (blob === null) {
|
||||||
selectedAvatarType = AvatarType.COLOR
|
selectedAvatarType = AvatarType.COLOR
|
||||||
selectedAvatar = getPlatformAvatarColorForTextDef(name ?? '', $themeStore.dark).name
|
selectedAvatar = imageOnly ? '' : getPlatformAvatarColorForTextDef(name ?? '', $themeStore.dark).name
|
||||||
selectedFile = undefined
|
selectedFile = undefined
|
||||||
} else {
|
} else {
|
||||||
selectedFile = blob
|
selectedFile = blob
|
||||||
@ -203,8 +205,12 @@
|
|||||||
<div
|
<div
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
on:click|self={(e) => {
|
on:click|self={(e) => {
|
||||||
if (selectedAvatarType === AvatarType.IMAGE) handleImageAvatarClick()
|
if (imageOnly) {
|
||||||
else if (selectedAvatarType === AvatarType.COLOR) showColorPopup(e)
|
handleImageAvatarClick()
|
||||||
|
} else {
|
||||||
|
if (selectedAvatarType === AvatarType.IMAGE) handleImageAvatarClick()
|
||||||
|
else if (selectedAvatarType === AvatarType.COLOR) showColorPopup(e)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AvatarComponent
|
<AvatarComponent
|
||||||
@ -220,7 +226,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TabList
|
<TabList
|
||||||
items={getAvatarTypeDropdownItems(hasGravatar)}
|
items={getAvatarTypeDropdownItems(hasGravatar, imageOnly)}
|
||||||
kind={'separated-free'}
|
kind={'separated-free'}
|
||||||
bind:selected={selectedAvatarType}
|
bind:selected={selectedAvatarType}
|
||||||
on:select={handleDropdownSelection}
|
on:select={handleDropdownSelection}
|
||||||
|
@ -335,7 +335,15 @@ function fillStores (): void {
|
|||||||
|
|
||||||
fillStores()
|
fillStores()
|
||||||
|
|
||||||
export function getAvatarTypeDropdownItems (hasGravatar: boolean): TabItem[] {
|
export function getAvatarTypeDropdownItems (hasGravatar: boolean, imageOnly?: boolean): TabItem[] {
|
||||||
|
if (imageOnly === true) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: AvatarType.IMAGE,
|
||||||
|
labelIntl: contact.string.UseImage
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: AvatarType.COLOR,
|
id: AvatarType.COLOR,
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
export let image: Blob
|
export let image: Blob
|
||||||
export let cropSize = 1200
|
export let cropSize = 1200
|
||||||
|
export let lessCrop: boolean = false
|
||||||
|
|
||||||
let imgRef: HTMLImageElement
|
let imgRef: HTMLImageElement
|
||||||
let cropper: Cropper | undefined
|
let cropper: Cropper | undefined
|
||||||
@ -86,7 +87,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="w-full h-full flex">
|
<div class="w-full h-full flex" class:less-crop={lessCrop}>
|
||||||
<img class="image" bind:this={imgRef} alt="img" />
|
<img class="image" bind:this={imgRef} alt="img" />
|
||||||
{#await init(image)}
|
{#await init(image)}
|
||||||
Waiting...
|
Waiting...
|
||||||
@ -99,6 +100,9 @@
|
|||||||
:global(.cropper-view-box, .cropper-face) {
|
:global(.cropper-view-box, .cropper-face) {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
:global(.less-crop .cropper-view-box, .less-crop .cropper-face) {
|
||||||
|
border-radius: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
@ -99,6 +99,7 @@
|
|||||||
"TaskTypes": "Task types",
|
"TaskTypes": "Task types",
|
||||||
"Automations": "Automations",
|
"Automations": "Automations",
|
||||||
"Collections": "Collections",
|
"Collections": "Collections",
|
||||||
"ClassColon": "Class:"
|
"ClassColon": "Class:",
|
||||||
|
"Branding": "Branding"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -100,6 +100,7 @@
|
|||||||
"TaskTypes": "Типы задач",
|
"TaskTypes": "Типы задач",
|
||||||
"Automations": "Автоматизация",
|
"Automations": "Автоматизация",
|
||||||
"Collections": "Коллекции",
|
"Collections": "Коллекции",
|
||||||
"ClassColon": "Класс:"
|
"ClassColon": "Класс:",
|
||||||
|
"Branding": "Брендинг"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2024 Hardcore Engineering Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License. You may
|
||||||
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
//
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||||
|
import contact, { Employee, PersonAccount, combineName, getFirstName, getLastName } from '@hcengineering/contact'
|
||||||
|
import { ChannelsEditor, EditableAvatar, employeeByIdStore } from '@hcengineering/contact-resources'
|
||||||
|
import { AttributeEditor, getClient, MessageBox } from '@hcengineering/presentation'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
createFocusManager,
|
||||||
|
EditBox,
|
||||||
|
FocusHandler,
|
||||||
|
showPopup,
|
||||||
|
Header,
|
||||||
|
Breadcrumb,
|
||||||
|
Label
|
||||||
|
} from '@hcengineering/ui'
|
||||||
|
import setting from '../plugin'
|
||||||
|
import { WorkspaceSetting } from '@hcengineering/setting'
|
||||||
|
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||||
|
|
||||||
|
export let visibleNav: boolean = true
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
let workspaceSettings: WorkspaceSetting | undefined = undefined
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
|
client.findOne(setting.class.WorkspaceSetting, {}).then((r) => {
|
||||||
|
workspaceSettings = r
|
||||||
|
})
|
||||||
|
|
||||||
|
let avatarEditor: EditableAvatar
|
||||||
|
|
||||||
|
async function onAvatarDone (e: any): Promise<void> {
|
||||||
|
if (workspaceSettings === undefined) {
|
||||||
|
const avatar = await avatarEditor.createAvatar()
|
||||||
|
await client.createDoc(
|
||||||
|
setting.class.WorkspaceSetting,
|
||||||
|
setting.space.Setting,
|
||||||
|
{ icon: avatar },
|
||||||
|
setting.ids.WorkspaceSetting
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workspaceSettings.icon != null) {
|
||||||
|
await avatarEditor.removeAvatar(workspaceSettings.icon)
|
||||||
|
}
|
||||||
|
const avatar = await avatarEditor.createAvatar()
|
||||||
|
await client.update(workspaceSettings, {
|
||||||
|
icon: avatar
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const manager = createFocusManager()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FocusHandler {manager} />
|
||||||
|
|
||||||
|
<div class="hulyComponent p-10 flex ac-body row">
|
||||||
|
<EditableAvatar
|
||||||
|
avatar={workspaceSettings?.icon}
|
||||||
|
size={'x-large'}
|
||||||
|
bind:this={avatarEditor}
|
||||||
|
on:done={onAvatarDone}
|
||||||
|
imageOnly
|
||||||
|
lessCrop
|
||||||
|
/>
|
||||||
|
<div class="heading-medium-20 p-4">
|
||||||
|
<Label label={getEmbeddedLabel('Workspace Logo')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.row {
|
||||||
|
flex-direction: row;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -40,6 +40,7 @@ import NumberTypeEditor from './components/typeEditors/NumberTypeEditor.svelte'
|
|||||||
import ArrayEditor from './components/typeEditors/ArrayEditor.svelte'
|
import ArrayEditor from './components/typeEditors/ArrayEditor.svelte'
|
||||||
import RefEditor from './components/typeEditors/RefEditor.svelte'
|
import RefEditor from './components/typeEditors/RefEditor.svelte'
|
||||||
import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte'
|
import StringTypeEditor from './components/typeEditors/StringTypeEditor.svelte'
|
||||||
|
import WorkspaceSetting from './components/WorkspaceSetting.svelte'
|
||||||
import WorkspaceSettings from './components/WorkspaceSettings.svelte'
|
import WorkspaceSettings from './components/WorkspaceSettings.svelte'
|
||||||
import InviteSetting from './components/InviteSetting.svelte'
|
import InviteSetting from './components/InviteSetting.svelte'
|
||||||
import Configure from './components/Configure.svelte'
|
import Configure from './components/Configure.svelte'
|
||||||
@ -84,6 +85,7 @@ export default async (): Promise<Resources> => ({
|
|||||||
Settings,
|
Settings,
|
||||||
Profile,
|
Profile,
|
||||||
Password,
|
Password,
|
||||||
|
WorkspaceSetting,
|
||||||
WorkspaceSettings,
|
WorkspaceSettings,
|
||||||
Integrations,
|
Integrations,
|
||||||
Support,
|
Support,
|
||||||
|
@ -97,6 +97,13 @@ export interface InviteSettings extends Configuration {
|
|||||||
limit: number
|
limit: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface WorkspaceSetting extends Doc {
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -114,7 +121,8 @@ export default plugin(settingId, {
|
|||||||
Terms: '' as Ref<Doc>,
|
Terms: '' as Ref<Doc>,
|
||||||
ClassSetting: '' as Ref<Doc>,
|
ClassSetting: '' as Ref<Doc>,
|
||||||
Owners: '' as Ref<Doc>,
|
Owners: '' as Ref<Doc>,
|
||||||
InviteSettings: '' as Ref<Doc>
|
InviteSettings: '' as Ref<Doc>,
|
||||||
|
WorkspaceSetting: '' as Ref<Doc>
|
||||||
},
|
},
|
||||||
mixin: {
|
mixin: {
|
||||||
Editable: '' as Ref<Mixin<Editable>>,
|
Editable: '' as Ref<Mixin<Editable>>,
|
||||||
@ -128,12 +136,14 @@ export default plugin(settingId, {
|
|||||||
WorkspaceSettingCategory: '' as Ref<Class<SettingsCategory>>,
|
WorkspaceSettingCategory: '' as Ref<Class<SettingsCategory>>,
|
||||||
Integration: '' as Ref<Class<Integration>>,
|
Integration: '' as Ref<Class<Integration>>,
|
||||||
IntegrationType: '' as Ref<Class<IntegrationType>>,
|
IntegrationType: '' as Ref<Class<IntegrationType>>,
|
||||||
InviteSettings: '' as Ref<Class<InviteSettings>>
|
InviteSettings: '' as Ref<Class<InviteSettings>>,
|
||||||
|
WorkspaceSetting: '' as Ref<Class<WorkspaceSetting>>
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
Settings: '' as AnyComponent,
|
Settings: '' as AnyComponent,
|
||||||
Profile: '' as AnyComponent,
|
Profile: '' as AnyComponent,
|
||||||
Password: '' as AnyComponent,
|
Password: '' as AnyComponent,
|
||||||
|
WorkspaceSetting: '' as AnyComponent,
|
||||||
WorkspaceSettings: '' as AnyComponent,
|
WorkspaceSettings: '' as AnyComponent,
|
||||||
Integrations: '' as AnyComponent,
|
Integrations: '' as AnyComponent,
|
||||||
Support: '' as AnyComponent,
|
Support: '' as AnyComponent,
|
||||||
@ -145,6 +155,7 @@ export default plugin(settingId, {
|
|||||||
Settings: '' as IntlString,
|
Settings: '' as IntlString,
|
||||||
Setting: '' as IntlString,
|
Setting: '' as IntlString,
|
||||||
WorkspaceSettings: '' as IntlString,
|
WorkspaceSettings: '' as IntlString,
|
||||||
|
Branding: '' as IntlString,
|
||||||
Integrations: '' as IntlString,
|
Integrations: '' as IntlString,
|
||||||
Support: '' as IntlString,
|
Support: '' as IntlString,
|
||||||
Privacy: '' as IntlString,
|
Privacy: '' as IntlString,
|
||||||
|
@ -13,11 +13,32 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import setting, { WorkspaceSetting } from '@hcengineering/setting'
|
||||||
|
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||||
|
import { Component, Icon } from '@hcengineering/ui'
|
||||||
|
import contact, { type GetAvatarUrl } from '@hcengineering/contact'
|
||||||
|
import { getResource } from '@hcengineering/platform'
|
||||||
|
|
||||||
export let mini: boolean = false
|
export let mini: boolean = false
|
||||||
export let workspace: string
|
export let workspace: string
|
||||||
|
const wsSettingQuery = createQuery()
|
||||||
|
let getFileUrl: undefined | GetAvatarUrl = undefined
|
||||||
|
getResource(contact.function.GetFileUrl).then((r) => (getFileUrl = r))
|
||||||
|
const client = getClient()
|
||||||
|
let workspaceSetting: WorkspaceSetting | undefined = undefined
|
||||||
|
wsSettingQuery.query(setting.class.WorkspaceSetting, {}, (res) => {
|
||||||
|
workspaceSetting = res[0]
|
||||||
|
})
|
||||||
|
$: url =
|
||||||
|
getFileUrl !== undefined && workspaceSetting?.icon != null ? getFileUrl(workspaceSetting.icon, 'large') : ['']
|
||||||
|
$: srcset = url?.slice(1)?.join(', ')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="antiLogo red" class:mini>{workspace?.toUpperCase()?.[0] ?? ''}</div>
|
{#if getFileUrl !== undefined && workspaceSetting?.icon != null}
|
||||||
|
<img class="logo-medium" src={url[0]} {srcset} alt={''} />
|
||||||
|
{:else}
|
||||||
|
<div class="antiLogo red" class:mini>{workspace?.toUpperCase()?.[0] ?? ''}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.antiLogo {
|
.antiLogo {
|
||||||
@ -46,4 +67,11 @@
|
|||||||
background-color: rgb(246, 105, 77);
|
background-color: rgb(246, 105, 77);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.logo-medium {
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user