Skip to content

Commit

Permalink
Merge branch 'fabians/fe2-automate-integration' of github.com:speckle…
Browse files Browse the repository at this point in the history
…systems/speckle-server into fabians/fe2-automate-integration
  • Loading branch information
gjedlicska committed Mar 26, 2024
2 parents 0614deb + ba95914 commit 918c5ac
Show file tree
Hide file tree
Showing 91 changed files with 1,770 additions and 929 deletions.
203 changes: 7 additions & 196 deletions packages/frontend-2/components/common/EditableTitleDescription.vue
Original file line number Diff line number Diff line change
@@ -1,203 +1,14 @@
<template>
<div>
<!-- Editable Title -->
<div class="flex group">
<label class="max-w-full overflow-hidden">
<div class="sr-only">Edit title</div>
<div
:class="titleInputClasses"
class="grow-textarea"
:data-replicated-value="title"
>
<textarea
v-model="title"
maxlength="512"
:class="titleInputClasses"
placeholder="Please enter a valid title"
rows="1"
spellcheck="false"
:disabled="isDisabled"
:cols="title && title.length < 20 ? title.length : undefined"
data-type="title"
@keydown="onInputKeydown"
@blur="onBlur('title')"
@input="onTitleInput"
/>
</div>
</label>
<PencilIcon
v-if="canEdit"
class="shrink-0 ml-2 mt-3 w-4 h-4 opacity-0 group-hover:opacity-100 transition text-foreground-2"
/>
</div>

<!-- Editable Description -->
<div class="flex gap-x-2 group">
<label>
<div class="sr-only">Edit description</div>
<div
class="grow-textarea"
:data-replicated-value="description"
:class="descriptionInputClasses"
>
<textarea
v-model="description"
:class="[
...descriptionInputClasses,
description ? 'focus:min-w-0' : 'min-w-[260px]'
]"
:placeholder="description ? undefined : 'Click here to add a description.'"
:disabled="isDisabled"
rows="1"
spellcheck="false"
maxlength="1000"
:cols="
description && description?.length < 20 ? description.length : undefined
"
data-type="description"
@keydown="onInputKeydown"
@blur="onBlur('description')"
@input="onDescriptionInput"
/>
</div>
</label>
<div class="shrink-0 ml-2 mt-1 text-foreground-2">
<PencilIcon
v-if="canEdit"
class="w-4 h-4 opacity-0 group-hover:opacity-100 transition text-foreground-2"
/>
</div>
</div>
<CommonEditableTitle v-model="title" :disabled="disabled" />
<CommonEditableDescription v-model="description" :disabled="disabled" />
</div>
</template>

<script setup lang="ts">
import { PencilIcon } from '@heroicons/vue/20/solid'
import { debounce } from 'lodash-es'
const props = defineProps({
title: String,
description: String,
canEdit: Boolean,
isDisabled: Boolean
})
const emit = defineEmits(['update:title', 'update:description'])
const title = ref(props.title)
const description = ref(props.description)
const lastTitleValue = ref(props.title)
const lastDescriptionValue = ref(props.description)
const titleDebounceSaved = ref(false)
const descriptionDebounceSaved = ref(false)
const emitTitle = () => {
lastTitleValue.value = title.value
titleDebounceSaved.value = true
emit('update:title', title.value)
}
const emitDescription = () => {
lastDescriptionValue.value = description.value
descriptionDebounceSaved.value = true
emit('update:description', description.value)
}
const debouncedEmitTitle = debounce(emitTitle, 2000)
const debouncedEmitDescription = debounce(emitDescription, 2000)
const titleInputClasses = computed(() => [
'h3 tracking-tight border-0 border-b-2 transition focus:border-outline-3 max-w-full',
'p-0 pb-1 bg-transparent border-transparent focus:outline-none focus:ring-0'
])
const descriptionInputClasses = computed(() => [
'normal placeholder:text-foreground-2 text-foreground-2 focus:text-foreground',
'border-0 border-b-2 focus:border-outline-3',
'p-0 bg-transparent border-transparent focus:outline-none focus:ring-0'
])
defineProps<{
disabled?: boolean
}>()
const onInputKeydown = (e: KeyboardEvent) => {
if (e.target instanceof HTMLElement) {
if (e.target.dataset.type === 'title' && e.code === 'Enter') {
e.preventDefault()
e.target.blur()
}
}
}
const onBlur = (inputType: string) => {
debouncedEmitTitle.cancel()
debouncedEmitDescription.cancel()
if (inputType === 'title' && !titleDebounceSaved.value) {
if (lastTitleValue.value !== title.value) {
lastTitleValue.value = title.value
emitTitle()
}
} else if (inputType === 'description' && !descriptionDebounceSaved.value) {
if (lastDescriptionValue.value !== description.value) {
lastDescriptionValue.value = description.value
emitDescription()
}
}
}
const onTitleInput = () => {
titleDebounceSaved.value = false
debouncedEmitTitle()
}
const onDescriptionInput = () => {
descriptionDebounceSaved.value = false
debouncedEmitDescription()
}
watch(
() => props.title,
(newVal) => {
title.value = newVal
}
)
watch(
() => props.description,
(newVal) => {
description.value = newVal
}
)
const description = defineModel<string>('description', { required: true })
const title = defineModel<string>('title', { required: true })
</script>

<style scoped>
/** more info: https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
.grow-textarea {
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
display: grid;
}
.grow-textarea::after {
/* Note the weird space! Needed to preventy jumpy behavior */
content: attr(data-replicated-value) ' ';
/* This is how textarea text behaves */
white-space: pre-wrap;
/* Hidden from view, clicks, and screen readers */
visibility: hidden;
}
.grow-textarea > textarea {
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
resize: none;
/* Firefox shows scrollbar on growth, you can hide like this. */
overflow: hidden;
}
.grow-textarea > textarea,
.grow-textarea::after {
/* Place on top of each other - has to have the same styling as the textarea! */
grid-area: 1 / 1 / 2 / 2;
}
</style>
64 changes: 64 additions & 0 deletions packages/frontend-2/components/common/editable/Description.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div class="flex gap-x-2 group">
<label>
<div class="sr-only">Edit description</div>
<div
class="grow-textarea"
:data-replicated-value="visibleDescription"
:class="descriptionInputClasses"
>
<textarea
name="Description"
:class="[
...descriptionInputClasses,
visibleDescription ? 'focus:min-w-0' : 'min-w-[260px]'
]"
:placeholder="
visibleDescription ? undefined : 'Click here to add a description.'
"
:disabled="disabled"
rows="1"
spellcheck="false"
maxlength="1000"
:cols="
visibleDescription && visibleDescription?.length < 20
? visibleDescription.length
: undefined
"
data-type="description"
:value="visibleDescription"
v-on="on"
/>
</div>
</label>
<div class="shrink-0 ml-2 mt-1 text-foreground-2">
<PencilIcon
v-if="!disabled"
class="w-4 h-4 opacity-0 group-hover:opacity-100 transition text-foreground-2"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { PencilIcon } from '@heroicons/vue/20/solid'
import { useDebouncedTextInput } from '@speckle/ui-components'
defineProps<{
disabled?: boolean
}>()
const description = defineModel<string>({ required: true })
const { on, bind } = useDebouncedTextInput({
model: description,
submitOnEnter: false,
debouncedBy: 2000,
isBasicHtmlInput: true
})
const visibleDescription = computed(() => bind.modelValue.value)
const descriptionInputClasses = computed(() => [
'normal placeholder:text-foreground-2 text-foreground-2 focus:text-foreground',
'border-0 border-b-2 focus:border-outline-3',
'p-0 bg-transparent border-transparent focus:outline-none focus:ring-0'
])
</script>
79 changes: 79 additions & 0 deletions packages/frontend-2/components/common/editable/Title.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<div class="flex group">
<label class="max-w-full overflow-hidden">
<div class="sr-only">Edit title</div>
<div
:class="titleInputClasses"
class="grow-textarea"
:data-replicated-value="visibleTitle"
>
<textarea
name="Title"
maxlength="512"
:class="titleInputClasses"
placeholder="Please enter a valid title"
rows="1"
spellcheck="false"
:disabled="disabled"
:cols="
visibleTitle && visibleTitle.length < 20 ? visibleTitle.length : undefined
"
data-type="title"
:value="visibleTitle"
v-on="on"
/>
</div>
</label>
<PencilIcon v-if="!disabled" :class="pencilClasses" />
</div>
</template>
<script setup lang="ts">
import { PencilIcon } from '@heroicons/vue/20/solid'
import { useDebouncedTextInput } from '@speckle/ui-components'
const props = defineProps<{
disabled?: boolean
customClasses?: {
input?: string
pencil?: string
}
}>()
const title = defineModel<string>({ required: true })
const { on, bind } = useDebouncedTextInput({
model: title,
debouncedBy: 2000,
isBasicHtmlInput: true,
submitOnEnter: true
})
const visibleTitle = computed(() => bind.modelValue.value)
const titleInputClasses = computed(() => {
const classParts = [
'border-0 border-b-2 transition focus:border-outline-3 max-w-full',
'p-0 pb-1 bg-transparent border-transparent focus:outline-none focus:ring-0'
]
if (props.customClasses?.input) {
classParts.push(props.customClasses.input)
} else {
classParts.push('h3 tracking-tight')
}
return classParts.join(' ')
})
const pencilClasses = computed(() => {
const classParts = [
'shrink-0 opacity-0 group-hover:opacity-100 transition text-foreground-2'
]
if (props.customClasses?.pencil) {
classParts.push(props.customClasses.pencil)
} else {
classParts.push('ml-2 mt-3 w-4 h-4')
}
return classParts.join(' ')
})
</script>
4 changes: 4 additions & 0 deletions packages/frontend-2/components/common/model/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
:multiple="multiple"
:disabled="!items.length"
:allow-unset="allowUnset"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #nothing-selected>
Expand Down Expand Up @@ -113,6 +115,8 @@ const { result, onResult, fetchMore } = useQuery(
})
)
const buttonId = useId()
const labelId = useId()
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
:rules="[isItemSelected]"
show-label
:items="applicationScopes"
:label-id="badgesLabelId"
:button-id="badgesButtonId"
by="id"
/>
<FormTextInput
Expand Down Expand Up @@ -95,6 +97,8 @@ const { mutate: editApplication } = useMutation(editApplicationMutation)
const { triggerNotification } = useGlobalToast()
const { handleSubmit, resetForm } = useForm<ApplicationFormValues>()
const badgesLabelId = useId()
const badgesButtonId = useId()
const name = ref('')
const scopes = ref<typeof applicationScopes.value>([])
const redirectUrl = ref('')
Expand Down
Loading

0 comments on commit 918c5ac

Please sign in to comment.