mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-11 04:07:43 +02:00
Status filters in table view (#693)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 { Class, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import task, { DoneState, SpaceWithStates, State, Task } from '@anticrm/task'
|
||||
import { ScrollBox } from '@anticrm/ui'
|
||||
import Label from '@anticrm/ui/src/components/Label.svelte'
|
||||
import { Table } from '@anticrm/view-resources'
|
||||
import Lost from './icons/Lost.svelte'
|
||||
import Won from './icons/Won.svelte'
|
||||
import StatesBar from './state/StatesBar.svelte'
|
||||
|
||||
export let _class: Ref<Class<Task>>
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let options: FindOptions<Task> | undefined
|
||||
export let config: string[]
|
||||
export let search: string
|
||||
|
||||
let doneStatusesView: boolean = false
|
||||
let state: Ref<State> | undefined = undefined
|
||||
let selectedDoneStates: Set<Ref<DoneState>> = new Set<Ref<DoneState>>()
|
||||
let resConfig = config
|
||||
let query = {}
|
||||
let doneStates: DoneState[] = []
|
||||
|
||||
function updateConfig (): void {
|
||||
if (state !== undefined) {
|
||||
resConfig = config.filter((p) => p !== '$lookup.state')
|
||||
return
|
||||
}
|
||||
if (selectedDoneStates.size === 1) {
|
||||
resConfig = config.filter((p) => p !== '$lookup.doneState')
|
||||
return
|
||||
}
|
||||
resConfig = config
|
||||
}
|
||||
|
||||
const doneStateQuery = createQuery()
|
||||
doneStateQuery.query(
|
||||
task.class.DoneState,
|
||||
{
|
||||
space
|
||||
},
|
||||
(res) => (doneStates = res)
|
||||
)
|
||||
|
||||
async function updateQuery (): Promise<void> {
|
||||
updateConfig()
|
||||
const result = {} as DocumentQuery<Task>
|
||||
if (search !== '') {
|
||||
result.$search = search
|
||||
}
|
||||
result.space = space
|
||||
if (state) {
|
||||
result.state = state
|
||||
}
|
||||
if (selectedDoneStates.size > 0) {
|
||||
result.doneState = {
|
||||
$in: Array.from(selectedDoneStates)
|
||||
}
|
||||
}
|
||||
query = result
|
||||
}
|
||||
|
||||
function doneStateClick (id: Ref<DoneState>): void {
|
||||
if (selectedDoneStates.has(id)) {
|
||||
selectedDoneStates.delete(id)
|
||||
} else {
|
||||
selectedDoneStates.add(id)
|
||||
}
|
||||
selectedDoneStates = selectedDoneStates
|
||||
updateQuery()
|
||||
}
|
||||
|
||||
$: updateQuery()
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="flex-between mb-4 mt-4 header">
|
||||
<div class="flex-row-center">
|
||||
<div
|
||||
class="button flex-center"
|
||||
class:active={!doneStatusesView}
|
||||
on:click={() => {
|
||||
doneStatusesView = false
|
||||
state = undefined
|
||||
selectedDoneStates.clear()
|
||||
updateQuery()
|
||||
}}
|
||||
>
|
||||
<Label label={'All statuses'} />
|
||||
</div>
|
||||
<div
|
||||
class="button flex-center ml-4"
|
||||
class:active={doneStatusesView}
|
||||
on:click={() => {
|
||||
doneStatusesView = true
|
||||
state = undefined
|
||||
selectedDoneStates.clear()
|
||||
updateQuery()
|
||||
}}
|
||||
>
|
||||
<Label label={'Done statuses'} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-row-center caption-color states">
|
||||
{#if doneStatusesView}
|
||||
{#each doneStates as state}
|
||||
<div
|
||||
class="doneState flex-row-center"
|
||||
class:won={state._class === task.class.WonState}
|
||||
class:lost={state._class === task.class.LostState}
|
||||
class:disable={!selectedDoneStates.has(state._id)}
|
||||
on:click={() => {
|
||||
doneStateClick(state._id)
|
||||
}}
|
||||
>
|
||||
{#if state._class === task.class.WonState}
|
||||
<Won size="medium" />
|
||||
{:else}
|
||||
<Lost size="medium" />
|
||||
{/if}
|
||||
<span class="ml-2">
|
||||
{state.title}
|
||||
</span>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<StatesBar bind:state {space} on:change={updateQuery} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<ScrollBox vertical stretch noShift>
|
||||
<Table {_class} {query} config={resConfig} {options} enableChecking />
|
||||
</ScrollBox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
padding-bottom: 2.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-left: 2.5rem;
|
||||
margin-right: 1.75rem;
|
||||
|
||||
.states {
|
||||
max-width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
cursor: pointer;
|
||||
height: 2.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
&:hover {
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
}
|
||||
&.active {
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
color: var(--theme-caption-color);
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
}
|
||||
}
|
||||
.doneState {
|
||||
cursor: pointer;
|
||||
height: 2.5rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
|
||||
&.won {
|
||||
background-color: #60b96e;
|
||||
}
|
||||
|
||||
&.lost {
|
||||
background-color: #f06c63;
|
||||
}
|
||||
|
||||
&.disable {
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
}
|
||||
}
|
||||
|
||||
.doneState + .doneState {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} fill-opacity="0.0" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="6" stroke="white" />
|
||||
<path d="M12 12L4 4" stroke="white" />
|
||||
</svg>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'currentColor'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} fill-opacity="0.0" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4 10V3.83337C4 3.59767 4 3.47982 4.07322 3.4066C4.14645 3.33337 4.2643 3.33337 4.5 3.33337H10.7929C11.3066 3.33337 11.5635 3.33337 11.6274 3.4877C11.6913 3.64203 11.5097 3.82366 11.1464 4.18693L8.94951 6.38386C8.81618 6.5172 8.74951 6.58386 8.74951 6.66671C8.74951 6.74955 8.81618 6.81622 8.94951 6.94955L11.1464 9.14649C11.5097 9.50975 11.6913 9.69138 11.6274 9.84571C11.5635 10 11.3066 10 10.7929 10H4ZM4 10V12.6667"
|
||||
stroke="white"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
@@ -0,0 +1,155 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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 { Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import task, { SpaceWithStates, State } from '@anticrm/task'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import StatesBarElement from './StatesBarElement.svelte'
|
||||
|
||||
export let space: Ref<SpaceWithStates>
|
||||
export let state: Ref<State> | undefined = undefined
|
||||
let states: State[] = []
|
||||
|
||||
let div: HTMLElement
|
||||
let maskLeft: boolean = false
|
||||
let maskRight: boolean = false
|
||||
let mask: 'left' | 'right' | 'both' | 'none' = 'none'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const statesQuery = createQuery()
|
||||
statesQuery.query(
|
||||
task.class.State,
|
||||
{ space },
|
||||
(res) => {
|
||||
states = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const checkMask = (): void => {
|
||||
maskLeft = !!(div && div.scrollLeft > 1)
|
||||
maskRight = !!(div && div.scrollWidth - div.scrollLeft - div.clientWidth > 1)
|
||||
if (maskLeft || maskRight) {
|
||||
if (maskLeft && maskRight) mask = 'both'
|
||||
else if (maskLeft) mask = 'left'
|
||||
else if (maskRight) mask = 'right'
|
||||
} else mask = 'none'
|
||||
}
|
||||
|
||||
const selectItem = (ev: Event, item: State): void => {
|
||||
const el: HTMLElement = ev.currentTarget as HTMLElement
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
|
||||
if (state === item._id) {
|
||||
state = undefined
|
||||
} else {
|
||||
state = item._id
|
||||
}
|
||||
dispatch('change')
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (div) {
|
||||
checkMask()
|
||||
div.addEventListener('scroll', checkMask)
|
||||
}
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (div) div.removeEventListener('scroll', checkMask)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={div} class="flex-row-center statusesbar-container {mask}">
|
||||
{#each states as item, i}
|
||||
<div
|
||||
class="flex-row-center cursor-pointer step-lr25"
|
||||
class:selected={item._id === state}
|
||||
on:click={(ev) => {
|
||||
selectItem(ev, item)
|
||||
}}
|
||||
>
|
||||
<StatesBarElement side={'left'} kind={i ? 'arrow' : 'round'} selected={item._id === state} color={item.color} />
|
||||
<div
|
||||
class="flex-row-center overflow-label label"
|
||||
style={item._id === state ? `background-color: ${item.color};` : ''}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
<StatesBarElement
|
||||
side={'right'}
|
||||
kind={i < states.length - 1 ? 'arrow' : 'round'}
|
||||
selected={item._id === state}
|
||||
color={item.color}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.statusesbar-container {
|
||||
overflow-x: auto;
|
||||
padding: 0.125rem 0;
|
||||
|
||||
&::-webkit-scrollbar:horizontal {
|
||||
height: 0.125rem;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
margin: 0.25rem;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--theme-bg-accent-color);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 0.5rem 2rem;
|
||||
height: 2.25rem;
|
||||
max-height: 2.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-bg-enabled);
|
||||
border-top: 1px solid var(--theme-button-border-enabled);
|
||||
border-bottom: 1px solid var(--theme-button-border-enabled);
|
||||
}
|
||||
|
||||
.selected {
|
||||
.label {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
.left {
|
||||
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem);
|
||||
}
|
||||
.right {
|
||||
mask-image: linear-gradient(to left, rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 1) 1rem);
|
||||
}
|
||||
.both {
|
||||
mask-image: linear-gradient(
|
||||
to right,
|
||||
rgba(0, 0, 0, 0) 0,
|
||||
rgba(0, 0, 0, 1) 1rem,
|
||||
rgba(0, 0, 0, 1) calc(100% - 1rem),
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
}
|
||||
.none {
|
||||
mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 1);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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">
|
||||
export let side: 'left' | 'right'
|
||||
export let kind: 'round' | 'arrow'
|
||||
export let selected: boolean = false
|
||||
export let color: string
|
||||
</script>
|
||||
|
||||
<svg width=".5rem" height="2.25rem" viewBox="0 0 8 36" class:selected xmlns="http://www.w3.org/2000/svg">
|
||||
{#if side === 'left' && kind === 'arrow'}
|
||||
<path
|
||||
class="bg"
|
||||
style={selected ? `fill: ${color}` : ''}
|
||||
d="M1,0C0.3,0-0.2,0.7,0.1,1.4l6,15.9c0.2,0.5,0.2,1,0,1.4l-6,15.9C-0.2,35.3,0.3,36,1,36h7V0H1z"
|
||||
/>
|
||||
<path
|
||||
class="border"
|
||||
d="M1,35l6-15.9c0.3-0.7,0.3-1.4,0-2.1L1,1h7V0H1C0.7,0,0.4,0.2,0.2,0.4C0,0.7-0.1,1,0.1,1.4 l6,15.9c0.2,0.5,0.2,1,0,1.4l-6,15.9C-0.1,35,0,35.3,0.2,35.6S0.7,36,1,36h7v-1H1z"
|
||||
/>
|
||||
{:else if side === 'left' && kind === 'round'}
|
||||
<path class="bg" style={selected ? `fill: ${color}` : ''} d="M0,8v10v10c0,4.4,3.6,8,8,8V0C3.6,0,0,3.6,0,8z" />
|
||||
<path class="border" d="M1,28V8c0-3.9,3.1-7,7-7V0C3.6,0,0,3.6,0,8v20c0,4.4,3.6,8,8,8v-1C4.1,35,1,31.9,1,28z" />
|
||||
{:else if side === 'right' && kind === 'arrow'}
|
||||
<path
|
||||
class="bg"
|
||||
style={selected ? `fill: ${color}` : ''}
|
||||
d="M1.8,1.3C1.5,0.5,0.8,0,0,0v36c0.8,0,1.5-0.5,1.8-1.3l6.1-16c0.2-0.5,0.2-1,0-1.4L1.8,1.3z"
|
||||
/>
|
||||
<path
|
||||
class="border"
|
||||
d="M1.8,1.3L1.8,1.3L1.8,1.3c0,0-0.1-0.2-0.1-0.3L1.6,0.8C1.2,0.3,0.6,0,0,0v1 c0.4,0,0.7,0.3,0.9,0.6l6.1,16c0.1,0.2,0.1,0.5,0,0.7l-6.1,16C0.7,34.7,0.4,35,0,35v1c0.8,0,1.5-0.5,1.8-1.3l6.1-16 c0.2-0.5,0.2-1,0-1.4L1.8,1.3z"
|
||||
/>
|
||||
{:else if side === 'right' && kind === 'round'}
|
||||
<path class="bg" style={selected ? `fill: ${color}` : ''} d="M0,0v36c4.4,0,8-3.6,8-8V8C8,3.6,4.4,0,0,0z" />
|
||||
<path class="border" d="M0,0v1c3.9,0,7,3.1,7,7v20c0,3.9-3.1,7-7,7v1c4.4,0,8-3.6,8-8V8C8,3.6,4.4,0,0,0z" />
|
||||
{:else}
|
||||
<rect x={0} y={0} width={'100%'} height={'100%'} fill={'red'} opacity={0.5} />
|
||||
{/if}
|
||||
</svg>
|
||||
|
||||
<style lang="scss">
|
||||
.bg {
|
||||
fill: var(--theme-button-bg-enabled);
|
||||
}
|
||||
.border {
|
||||
fill: var(--theme-button-border-enabled);
|
||||
}
|
||||
.selected {
|
||||
.border {
|
||||
fill: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -13,9 +13,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import task, { SpaceWithStates, State } from '@anticrm/task'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -24,14 +23,29 @@
|
||||
let states: State[] = []
|
||||
const dispatch = createEventDispatcher()
|
||||
const statesQuery = createQuery()
|
||||
statesQuery.query(task.class.State, { space }, (res) => { states = res })
|
||||
|
||||
statesQuery.query(
|
||||
task.class.State,
|
||||
{ space },
|
||||
(res) => {
|
||||
states = res
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="flex-col statuses-container">
|
||||
{#each states as state}
|
||||
<div class="flex-row-center state" on:click={() => { dispatch('close', state) }}>
|
||||
<div class="color" style="background-color: {state.color}"></div>
|
||||
<div
|
||||
class="flex-row-center state"
|
||||
on:click={() => {
|
||||
dispatch('close', state)
|
||||
}}
|
||||
>
|
||||
<div class="color" style="background-color: {state.color}" />
|
||||
{state.title}
|
||||
</div>
|
||||
{/each}
|
||||
@@ -39,28 +53,30 @@
|
||||
|
||||
<style lang="scss">
|
||||
.statuses-container {
|
||||
padding: .5rem;
|
||||
padding: 0.5rem;
|
||||
max-height: 100%;
|
||||
min-width: 10rem;
|
||||
color: var(--theme-caption-color);
|
||||
background-color: var(--theme-button-bg-focused);
|
||||
border: 1px solid var(--theme-button-border-enabled);
|
||||
border-radius: .75rem;
|
||||
border-radius: 0.75rem;
|
||||
user-select: none;
|
||||
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, .35));
|
||||
filter: drop-shadow(0 1.5rem 4rem rgba(0, 0, 0, 0.35));
|
||||
|
||||
.state {
|
||||
padding: .5rem;
|
||||
border-radius: .5rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover { background-color: var(--theme-button-bg-hovered); }
|
||||
&:hover {
|
||||
background-color: var(--theme-button-bg-hovered);
|
||||
}
|
||||
.color {
|
||||
margin-right: .75rem;
|
||||
margin-right: 0.75rem;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: .25rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user