Skip to content

Commit

Permalink
improve focus state styling
Browse files Browse the repository at this point in the history
  • Loading branch information
Onatcer committed Jan 27, 2025
1 parent 49e0458 commit cd207c6
Show file tree
Hide file tree
Showing 27 changed files with 319 additions and 178 deletions.
2 changes: 1 addition & 1 deletion resources/js/Components/Common/Report/ReportTableRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async function deleteReport() {
<span v-else>Copied!</span>
</SecondaryButton>
<button
class="outline-0 focus-visible:ring-2 w-6 h-6 flex items-center justify-center rounded focus-visible:ring-white/80"
class="outline-0 focus-visible:ring-2 w-6 h-6 flex items-center justify-center rounded focus-visible:ring-ring"
@click="openSharableLink">
<ArrowTopRightOnSquareIcon
class="w-4 text-text-tertiary hover:text-text-secondary transition"></ArrowTopRightOnSquareIcon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function downloadCurrentExport() {
@close="showExportModal = false"
:show="showExportModal">
<button
class="text-text-tertiary w-6 mx-auto absolute focus-visible:outline-none focus-visible:ring-2 rounded-full focus-visible:ring-white/80 transition focus-visible:text-text-primary hover:text-text-primary top-2 right-2">
class="text-text-tertiary w-6 mx-auto absolute focus-visible:outline-none focus-visible:ring-2 rounded-full focus-visible:ring-ring transition focus-visible:text-text-primary hover:text-text-primary top-2 right-2">
<XMarkIcon @click="showExportModal = false"></XMarkIcon>
</button>
<div class="text-center text-text-primary py-6">
Expand Down
6 changes: 3 additions & 3 deletions resources/js/Pages/ReportingDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -485,13 +485,13 @@ async function downloadExport(format: ExportFormat) {
</template>
<style lang="postcss">
.navigation-item {
@apply bg-quaternary h-8 w-8 flex items-center justify-center rounded border border-border-primary text-text-tertiary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-white/80;
@apply bg-quaternary h-8 w-8 flex items-center justify-center rounded border border-border-primary text-text-tertiary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-ring;
}
.pagination-item {
@apply bg-secondary h-8 w-8 flex items-center justify-center rounded border border-border-tertiary text-text-secondary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-white/80;
@apply bg-secondary h-8 w-8 flex items-center justify-center rounded border border-border-tertiary text-text-secondary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-ring;
}
.pagination-item[data-selected] {
@apply text-white bg-accent-300/10 border border-accent-300/20 rounded-md font-medium hover:bg-accent-300/20 active:bg-accent-300/20 outline-0 focus-visible:ring-2 focus:ring-white/80 transition ease-in-out duration-150;
@apply text-white bg-accent-300/10 border border-accent-300/20 rounded-md font-medium hover:bg-accent-300/20 active:bg-accent-300/20 outline-0 focus-visible:ring-2 focus:ring-ring transition ease-in-out duration-150;
}
</style>
6 changes: 3 additions & 3 deletions resources/js/Pages/ReportingShared.vue
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,13 @@ watch(currentPage, () => {
</template>
<style lang="postcss">
.navigation-item {
@apply bg-quaternary h-8 w-8 flex items-center justify-center rounded border border-border-primary text-text-tertiary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-white/80;
@apply bg-quaternary h-8 w-8 flex items-center justify-center rounded border border-border-primary text-text-tertiary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-ring;
}
.pagination-item {
@apply bg-secondary h-8 w-8 flex items-center justify-center rounded border border-border-tertiary text-text-secondary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-white/80;
@apply bg-secondary h-8 w-8 flex items-center justify-center rounded border border-border-tertiary text-text-secondary hover:text-text-primary transition cursor-pointer hover:border-border-secondary hover:bg-secondary focus-visible:text-text-primary focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-ring;
}
.pagination-item[data-selected] {
@apply text-white bg-accent-300/10 border border-accent-300/20 rounded-md font-medium hover:bg-accent-300/20 active:bg-accent-300/20 outline-0 focus-visible:ring-2 focus:ring-white/80 transition ease-in-out duration-150;
@apply text-white bg-accent-300/10 border border-accent-300/20 rounded-md font-medium hover:bg-accent-300/20 active:bg-accent-300/20 outline-0 focus-visible:ring-2 focus:ring-ring transition ease-in-out duration-150;
}
</style>
10 changes: 9 additions & 1 deletion resources/js/packages/ui/src/Badge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,24 @@ const borderClasses = computed(() => {
}
return '';
});
const tagClasses = computed(() => {
if (props.tag === 'button') {
return 'hover:bg-tertiary';
}
return '';
});
</script>

<template>
<component
:is="tag"
:class="
twMerge(
tagClasses,
badgeClasses[size],
borderClasses,
'rounded inline-flex items-center font-semibold text-white disabled:text-text-quaternary outline-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80',
'rounded transition inline-flex items-center font-semibold text-white disabled:text-text-quaternary outline-0 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
props.class
)
">
Expand Down
2 changes: 1 addition & 1 deletion resources/js/packages/ui/src/Buttons/PrimaryButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const props = withDefaults(
<button
:type="type"
:disabled="loading"
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 active:bg-accent-300/20 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150">
class="inline-flex items-center px-2 sm:px-3 py-1 sm:py-2 bg-accent-300/10 border border-accent-300/20 rounded-md font-medium text-xs sm:text-sm text-white hover:bg-accent-300/20 active:bg-accent-300/20 focus:outline-none focus-visible:ring-2 focus-visible:border-transparent focus-visible:ring-ring transition ease-in-out duration-150">
<span
:class="
twMerge('flex items-center ', props.icon ? 'space-x-1.5' : '')
Expand Down
2 changes: 1 addition & 1 deletion resources/js/packages/ui/src/Buttons/SecondaryButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const sizeClasses = {
:disabled="loading"
:class="
twMerge(
'bg-button-secondary-background border border-button-secondary-border hover:bg-button-secondary-background-hover shadow-sm transition text-white rounded-lg font-semibold inline-flex items-center space-x-1.5 focus-visible:border-input-border-active focus:outline-none focus:ring-0 disabled:opacity-25 ease-in-out',
'bg-button-secondary-background border border-button-secondary-border hover:bg-button-secondary-background-hover shadow-sm transition text-white rounded-lg font-semibold inline-flex items-center space-x-1.5 focus-visible:outline-none focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring focus:border-transparent disabled:opacity-25 ease-in-out',
sizeClasses[props.size],
props.class
)
Expand Down
4 changes: 2 additions & 2 deletions resources/js/packages/ui/src/GroupedItemsCountButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ const expandedStatusClasses = computed(() => {
if (props.expanded) {
return 'border-card-border border bg-card-background-active text-white';
}
return 'border-card-border border bg-card-background text-muted';
return 'border-card-border border bg-card-background hover:bg-card-background-active hover:text-text-primary transition text-muted';
});
</script>

<template>
<button
:class="
twMerge(
'font-medium rounded flex items-center transition justify-center',
'font-medium rounded flex items-center transition justify-center focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:border-transparent',
expandedStatusClasses,
props.size
)
Expand Down
4 changes: 2 additions & 2 deletions resources/js/packages/ui/src/Input/BillableToggleButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const iconSizeClasses = computed(() => {
});
const iconSizeWrapperClasses =
props.size === 'small' ? 'w-6 sm:w-8 h-6 sm:h-8' : 'w-11 h-11';
props.size === 'small' ? 'w-6 sm:w-8 h-6 sm:h-8' : 'w-10 h-10';
</script>

<template>
Expand All @@ -45,7 +45,7 @@ const iconSizeWrapperClasses =
twMerge(
iconColorClasses,
iconSizeWrapperClasses,
'flex-shrink-0 ring-0 focus:outline-none focus:ring-0 transition focus:bg-card-background-separator hover:bg-card-background-separator rounded-full flex items-center justify-center'
'flex-shrink-0 ring-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring transition focus:bg-card-background-separator hover:bg-card-background-separator rounded-full flex items-center justify-center'
)
">
<BillableIcon :class="iconSizeClasses"></BillableIcon>
Expand Down
2 changes: 1 addition & 1 deletion resources/js/packages/ui/src/Input/Checkbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ const proxyChecked = computed({
type="checkbox"
:id="id"
:value="value"
class="h-4 w-4 rounded bg-card-background border-input-border text-accent-500/80 focus:ring-accent-500/80" />
class="h-4 w-4 rounded bg-card-background border-input-border text-accent-500/80 focus:outline-none focus:ring-ring/50 focus-visible:outline-none focus-visible:ring-ring/50" />
</template>
2 changes: 1 addition & 1 deletion resources/js/packages/ui/src/Input/TextInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const model = defineModel();
ref="input"
:class="
twMerge(
'border-input-border border bg-input-background text-white focus:ring-input-border-active focus:ring-0 focus-visible:border-input-border-active rounded-md shadow-sm',
'border-input-border border bg-input-background text-white focus-visible:ring-2 focus-visible:ring-ring focus-visible:border-transparent rounded-md shadow-sm',
props.class
)
"
Expand Down
119 changes: 119 additions & 0 deletions resources/js/packages/ui/src/Input/TimePickerSimple.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { getLocalizedDayJs } from '@/packages/ui/src/utils/time';
import { useFocus } from '@vueuse/core';
import { TextInput } from '@/packages/ui/src';
import { twMerge } from 'tailwind-merge';
// This has to be a localized timestamp, not UTC
const model = defineModel<string | null>({
default: null,
});
const props = withDefaults(
defineProps<{
size: 'base' | 'large';
focus: boolean;
}>(),
{
size: 'base',
focus: false,
}
);
function updateTime(event: Event) {
const target = event.target as HTMLInputElement;
const newValue = target.value.trim();
if (newValue.split(':').length === 2) {
const [hours, minutes] = newValue.split(':');
if (!isNaN(parseInt(hours)) && !isNaN(parseInt(minutes))) {
model.value = getLocalizedDayJs(model.value)
.set('hours', Math.min(parseInt(hours), 23))
.set('minutes', Math.min(parseInt(minutes), 59))
.format();
emit('changed', model.value);
}
}
// check if input is only numbers
else if (/^\d+$/.test(newValue)) {
if (newValue.length === 4) {
// parse 1300 to 13:00
const [hours, minutes] = [
newValue.slice(0, 2),
newValue.slice(2, 4),
];
model.value = getLocalizedDayJs(model.value)
.set('hours', Math.min(parseInt(hours), 23))
.set('minutes', Math.min(parseInt(minutes), 59))
.format();
emit('changed', model.value);
} else if (newValue.length === 3) {
// parse 130 to 01:30
const [hours, minutes] = [
newValue.slice(0, 1),
newValue.slice(1, 3),
];
model.value = getLocalizedDayJs(model.value)
.set('hours', Math.min(parseInt(hours), 23))
.set('minutes', Math.min(parseInt(minutes), 59))
.format();
emit('changed', model.value);
} else if (newValue.length === 2) {
// parse 13 to 13:00
model.value = getLocalizedDayJs(model.value)
.set('hours', Math.min(parseInt(newValue), 23))
.set('minutes', 0)
.format();
emit('changed', model.value);
} else if (newValue.length === 1) {
// parse 1 to 01:00
model.value = getLocalizedDayJs(model.value)
.set('hours', Math.min(parseInt(newValue), 23))
.set('minutes', 0)
.format();
emit('changed', model.value);
}
}
inputValue.value = getLocalizedDayJs(model.value).format('HH:mm');
}
watch(model, (value) => {
inputValue.value = value ? getLocalizedDayJs(value).format('HH:mm') : null;
});
const timeInput = ref<HTMLInputElement | null>(null);
const emit = defineEmits(['changed']);
useFocus(timeInput, { initialValue: props.focus });
const inputValue = ref(
model.value ? getLocalizedDayJs(model.value).format('HH:mm') : null
);
const open = ref(false);
</script>

<template>
<TextInput
v-bind="$attrs"
v-model="inputValue"
ref="timeInput"
:class="
twMerge('text-center w-24 px-3 py-2', size === 'large' && 'w-28')
"
@blur="updateTime"
@keydown.enter="
updateTime($event);
open = false;
"
@keydown.tab="open = false"
@focus="($event.target as HTMLInputElement).select()"
@mouseup="($event.target as HTMLInputElement).select()"
@click="($event.target as HTMLInputElement).select()"
@pointerup="($event.target as HTMLInputElement).select()"
@focusin="open = true"
data-testid="time_picker_input"
type="text" />
</template>

<style scoped></style>
14 changes: 8 additions & 6 deletions resources/js/packages/ui/src/Input/TimeRangeSelector.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { defineProps, ref, watch } from 'vue';
import TimePicker from '@/packages/ui/src/Input/TimePicker.vue';
import { useFocusWithin } from '@vueuse/core';
import DatePicker from '@/packages/ui/src/Input/DatePicker.vue';
import {
getDayJsInstance,
getLocalizedDayJs,
} from '@/packages/ui/src/utils/time';
import dayjs from 'dayjs';
import TimePickerSimple from '@/packages/ui/src/Input/TimePickerSimple.vue';
const props = defineProps<{
start: string;
Expand All @@ -16,7 +16,7 @@ const props = defineProps<{
}>();
// The timestamps for the changed event are UTC
const emit = defineEmits(['changed']);
const emit = defineEmits(['changed', 'close']);
const tempStart = ref(
props.start ? getLocalizedDayJs(props.start).format() : dayjs().format()
Expand Down Expand Up @@ -58,24 +58,26 @@ watch(focused, (newValue, oldValue) => {
<div class="px-2">
<div class="font-bold text-white text-sm pb-2">Start</div>
<div class="space-y-2">
<TimePicker
<TimePickerSimple
data-testid="time_entry_range_start"
tabindex="0"
:focus
@changed="updateTimeEntry"
v-model="tempStart"></TimePicker>
v-model="tempStart"></TimePickerSimple>
<DatePicker
class="text-xs text-text-tertiary max-w-24 px-1.5 py-1.5"
@changed="updateTimeEntry"
@blur.stop.prevent="emit('close')"
v-model="tempStart"></DatePicker>
</div>
</div>
<div class="px-2">
<div class="font-bold text-white text-sm pb-2">End</div>
<div v-if="tempEnd !== null" class="space-y-2">
<TimePicker
<TimePickerSimple
data-testid="time_entry_range_end"
@changed="updateTimeEntry"
v-model="tempEnd"></TimePicker>
v-model="tempEnd"></TimePickerSimple>
<DatePicker
class="text-xs text-text-tertiary max-w-24 px-1.5 py-1.5"
@changed="updateTimeEntry"
Expand Down
4 changes: 2 additions & 2 deletions resources/js/packages/ui/src/MoreOptionsDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ defineProps<{
<Dropdown align="bottom-end">
<template #trigger>
<button
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity text-muted"
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-2 focus-visible:ring-ring focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity text-muted"
:aria-label="label">
<svg
class="h-10 w-10 p-2 rounded-full"
class="h-8 w-8 p-1 rounded-full"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path
Expand Down
6 changes: 2 additions & 4 deletions resources/js/packages/ui/src/Tag/TagBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ const indicatorClasses = {

<template>
<Badge :name :size :tag :class="props.class" :color :border>
<TagIcon
:style="{ color: color }"
:class="twMerge(indicatorClasses[size])"></TagIcon>
<span>
<TagIcon :class="twMerge(indicatorClasses[size])"></TagIcon>
<span v-if="name">
{{ name }}
</span>
</Badge>
Expand Down
Loading

0 comments on commit cd207c6

Please sign in to comment.