Skip to content

Commit

Permalink
Equipment item editing
Browse files Browse the repository at this point in the history
Fixes #154
  • Loading branch information
Perdolique committed Nov 18, 2024
1 parent 74dd9fc commit d56579a
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 228 deletions.
1 change: 1 addition & 0 deletions app/assets/styles/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
--font-weight-bold: 700;

// Borders
--border-radius-4: 0.25rem;
--border-radius-8: 0.5rem;
--border-radius-12: 0.75rem;
--border-radius-16: 1rem;
Expand Down
1 change: 1 addition & 0 deletions app/components/PerdMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
transform: translateY(calc(-1 * var(--spacing)));
top: calc(100% + var(--spacing));
right: 0;
z-index: 1;
overflow: hidden;
background-color: var(--secondary-50);
border: 1px solid var(--secondary-200);
Expand Down
5 changes: 5 additions & 0 deletions app/components/PerdTag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,10 @@
border-radius: var(--border-radius-8);
font-weight: var(--font-weight-medium);
border: 1px solid color-mix(in oklch, v-bind(tagColor), black 5%);
@media (prefers-color-scheme: dark) {
background-color: color-mix(in oklch, v-bind(tagColor), black 50%);
border: 1px solid color-mix(in oklch, v-bind(tagColor), black 45%);
}
}
</style>
141 changes: 141 additions & 0 deletions app/components/_experimental/EditableText.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<template>
<component
:is="is"
ref="componentRef"
tabindex="0"
:contenteditable="isEditMode"
:class="$style.component"
@dblclick="onDoubleClick"
@keydown.escape.prevent="disableEditMode"
@keydown.enter.prevent="onEnterDown"
@blur.capture="onBlur"
@touchstart.passive.stop="onTouchStart"
>
{{ model }}
</component>
</template>

<script lang="ts" setup>
interface Props {
readonly is?: string | Component;
}
const { is = 'span' } = defineProps<Props>();
const model = defineModel<string>({
required: true,
});
let lastTap = 0;
const componentRef = useTemplateRef('componentRef');
const isEditMode = ref(false);
function enableEditMode() {
if (componentRef.value instanceof HTMLElement) {
isEditMode.value = true;
}
}
function disableEditMode() {
isEditMode.value = false;
}
function setCaretToEnd(element: HTMLElement) {
const range = document.createRange();
const selection = window.getSelection();
range.selectNodeContents(element);
range.collapse();
selection?.removeAllRanges();
selection?.addRange(range);
}
function activateEditMode() {
if (isEditMode.value === false && componentRef.value instanceof HTMLElement) {
enableEditMode();
}
}
function onDoubleClick() {
if (isEditMode.value) {
return;
}
activateEditMode();
if (componentRef.value instanceof HTMLElement) {
setCaretToEnd(componentRef.value);
}
}
function onEnterDown() {
if (componentRef.value instanceof HTMLElement) {
if (isEditMode.value) {
componentRef.value.blur();
} else {
activateEditMode();
setCaretToEnd(componentRef.value);
}
}
}
function onTouchStart() {
if (isEditMode.value === false) {
const now = new Date().getTime();
const timeSince = now - lastTap;
if (timeSince < 300 && timeSince > 0) {
activateEditMode();
}
lastTap = new Date().getTime();
}
}
function onBlur() {
if (isEditMode.value) {
if (componentRef.value instanceof HTMLElement) {
const rawTextContent = componentRef.value.textContent ?? '';
const processedText = rawTextContent.trim();
if (processedText !== model.value) {
model.value = processedText;
}
disableEditMode();
}
}
}
watch(model, (newValue) => {
if (componentRef.value instanceof HTMLElement) {
componentRef.value.textContent = newValue;
}
});
</script>

<style module>
.component {
transition: background-color var(--transition-time-quick);
border-radius: var(--border-radius-4);
cursor: pointer;
outline: 2px solid transparent;
outline-offset: 2px;
&[contenteditable="true"] {
background-color: var(--background-50);
cursor: text;
}
&:focus-visible,
&[contenteditable="true"] {
outline: 2px solid var(--accent-300);
}
&:hover:not([contenteditable="true"]) {
background-color: var(--background-50);
}
}
</style>
30 changes: 20 additions & 10 deletions app/components/dialogs/InputDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<TextInput
required
autofocus
ref="textInputRef"
v-model="input"
:placeholder="placeholder"
:class="$style.input"
Expand Down Expand Up @@ -41,18 +42,19 @@
import ModalDialog from './ModalDialog.vue'
interface Props {
readonly headerText: string
readonly placeholder: string
readonly addButtonText: string
readonly maxlength: number
readonly headerText: string;
readonly placeholder: string;
readonly addButtonText: string;
readonly maxlength: number;
readonly initialValue?: string;
}
type Emits = (event: 'submit', input: string) => void
defineProps<Props>()
const { initialValue = '' } = defineProps<Props>()
const textInputRef = useTemplateRef('textInputRef')
const emit = defineEmits<Emits>()
const input = ref('')
const input = ref(initialValue)
const isOpened = defineModel<boolean>({
required: true
Expand All @@ -69,9 +71,17 @@
}
// Reset form when dialog is opened
watch(isOpened, (value) => {
if (value) {
input.value = ''
watch(isOpened, (opened) => {
if (opened) {
// Reset input field to initial value
if (input.value !== initialValue) {
input.value = initialValue
}
// Select text in input field if it's not empty
if (input.value !== '') {
textInputRef.value?.$el.select()
}
}
})
</script>
Expand Down
Loading

0 comments on commit d56579a

Please sign in to comment.