mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 04:02:15 +01:00
add outline and secondary variants to TimeTrackerStartStop button to reduce visual complexity
This commit is contained in:
@@ -53,7 +53,7 @@ test('test that starting and stopping an empty time entry shows a new time entry
|
|||||||
// Test that description update works
|
// Test that description update works
|
||||||
|
|
||||||
async function assertThatTimeEntryRowIsStopped(newTimeEntry: Locator) {
|
async function assertThatTimeEntryRowIsStopped(newTimeEntry: Locator) {
|
||||||
await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass(/bg-accent-300\/70/);
|
await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass(/bg-tertiary/);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('test that updating a description of a time entry in the overview works on blur', async ({
|
test('test that updating a description of a time entry in the overview works on blur', async ({
|
||||||
@@ -224,7 +224,7 @@ test('test that starting a time entry from the overview works', async ({ page })
|
|||||||
|
|
||||||
const newTimeEntry = timeEntryRows.first();
|
const newTimeEntry = timeEntryRows.first();
|
||||||
const startButton = newTimeEntry.getByTestId('timer_button');
|
const startButton = newTimeEntry.getByTestId('timer_button');
|
||||||
await expect(startButton).toHaveClass(/bg-accent-300\/70/);
|
await expect(startButton).toHaveClass(/bg-tertiary/);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForResponse(async (response) => {
|
page.waitForResponse(async (response) => {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const isRunningInDifferentOrganization = computed(() => {
|
|||||||
<TimeTrackerStartStop
|
<TimeTrackerStartStop
|
||||||
:active="isActive"
|
:active="isActive"
|
||||||
size="base"
|
size="base"
|
||||||
|
variant="outline"
|
||||||
@changed="setActiveState"></TimeTrackerStartStop>
|
@changed="setActiveState"></TimeTrackerStartStop>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ async function startTaskTimer() {
|
|||||||
</ProjectBadge>
|
</ProjectBadge>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<TimeTrackerStartStop @changed="startTaskTimer"></TimeTrackerStartStop>
|
<TimeTrackerStartStop
|
||||||
|
variant="outline"
|
||||||
|
@changed="startTaskTimer"></TimeTrackerStartStop>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ async function handleDeleteTimeEntry() {
|
|||||||
@changed="updateStartEndTime"></TimeEntryRowDurationInput>
|
@changed="updateStartEndTime"></TimeEntryRowDurationInput>
|
||||||
<TimeTrackerStartStop
|
<TimeTrackerStartStop
|
||||||
:active="!!(timeEntry.start && !timeEntry.end)"
|
:active="!!(timeEntry.start && !timeEntry.end)"
|
||||||
class="opacity-20 flex focus-visible:opacity-100 group-hover:opacity-100"
|
variant="secondary"
|
||||||
|
class="opacity-60 flex focus-visible:opacity-100 group-hover:opacity-100"
|
||||||
@changed="onStartStopClick"></TimeTrackerStartStop>
|
@changed="onStartStopClick"></TimeTrackerStartStop>
|
||||||
<TimeEntryMoreOptionsDropdown
|
<TimeEntryMoreOptionsDropdown
|
||||||
@edit="handleEdit"
|
@edit="handleEdit"
|
||||||
|
|||||||
@@ -1,38 +1,72 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
import { computed } from 'vue';
|
import { cn } from './utils/cn';
|
||||||
|
|
||||||
|
const timeTrackerVariants = cva(
|
||||||
|
'flex items-center justify-center transition focus:outline-0 rounded-full',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
primary:
|
||||||
|
'text-white ring-accent-200/10 focus-visible:ring-ring focus-visible:ring-2 ring-4 sm:ring-[6px]',
|
||||||
|
secondary:
|
||||||
|
'bg-tertiary text-text-tertiary hover:text-text-primary focus:ring-2 focus:ring-border-tertiary',
|
||||||
|
outline:
|
||||||
|
'border border-border-primary text-text-tertiary hover:text-text-primary hover:bg-tertiary/50',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
small: 'w-6 h-6',
|
||||||
|
base: 'w-9 h-9 hover:scale-110',
|
||||||
|
large: 'w-11 h-11 hover:scale-110',
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
true: '',
|
||||||
|
false: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
compoundVariants: [
|
||||||
|
{
|
||||||
|
variant: 'primary',
|
||||||
|
active: true,
|
||||||
|
class: 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant: 'primary',
|
||||||
|
active: false,
|
||||||
|
class: 'bg-accent-300/70 hover:bg-accent-400/70 focus:bg-accent-700',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'primary',
|
||||||
|
size: 'base',
|
||||||
|
active: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type TimeTrackerVariants = VariantProps<typeof timeTrackerVariants>;
|
||||||
|
|
||||||
const emit = defineEmits(['changed']);
|
const emit = defineEmits(['changed']);
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
size?: 'base' | 'large' | 'small';
|
variant?: TimeTrackerVariants['variant'];
|
||||||
|
size?: TimeTrackerVariants['size'];
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
variant: 'primary',
|
||||||
size: 'base',
|
size: 'base',
|
||||||
active: false,
|
active: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const buttonSizeClasses = {
|
|
||||||
small: 'w-6 h-6 bg-accent-200/40 hover:bg-accent-300/70',
|
|
||||||
base: 'w-8 h-8 bg-accent-200/40 hover:scale-110 hover:bg-accent-300/70 ring-accent-200/10 focus-visible:ring-ring ring-4 hover:ring-4',
|
|
||||||
large: 'w-11 h-11 ring-accent-200/10 focus-visible:ring-ring focus-visible:ring-2 ring-4 sm:ring-[6px] hover:scale-110',
|
|
||||||
};
|
|
||||||
const iconClass = {
|
const iconClass = {
|
||||||
small: 'w-2.5 h-2.5',
|
small: 'w-2.5 h-2.5',
|
||||||
base: 'w-3.5 h-3.5',
|
base: 'w-3 h-3',
|
||||||
large: 'w-4 h-4',
|
large: 'w-4 h-4',
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonColorClasses = computed(() => {
|
|
||||||
if (props.active) {
|
|
||||||
return 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80';
|
|
||||||
} else {
|
|
||||||
return 'bg-accent-300/70 hover:bg-accent-400/70 focus:bg-accent-700';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function toggleState() {
|
function toggleState() {
|
||||||
emit('changed', !props.active);
|
emit('changed', !props.active);
|
||||||
}
|
}
|
||||||
@@ -41,18 +75,12 @@ function toggleState() {
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
data-testid="timer_button"
|
data-testid="timer_button"
|
||||||
:class="
|
:class="cn(timeTrackerVariants({ variant, size, active }))"
|
||||||
twMerge(
|
|
||||||
buttonSizeClasses[size],
|
|
||||||
buttonColorClasses,
|
|
||||||
'flex items-center justify-center py-1 transition focus:outline-0 rounded-full text-white '
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="toggleState">
|
@click="toggleState">
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<svg
|
<svg
|
||||||
v-if="props.active"
|
v-if="props.active"
|
||||||
:class="iconClass[size]"
|
:class="iconClass[size ?? 'base']"
|
||||||
viewBox="0 0 14 14"
|
viewBox="0 0 14 14"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -64,7 +92,7 @@ function toggleState() {
|
|||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
v-else
|
v-else
|
||||||
:class="iconClass[size]"
|
:class="iconClass[size ?? 'base']"
|
||||||
viewBox="0 0 7 8"
|
viewBox="0 0 7 8"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
--color-bg-primary: oklch(0.14 0.0041 285.97);
|
--color-bg-primary: oklch(0.14 0.0041 285.97);
|
||||||
--color-bg-secondary: oklch(0.18 0.005 285.97);
|
--color-bg-secondary: oklch(0.18 0.005 285.97);
|
||||||
--color-bg-tertiary: oklch(0.22 0.0112 285.97);
|
--color-bg-tertiary: oklch(0.22 0.0112 285.97);
|
||||||
--color-bg-quaternary: oklch(0.175 0.005 285.97);
|
--color-bg-quaternary: oklch(0.26 0.015 285.97);
|
||||||
--color-bg-background: oklch(0.1 0 0);
|
--color-bg-background: oklch(0.1 0 0);
|
||||||
--color-text-primary: #ffffff;
|
--color-text-primary: #ffffff;
|
||||||
--color-text-secondary: #e3e4e6;
|
--color-text-secondary: #e3e4e6;
|
||||||
|
|||||||
Reference in New Issue
Block a user