Pre Specklecon polish - Various tasks (#1849)

* “Selection info” panel should remember the Fold/Unfold state of it

* Remove old naming terminology strings

* Rename “Share” To “Copy Link” - Difficult to find the model ID for a new model before data is uploaded

* Add & Rearrange new Viewer Buttons WIP

* Small enhancements to the registration form

* {mobile } The extra “new model” functionality in Model tree/list view just falls apart

* {mobile } Shorten the long Webhook buttons

* Revert "“Selection info” panel should remember the Fold/Unfold state of it"

This reverts commit d1b94036a596c3c995e65eae32ebe2f63580ea66.

* Revert Player controls default

* Responsive changes to Viewer

* Responsive Comments

* Responsive Viewer

* Small fix

* Small fix

* Design improvements

* Styling updates

* style fixes

* Profile pop-up stays opened after selecting “Server Management”

* Profile pop-up stays opened after selecting “Server Management”

* Change the Project Settings Icon to a gear

* Misleading text while uploading ifc/obj file

* Amends from polish list

* Viewer Buttons WIP

* Updates buttons in Viewer

* No backdrop blur

* Style updates

* New Colouring Icon

* New useBreakpoints

* Latest fixes pre PR

* Updates from PR Comments
This commit is contained in:
andrewwallacespeckle
2023-11-02 11:56:16 +00:00
committed by GitHub
parent f3d6484d97
commit cde5dd355c
37 changed files with 552 additions and 262 deletions
@@ -6,7 +6,7 @@
type="text"
name="name"
label="Full Name"
placeholder="John Doe"
placeholder="My Name"
size="xl"
:rules="nameRules"
:custom-icon="UserIcon"
@@ -34,15 +34,14 @@
:rules="passwordRules"
show-label
:disabled="loading"
@focusin="pwdFocused = true"
@focusout="pwdFocused = false"
@focus="pwdFocused = true"
@blur="pwdFocused = false"
/>
</div>
<AuthPasswordChecks
:password="password"
:class="`mt-2 overflow-hidden ${pwdFocused ? 'h-8' : 'h-0'} transition-[height]`"
/>
<FormButton submit full-width class="mt-4" :disabled="loading">Sign up</FormButton>
<div
class="mt-3 text-xs flex items-center justify-center text-foreground-2 space-x-2"
>
@@ -56,6 +55,7 @@
label="Opt in for exclusive Speckle news and tips"
/>
</div>
<FormButton submit full-width class="mt-4" :disabled="loading">Sign up</FormButton>
<div
v-if="serverInfo.termsOfService"
class="mt-2 text-xs text-foreground-2 text-center linkify-tos"
@@ -0,0 +1,14 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17 2C17.7652 1.99996 18.5015 2.29233 19.0583 2.81728C19.615 3.34224 19.9501 4.06011 19.995 4.824L20 5C20.7956 5 21.5587 5.31607 22.1213 5.87868C22.6839 6.44129 23 7.20435 23 8C23.0001 9.55238 22.3984 11.0444 21.3215 12.1625C20.2446 13.2806 18.7763 13.9378 17.225 13.996L17 14H13L13.15 14.005C13.6262 14.0408 14.0738 14.2458 14.412 14.5829C14.7502 14.92 14.9567 15.3669 14.994 15.843L15 16V20C15.0002 20.5046 14.8096 20.9906 14.4665 21.3605C14.1234 21.7305 13.6532 21.9572 13.15 21.995L13 22H11C10.4954 22.0002 10.0094 21.8096 9.63945 21.4665C9.26947 21.1234 9.04284 20.6532 9.005 20.15L9 20V16C8.99984 15.4954 9.19041 15.0094 9.5335 14.6395C9.87659 14.2695 10.3468 14.0428 10.85 14.005L11 14V13C11 12.7551 11.09 12.5187 11.2527 12.3356C11.4155 12.1526 11.6397 12.0357 11.883 12.007L12 12H17C18.0609 12 19.0783 11.5786 19.8284 10.8284C20.5786 10.0783 21 9.06087 21 8C21 7.75507 20.91 7.51866 20.7473 7.33563C20.5845 7.15259 20.3603 7.03566 20.117 7.007L20 7L19.995 7.176C19.9519 7.90959 19.6411 8.60186 19.1215 9.12148C18.6019 9.6411 17.9096 9.95193 17.176 9.995L17 10H7C6.23479 10 5.49849 9.70767 4.94174 9.18272C4.38499 8.65776 4.04989 7.93989 4.005 7.176L4 7V5C3.99996 4.23479 4.29233 3.49849 4.81728 2.94174C5.34224 2.38499 6.06011 2.04989 6.824 2.005L7 2H17Z"
fill="currentColor"
/>
</svg>
</template>
@@ -0,0 +1,31 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 5C5 4.46957 5.21071 3.96086 5.58579 3.58579C5.96086 3.21071 6.46957 3 7 3H17C17.5304 3 18.0391 3.21071 18.4142 3.58579C18.7893 3.96086 19 4.46957 19 5V7C19 7.53043 18.7893 8.03914 18.4142 8.41421C18.0391 8.78929 17.5304 9 17 9H7C6.46957 9 5.96086 8.78929 5.58579 8.41421C5.21071 8.03914 5 7.53043 5 7V5Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19 6H20C20.5304 6 21.0391 6.21071 21.4142 6.58579C21.7893 6.96086 22 7.46957 22 8C22 9.32608 21.4732 10.5979 20.5355 11.5355C19.5979 12.4732 18.3261 13 17 13H12V15"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M10 16C10 15.7348 10.1054 15.4804 10.2929 15.2929C10.4804 15.1054 10.7348 15 11 15H13C13.2652 15 13.5196 15.1054 13.7071 15.2929C13.8946 15.4804 14 15.7348 14 16V20C14 20.2652 13.8946 20.5196 13.7071 20.7071C13.5196 20.8946 13.2652 21 13 21H11C10.7348 21 10.4804 20.8946 10.2929 20.7071C10.1054 20.5196 10 20.2652 10 20V16Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
@@ -0,0 +1,59 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.8438 11.1563L21.9062 7.21875V14.2187L17.9062 18.2188L17.8438 11.1563Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5.19123 15.2186L2.15624 18.2188L2.09375 11.1563L6.15626 7.21875V11.7187"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.9062 7.71875L18.4062 3.21875H11.9062L7.90625 7.71875H14.9062Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M13.9062 11.7188H5.40625V20.7188H13.9062V11.7188Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11.6562 8.20312V10.9687"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M16.9062 5.46875L20.1563 5.46875L20.1563 8.71875"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.6562 14.4531L17.0313 14.4686"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
@@ -0,0 +1,24 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 16H16V20"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M19.458 11.042C20.318 8.67599 20.18 6.46199 18.858 5.14199C16.586 2.86799 11.673 4.09699 7.88503 7.88499C4.09703 11.673 2.86803 16.586 5.14103 18.859C7.36803 21.085 12.128 19.952 15.881 16.344"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
@@ -0,0 +1,45 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.5 13H18.5L21.5 22H2.5L5.5 13Z"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M2 7.5H9"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6.5 5L9 7.5L6.5 10"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M22 7.5H15"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M17.5 10L15 7.5L17.5 5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
@@ -0,0 +1,38 @@
<template>
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M18.5 8.79167L12 12.5833M18.5 8.79167V15.2917L12 19.0833M18.5 8.79167L12 5L5.5 8.79167M12 12.5833L5.5 8.79167M12 12.5833V19.0833M12 19.0833L5.5 15.2917V8.79167"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5.5 15.2917L1.5 17.6251"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M18.5 15.2957L22.5 17.629"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12 5V1"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
@@ -49,7 +49,7 @@
active ? 'bg-foundation-focus' : '',
'flex items-center justify-between px-2 py-3 text-sm text-foreground cursor-pointer transition'
]"
to="/server-management"
@click="goToServerManagement()"
>
Server Management
<Cog6ToothIcon class="w-5 h-5 mr-2" />
@@ -167,6 +167,10 @@ const goToConnectors = () => {
router.push('/downloads')
}
const goToServerManagement = () => {
router.push('/server-management')
}
const loginUrl = computed(() =>
router.resolve({
path: loginRoute,
@@ -8,7 +8,7 @@
].includes(upload.convertedStatus)
"
>
<span>{{ isSelfImport ? 'Importing' : 'Importing new version' }}</span>
<span>{{ isSelfImport ? 'Importing' : 'Uploading new version' }}</span>
<CommonLoadingBar loading class="max-w-[100px]" />
</template>
<template
@@ -79,7 +79,7 @@ const actionsItems = computed<LayoutMenuItem[][]>(() => [
}
],
[
{ title: 'Share', id: ActionTypes.Share },
{ title: 'Copy Link', id: ActionTypes.Share },
{ title: 'Copy ID', id: ActionTypes.CopyId }
],
[
@@ -27,7 +27,7 @@
:disabled="anyMutationsLoading"
/>
</div>
<div class="space-x-2">
<div class="flex gap-2">
<FormButton submit :disabled="anyMutationsLoading">Save</FormButton>
<FormButton outlined color="danger" @click="showNewModelCard = false">
Cancel
@@ -4,7 +4,7 @@
<div class="flex items-center">
<div class="flex items-center justify-between w-full">
<div class="flex items-center gap-0.5 flex-grow">
<UsersIcon class="h-5 w-5" />
<Cog6ToothIcon class="h-5 w-5" />
<span class="text-xs">Settings</span>
</div>
<div class="text-xs">{{ project.role?.split(':').reverse()[0] }}</div>
@@ -27,7 +27,7 @@
</ProjectPageStatsBlock>
</template>
<script setup lang="ts">
import { UsersIcon } from '@heroicons/vue/20/solid'
import { Cog6ToothIcon } from '@heroicons/vue/20/solid'
import { graphql } from '~~/lib/common/generated/gql'
import { ProjectPageStatsBlockTeamFragment } from '~~/lib/common/generated/gql/graphql'
@@ -91,8 +91,8 @@ const isExpanded = ref(false)
const notificationTypeMapping = ref({
activityDigest: 'Weekly activity digest',
mentionedInComment: 'Mentioned in comment',
newStreamAccessRequest: 'Stream access request',
streamAccessRequestApproved: 'Stream access request approved'
newStreamAccessRequest: 'Project access request',
streamAccessRequestApproved: 'Project access request approved'
} as Record<string, string>)
const localPreferences = ref({} as NotificationPreferences)
@@ -40,6 +40,8 @@
<!-- Standard viewer controls -->
<ViewerControlsButtonGroup>
<!-- Views -->
<ViewerViewsMenu v-tippy="'Views'" />
<!-- Zoom extents -->
<ViewerControlsButtonToggle
v-tippy="zoomExtentsShortcut"
@@ -49,6 +51,10 @@
<ArrowsPointingOutIcon class="h-5 w-5" />
</ViewerControlsButtonToggle>
<!-- Sun and lights -->
<ViewerSunMenu v-tippy="'Light Controls'" />
</ViewerControlsButtonGroup>
<ViewerControlsButtonGroup>
<!-- Projection type -->
<!-- TODO (Question for fabs): How to persist state between page navigation? e.g., swap to iso mode, move out, move back, iso mode is still on in viewer but not in ui -->
<ViewerControlsButtonToggle
@@ -73,22 +79,16 @@
<ScissorsIcon class="h-5 w-5" />
</ViewerControlsButtonToggle>
<!-- Sun and lights -->
<ViewerSunMenu v-tippy="'Light Controls'" />
<!-- Explosion -->
<ViewerExplodeMenu v-tippy="'Explode'" />
<!-- Views -->
<ViewerViewsMenu v-tippy="'Views'" />
<!-- Settings -->
<ViewerSettingsMenu />
</ViewerControlsButtonGroup>
</div>
<div
ref="scrollableControlsContainer"
:class="`simple-scrollbar absolute z-10 mx-14 mt-[4rem] mb-4 max-h-[calc(100vh-5.5rem)] w-72 overflow-y-auto px-[2px] py-[2px] transition ${
:class="`simple-scrollbar absolute z-10 mx-14 mt-[4rem] mb-4 max-h-[calc(100vh-5.5rem)] w-64 sm:w-72 overflow-y-auto px-[2px] py-[2px] transition ${
activeControl !== 'none'
? 'translate-x-0 opacity-100'
: '-translate-x-[100%] opacity-0'
@@ -155,6 +155,9 @@ import {
useCameraUtilities,
useSectionBoxUtilities
} from '~~/lib/viewer/composables/ui'
import { useBreakpoints } from '@vueuse/core'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
import {
onKeyboardShortcut,
ModifierKeys,
@@ -176,10 +179,13 @@ const { resourceItems } = useInjectedViewerLoadedResources()
const { toggleSectionBox, isSectionBoxEnabled } = useSectionBoxUtilities()
const breakpoints = useBreakpoints(TailwindBreakpoints)
type ActiveControl = 'none' | 'models' | 'explorer' | 'filters' | 'discussions'
const openAddModel = ref(false)
const activeControl = ref<ActiveControl>('models')
const scrollableControlsContainer = ref(null as Nullable<HTMLDivElement>)
const {
diff: { enabled }
@@ -195,7 +201,7 @@ const discussionsShortcut = ref(
`Discussions (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 't'])})`
)
const zoomExtentsShortcut = ref(
`Zoom Extents (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'Space'])})`
`Fit to screen (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'Space'])})`
)
const projectionShortcut = ref(
`Projection (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'p'])})`
@@ -204,6 +210,8 @@ const sectionBoxShortcut = ref(
`Section Box (${getKeyboardShortcutTitle([ModifierKeys.AltOrOpt, 'b'])})`
)
const isSmallerOrEqualSM = computed(() => breakpoints.smallerOrEqual('sm').value)
const toggleActiveControl = (control: ActiveControl) =>
activeControl.value === control
? (activeControl.value = 'none')
@@ -265,4 +273,8 @@ const scrollControlsToBottom = () => {
// if (scrollableControlsContainer.value)
// scrollToBottom(scrollableControlsContainer.value)
}
watch(isSmallerOrEqualSM, (newVal) => {
activeControl.value = newVal ? 'none' : 'models'
})
</script>
@@ -32,122 +32,128 @@
<div
v-if="isExpanded"
ref="threadContainer"
class="fixed z-50 pointer-events-auto"
class="thread-container fixed mb-16 bottom-0 right-0 sm:bottom-auto sm:right-auto w-screen sm:w-80 z-50 pointer-events-auto"
:style="threadStyle"
>
<div
ref="handle"
class="p-1.5 cursor-move rounded-lg group hover:bg-blue-500/50 transition"
:class="{ 'is-dragging bg-blue-500/50': isDragging }"
>
<ViewerCommentsPortalOrDiv to="mobileComments">
<div
:class="[
'bg-white dark:bg-neutral-800 backdrop-blur-sm shadow-md cursor-auto rounded-lg',
'group-hover:bg-foundation dark:group-hover:bg-neutral-800 group-[.is-dragging]:bg-foundation dark:group-[.is-dragging]:bg-neutral-800'
]"
ref="handle"
class="sm:p-1.5 cursor-move sm:rounded-lg group hover:sm:bg-blue-500/50 transition h-full transition-all duration-200"
:class="{ 'is-dragging bg-blue-500/50': isDragging }"
>
<div class="relative w-80 flex pt-3">
<div class="flex-grow flex items-center">
<FormButton
v-tippy="'Previous'"
size="sm"
:icon-left="ChevronLeftIcon"
text
hide-text
@click="emit('prev', props.modelValue)"
></FormButton>
<FormButton
v-tippy="'Next'"
size="sm"
:icon-left="ChevronRightIcon"
text
hide-text
@click="emit('next', props.modelValue)"
></FormButton>
<div class="flex-grow"></div>
<FormButton
v-show="isDragged"
v-tippy="'Pop In'"
size="sm"
:icon-left="ArrowTopRightOnSquareIcon"
text
hide-text
class="rotate-180"
@click="isDragged = false"
></FormButton>
</div>
<div>
<FormButton
v-tippy="modelValue.archived ? 'Unresolve' : 'Resolve'"
size="sm"
:icon-left="
modelValue.archived ? CheckCircleIcon : CheckCircleIconOutlined
"
text
hide-text
:color="modelValue.archived ? 'default' : 'default'"
:disabled="!canArchiveOrUnarchive"
@click="toggleCommentResolvedStatus()"
></FormButton>
<FormButton
v-tippy="'Copy link'"
size="sm"
:icon-left="LinkIcon"
text
hide-text
@click="onCopyLink"
></FormButton>
<FormButton
size="sm"
:icon-left="XMarkIcon"
text
hide-text
@click="changeExpanded(false)"
></FormButton>
</div>
</div>
<div class="relative w-80 flex flex-col">
<div
:class="[
'bg-foundation sm:bg-white/70 dark:sm:bg-neutral-800/70 sm:backdrop-blur flex flex-col overflow-hidden sm:shadow-md cursor-auto sm:rounded-lg h-full transition-all duration-200',
'group-hover:bg-foundation dark:group-hover:bg-neutral-800 group-[.is-dragging]:bg-foundation dark:group-[.is-dragging]:bg-neutral-800'
]"
>
<div
ref="commentsContainer"
class="max-h-[500px] overflow-y-auto simple-scrollbar flex flex-col space-y-1 pr-1"
class="relative w-full sm:w-80 flex py-2 pl-3 pr-2 sm:px-2 bg-foundation-2"
>
<div
v-if="!isThreadResourceLoaded"
class="pl-3 pr-1 py-1 mt-2 flex items-center justify-between text-xs text-primary bg-primary-muted"
>
<span>Conversation started in a different version.</span>
<div class="flex-grow flex items-center">
<span class="sm:hidden text-primary text-sm font-medium">
Discussions
</span>
<FormButton
v-tippy="'Load thread context'"
size="xs"
v-tippy="'Previous'"
size="sm"
:icon-left="ChevronLeftIcon"
text
@click="onLoadThreadContext"
>
<ArrowDownCircleIcon class="w-5 h-5" />
</FormButton>
hide-text
@click="emit('prev', modelValue)"
></FormButton>
<FormButton
v-tippy="'Next'"
size="sm"
:icon-left="ChevronRightIcon"
text
hide-text
@click="emit('next', modelValue)"
></FormButton>
<div class="flex-grow"></div>
<FormButton
v-show="isDragged"
v-tippy="'Pop In'"
size="sm"
:icon-left="ArrowTopRightOnSquareIcon"
text
hide-text
class="rotate-180"
@click="isDragged = false"
></FormButton>
</div>
<div>
<FormButton
v-tippy="modelValue.archived ? 'Unresolve' : 'Resolve'"
size="sm"
:icon-left="
modelValue.archived ? CheckCircleIcon : CheckCircleIconOutlined
"
text
hide-text
:color="modelValue.archived ? 'default' : 'default'"
:disabled="!canArchiveOrUnarchive"
@click="toggleCommentResolvedStatus()"
></FormButton>
<FormButton
v-tippy="'Copy link'"
size="sm"
:icon-left="LinkIcon"
text
hide-text
@click="onCopyLink"
></FormButton>
<FormButton
size="sm"
:icon-left="XMarkIcon"
text
hide-text
@click="changeExpanded(false)"
></FormButton>
</div>
<ViewerAnchoredPointThreadComment
v-for="comment in comments"
:key="comment.id"
:comment="comment"
:project-id="projectId"
@mounted="onCommentMounted"
/>
</div>
<div
v-if="isTypingMessage"
class="bg-foundation rounded-full w-full p-2 caption mt-2"
>
{{ isTypingMessage }}
<div class="relative w-full sm:w-80 flex flex-col flex-1 justify-between">
<div
ref="commentsContainer"
class="max-h-[calc(50vh)] sm:max-h-[300px] 2xl:max-h-[500px] pb-16 overflow-y-auto simple-scrollbar flex flex-col space-y-1 pr-1"
>
<div
v-if="!isThreadResourceLoaded"
class="pl-3 pr-1 py-1 mt-2 flex items-center justify-between text-xs text-primary bg-primary-muted"
>
<span>Conversation started in a different version.</span>
<FormButton
v-tippy="'Load thread context'"
size="xs"
text
@click="onLoadThreadContext"
>
<ArrowDownCircleIcon class="w-5 h-5" />
</FormButton>
</div>
<ViewerAnchoredPointThreadComment
v-for="comment in comments"
:key="comment.id"
:comment="comment"
:project-id="projectId"
@mounted="onCommentMounted"
/>
</div>
<div
v-if="isTypingMessage"
class="bg-foundation rounded-full w-full p-2 caption mt-2"
>
{{ isTypingMessage }}
</div>
</div>
<ViewerAnchoredPointThreadNewReply
v-if="!modelValue.archived && canReply"
:model-value="modelValue"
class="mt-2"
@submit="onNewReply"
/>
</div>
</div>
</div>
</ViewerCommentsPortalOrDiv>
</div>
</div>
</template>
@@ -205,6 +211,7 @@ const props = defineProps<{
const threadId = computed(() => props.modelValue.id)
const { copy } = useClipboard()
const { activeUser } = useActiveUser()
const archiveComment = useArchiveComment()
const { triggerNotification } = useGlobalToast()
const {
@@ -474,3 +481,10 @@ onMounted(() => {
}
})
</script>
<style scoped>
@media (max-width: 640px) {
.thread-container {
transform: none !important;
}
}
</style>
@@ -1,16 +1,18 @@
<template>
<div class="flex flex-col items-center space-y-1">
<div class="flex flex-col items-center">
<!-- <div class="xxx-bg-foundation rounded-full caption space-x-2 p-1">
<span>{{ absoluteDate }}</span>
<span>{{ timeFromNow }}</span>
</div> -->
<div class="xxx-bg-foundation rounded-xl p-4 w-full relative">
<div class="xxx-bg-foundation sm:rounded-xl py-2 px-3 sm:p-4 w-full relative">
<div class="flex items-center space-x-1">
<UserAvatar :user="comment.author" class="mr-1" />
<span class="grow truncate text-sm font-medium">
<span class="grow truncate text-xs sm:text-sm font-bold">
{{ comment.author.name }}
</span>
<span class="text-xs truncate text-foreground-2">{{ timeFromNow }}</span>
<span class="text-xs truncate text-foreground-2 font-medium sm:font-bold">
{{ timeFromNow }}
</span>
<!-- Note: disabled as archiving comments is now equivalent to "resolving" them. -->
<!-- <div class="pl-2">
<CommonTextLink
@@ -22,7 +24,7 @@
</CommonTextLink>
</div> -->
</div>
<div class="truncate text-sm text-foreground flex flex-col mt-2">
<div class="truncate text-xs sm:text-sm text-foreground flex flex-col mt-2">
<CommonTiptapTextEditor
v-if="comment.text.doc"
:model-value="comment.text.doc"
@@ -7,7 +7,9 @@
size="sm"
@click="() => onAttachmentClick(attachment)"
>
<span class="truncate relative bottom-0.5">{{ attachment.fileName }}</span>
<span class="truncate relative bottom-0.5 text-xs">
{{ attachment.fileName }}
</span>
</CommonTextLink>
<LayoutDialog v-model:open="dialogOpen" max-width="lg">
<template v-if="dialogAttachment">
@@ -1,6 +1,17 @@
<!-- eslint-disable vuejs-accessibility/no-autofocus -->
<template>
<div class="relative">
<div
class="absolute bottom-0 left-0 bg-foundation pl-4 pr-3 py-2 sm:py-1.5 rounded-b flex items-center w-full"
>
<FormButton
:icon-left="PaperClipIcon"
hide-text
text
:disabled="loading"
size="sm"
class="-ml-2 sm:mr-2"
@click="trackAttachAndOpenFilePicker()"
/>
<ViewerCommentsEditor
ref="editor"
v-model="commentValue"
@@ -10,23 +21,15 @@
@keydown="onKeyDownHandler"
@submit="onSubmit"
/>
<div class="w-full flex justify-end pt-2 space-x-2 p-2">
<div class="flex space-x-2">
<FormButton
:icon-left="PaperClipIcon"
hide-text
text
:disabled="loading"
@click="trackAttachAndOpenFilePicker()"
/>
<FormButton
:icon-left="PaperAirplaneIcon"
hide-text
:disabled="loading"
@click="onSubmit"
/>
</div>
</div>
<FormButton
:icon-left="PaperAirplaneIcon"
hide-text
size="sm"
color="invert"
:disabled="loading"
class="absolute right-6 sm:right-6"
@click="onSubmit"
/>
</div>
</template>
<script setup lang="ts">
@@ -1,5 +1,6 @@
<template>
<ViewerLayoutPanel @close="$emit('close')">
<template #title>Discussions</template>
<template #actions>
<FormButton
text
@@ -10,10 +11,10 @@
Discussion Visibility Options
</FormButton>
</template>
<div class="flex flex-col px-1">
<div class="flex flex-col">
<div
v-show="showVisibilityOptions"
class="sticky top-10 px-0 py-2 flex flex-col justify-start text-left border-b-2 border-primary-muted bg-foundation"
class="sticky top-10 px-2 py-2 flex flex-col justify-start text-left border-b-2 border-primary-muted bg-foundation"
>
<div>
<FormButton
@@ -1,6 +1,6 @@
<!-- eslint-disable vuejs-accessibility/no-autofocus -->
<template>
<div class="flex flex-col w-80 max-h-40 overflow-y-auto simple-scrollbar">
<div class="flex flex-col w-full max-h-28 overflow-y-auto simple-scrollbar">
<FormFileUploadZone
ref="uploadZone"
v-slot="{ isDraggingFiles }"
@@ -13,7 +13,7 @@
<CommonTiptapTextEditor
v-model="doc"
:class="[
'dark:bg-foundation-2 bg-neutral-100 rounded-lg p-4 border',
'dark:bg-foundation-2 bg-neutral-100 rounded-lg p-2 pr-12 border text-sm',
isDraggingFiles ? 'border-success' : 'border-transparent'
]"
:autofocus="autofocus"
@@ -20,7 +20,7 @@
></FormButton>
</div>
<div class="flex items-center space-x-1">
<span class="grow truncate text-sm font-medium text-foreground-2">
<span class="grow truncate text-xs font-medium text-foreground-2">
{{ thread.author.name }}
<span v-if="threadAuthors.length !== 1">
& {{ thread.replyAuthors.totalCount }} others
@@ -31,7 +31,7 @@
{{ thread.rawText }}
</div>
<div
:class="`text-xs font-bold flex items-center space-x-2 ${
:class="`text-xs font-medium flex items-center space-x-2 ${
thread.replies.totalCount > 0 ? 'text-primary' : 'text-foreground-2'
} mb-1`"
>
@@ -0,0 +1,23 @@
<template>
<div>
<div v-if="!isMobile">
<slot></slot>
</div>
<Portal v-else :to="props.to">
<slot></slot>
</Portal>
</div>
</template>
<script setup lang="ts">
import { useBreakpoints } from '@vueuse/core'
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
const props = defineProps({
to: String
})
const breakpoints = useBreakpoints(TailwindBreakpoints)
const isMobile = computed(() => breakpoints.smallerOrEqual('sm').value)
</script>
@@ -3,7 +3,9 @@
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<!-- <ChevronUpDownIcon class="w-5 h-5 rotate-45" /> -->
<span :class="`${explodeFactor > 0.01 ? '' : 'grayscale'}`">💥</span>
<span :class="`${explodeFactor > 0.01 ? '' : 'grayscale'}`">
<IconExplode class="h-5 w-5" />
</span>
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
@@ -1,6 +1,8 @@
<template>
<div>
<ViewerLayoutPanel @close="$emit('close')">
<template #title>Scene Explorer</template>
<template #actions>
<FormButton
size="xs"
@@ -33,8 +33,8 @@
text
@click="toggleColors()"
>
<SparklesIconOutline v-if="!colors" class="w-3 h-3 text-primary" />
<SparklesIcon v-else class="w-3 h-3 text-primary" />
<IconColouringOutline v-if="!colors" class="w-3 h-3 text-primary" />
<IconColouring v-else class="w-3 h-3 text-primary" />
</FormButton>
</div>
</div>
@@ -88,8 +88,7 @@
</ViewerLayoutPanel>
</template>
<script setup lang="ts">
import { ChevronDownIcon, ChevronUpIcon, SparklesIcon } from '@heroicons/vue/24/solid'
import { SparklesIcon as SparklesIconOutline } from '@heroicons/vue/24/outline'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/solid'
import { PropertyInfo, StringPropertyInfo, NumericPropertyInfo } from '@speckle/viewer'
import { useFilterUtilities } from '~~/lib/viewer/composables/ui'
import { useMixpanel } from '~~/lib/core/composables/mp'
@@ -9,7 +9,7 @@
} ${isSelected ? 'border-primary bg-primary-muted' : 'border-transparent'}`"
@click="setSelection()"
>
<div class="flex space-x-2 items-center flex-shrink truncate">
<div class="flex space-x-2 items-center flex-shrink truncate text-sm">
<span
v-if="color"
class="w-3 h-3 rounded"
@@ -1,14 +1,28 @@
<template>
<div class="bg-foundation rounded-lg shadow">
<div class="bg-foundation sm:rounded-lg shadow">
<div
class="bg-foundation-2 sticky top-0 z-50 flex h-10 items-center justify-between rounded-t-lg px-2 shadow-md"
class="sticky top-0 z-50 flex flex-col bg-foundation sm:rounded-t-lg sm:shadow-md"
>
<div class="flex items-center w-full"><slot name="actions"></slot></div>
<div v-if="!hideClose">
<FormButton size="sm" text @click="$emit('close')">
<XMarkIcon class="h-3 w-3" />
<div v-if="!hideClose" class="absolute top-2 right-2 sm:right-0 z-10">
<FormButton size="sm" color="secondary" text @click="$emit('close')">
<XMarkIcon class="h-4 w-4 sm:h-3 sm:w-3 text-primary sm:text-foreground" />
</FormButton>
</div>
<div
v-if="$slots.title"
class="flex items-center h-10 px-3 sm:border-b border-foundation-2 bg-foundation rounded-t"
>
<div
class="flex items-center h-full w-full pr-8 font-semibold sm:font-bold text-sm text-primary"
>
<span class="truncate">
<slot name="title"></slot>
</span>
</div>
</div>
<div class="flex items-center h-8 sm:h-10 gap-2 px-2">
<slot name="actions"></slot>
</div>
</div>
<div>
<slot></slot>
@@ -1,5 +1,6 @@
<template>
<ViewerLayoutPanel @close="$emit('close')">
<template #title>Models</template>
<template #actions>
<FormButton
size="xs"
@@ -13,7 +14,6 @@
<FormButton
size="xs"
text
:color="showRemove ? 'default' : 'secondary'"
:icon-left="showRemove ? CheckIcon : MinusIcon"
:disabled="modelsAndVersionIds.length <= 1"
@click="showRemove = !showRemove"
@@ -12,9 +12,9 @@
label="Value"
full-width
size="lg"
:custom-icon="CubeIcon"
:custom-icon="LinkIcon"
:rules="[isRequired, isValidValue]"
placeholder="Comma-delimited object IDs or an URL to an object(-s)"
placeholder="Comma-delimited object IDs/URLs"
auto-focus
/>
<FormButton :icon-left="PlusIcon" size="lg" submit>Add</FormButton>
@@ -23,7 +23,7 @@
</template>
<script setup lang="ts">
import { RuleExpression, useForm } from 'vee-validate'
import { PlusIcon, CubeIcon } from '@heroicons/vue/24/solid'
import { PlusIcon, LinkIcon } from '@heroicons/vue/24/solid'
import { isRequired } from '~~/lib/common/helpers/validation'
import { isObjectId } from '~~/lib/common/helpers/resources'
import { useInjectedViewerLoadedResources } from '~~/lib/viewer/composables/setup'
@@ -8,14 +8,10 @@
>
<div class="mb-1 flex items-center">
<button
class="hover:bg-primary-muted hover:text-primary flex h-full min-w-0 items-center space-x-1 rounded"
class="flex h-full w-full p-2 items-center justify-between gap-4 rounded bg-foundation-2 hover:sm:bg-primary-muted hover:text-primary"
:class="unfold && 'text-primary'"
@click="unfold = !unfold"
>
<ChevronDownIcon
:class="`h-3 w-3 transition ${headerClasses} ${
!unfold ? '-rotate-90' : 'rotate-0'
}`"
/>
<div :class="`truncate text-xs font-bold ${headerClasses}`">
{{ title || headerAndSubheader.header }}
<span
@@ -24,9 +20,14 @@
{{ isModifiedQuery.isNew ? '(New)' : '(Old)' }}
</span>
</div>
<ChevronDownIcon
:class="`h-3 w-3 transition ${headerClasses} ${
!unfold ? '-rotate-90' : 'rotate-0'
}`"
/>
</button>
</div>
<div v-if="unfold" class="ml-1 space-y-1">
<div v-if="unfold" class="ml-1 space-y-1 p-2">
<div
v-for="(kvp, index) in [
...categorisedValuePairs.primitives,
@@ -45,7 +46,7 @@
>
{{ kvp.key }}
</div>
<div class="col-span-2 truncate text-xs" :title="(kvp.value as string)">
<div class="col-span-2 pl-1 truncate text-xs" :title="(kvp.value as string)">
<!-- NOTE: can't do kvp.value || 'null' because 0 || 'null' = 'null' -->
{{ kvp.value === null ? 'null' : kvp.value }}
</div>
@@ -1,50 +1,66 @@
<template>
<div
:class="`bg-foundation simple-scrollbar fixed top-[4rem] right-4 mb-4 max-h-[calc(100vh-5.5rem)] w-64 overflow-y-auto rounded-md shadow transition ${
objects.length !== 0
? 'translate-x-0 opacity-100'
: 'translate-x-[120%] opacity-0'
}`"
>
<ViewerLayoutPanel @close="trackAndClearSelection()">
<template #actions>
<button
class="hover:text-primary px-1 py-2 transition"
@click.stop="hideOrShowSelection"
>
<EyeIcon v-if="!isHidden" class="h-3 w-3" />
<EyeSlashIcon v-else class="h-3 w-3" />
</button>
<button
class="hover:text-primary px-1 py-2 transition"
@click.stop="isolateOrUnisolateSelection"
>
<FunnelIconOutline v-if="!isIsolated" class="h-3 w-3" />
<FunnelIcon v-else class="h-3 w-3" />
</button>
</template>
<div class="px-1 py-2">
<div class="space-y-2">
<ViewerSelectionObject
v-for="object in objectsLimited"
:key="(object.id as string)"
:object="object"
:unfold="false"
:root="true"
/>
</div>
<div v-if="itemCount <= objects.length" class="mb-2">
<FormButton size="xs" text full-width @click="itemCount += 10">
View More ({{ objects.length - itemCount }})
<ViewerCommentsPortalOrDiv v-if="objects.length !== 0" to="bottomPanel">
<div
:class="`sm:bg-foundation simple-scrollbar z-10 relative sm:fixed sm:top-16 sm:right-4 sm:top-[4rem] sm:right-4 sm:mb-4 sm:max-w-64 min-h-[4.75rem] max-h-[50vh] sm:max-h-[calc(100vh-5.5rem)] w-full sm:w-64 overflow-y-auto sm:rounded-md sm:shadow transition ${
objects.length !== 0
? 'translate-x-0 opacity-100'
: 'translate-x-[120%] opacity-0'
}`"
>
<ViewerLayoutPanel @close="trackAndClearSelection()">
<template #title>Selection Info</template>
<template #actions>
<FormButton
size="xs"
color="secondary"
class="opacity-80 hover:opacity-100"
@click.stop="hideOrShowSelection"
>
<div class="flex items-center gap-1">
<EyeIcon v-if="!isHidden" class="h-4 w-4" />
<EyeSlashIcon v-else class="h-4 w-4" />
Hide
</div>
</FormButton>
<FormButton
size="xs"
color="secondary"
class="hover:opacity-100"
:class="isIsolated ? 'text-primary opacity-100' : 'opacity-80'"
@click.stop="isolateOrUnisolateSelection"
>
<div class="flex items-center gap-1">
<FunnelIconOutline v-if="!isIsolated" class="h-4 w-4" />
<FunnelIcon v-else class="h-4 w-4" />
Isolate
</div>
</FormButton>
</template>
<div class="p-1 sm:py-2 sm:bg-white/90 dark:sm:bg-neutral-700/90">
<div class="space-y-2">
<ViewerSelectionObject
v-for="object in objectsLimited"
:key="(object.id as string)"
:object="object"
:unfold="false"
:root="true"
/>
</div>
<div v-if="itemCount <= objects.length" class="mb-2">
<FormButton size="xs" text full-width @click="itemCount += 10">
View More ({{ objects.length - itemCount }})
</FormButton>
</div>
<div
v-if="objects.length === 1"
class="hidden sm:block text-foreground-2 mt-2 px-2 text-xs"
>
Hold "shift" to select multiple objects
</div>
</div>
<div v-if="objects.length === 1" class="text-foreground-2 mt-2 px-2 text-xs">
Hold down "shift" to select multiple objects.
</div>
</div>
</ViewerLayoutPanel>
</div>
</ViewerLayoutPanel>
</div>
</ViewerCommentsPortalOrDiv>
</template>
<script setup lang="ts">
import { EyeIcon, EyeSlashIcon, FunnelIcon } from '@heroicons/vue/24/solid'
@@ -1,41 +1,15 @@
<template>
<Popover as="div" class="relative z-30">
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<CogIcon class="w-5 h-5" />
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<PopoverPanel
class="absolute translate-x-0 left-12 top-2 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
>
<div class="text-sm text-foreground-2 text-left">Local Settings</div>
<div class="flex items-center space-x-2 w-44">
<span class="text-xs text-left">
Prevent camera from going under the model
</span>
<FormButton
size="xs"
:outlined="!localViewerSettings.turntableMode"
@click="toggleTurntableMode()"
>
{{ localViewerSettings.turntableMode ? 'ON' : 'OFF' }}
</FormButton>
</div>
</PopoverPanel>
</Transition>
</Popover>
<ViewerControlsButtonToggle
v-tippy="'Free Orbit'"
flat
:active="localViewerSettings.turntableMode"
secondary
@click="toggleTurntableMode()"
>
<IconFreeOrbit class="h-5 w-5" />
</ViewerControlsButtonToggle>
</template>
<script setup lang="ts">
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { CogIcon } from '@heroicons/vue/24/outline'
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
import { useInjectedViewer } from '~~/lib/viewer/composables/setup'
type ViewerUserSettings = {
@@ -46,7 +20,7 @@ const localViewerSettings = useSynchronizedCookie<ViewerUserSettings>(
`localViewerSettings`,
{
default: () => {
return { turntableMode: true }
return { turntableMode: false }
}
}
)
@@ -2,7 +2,7 @@
<Menu as="div" class="relative z-30">
<MenuButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<VideoCameraIcon class="w-5 h-5" />
<IconViews class="w-5 h-5" />
</ViewerControlsButtonToggle>
</MenuButton>
<Transition
@@ -62,7 +62,6 @@
</template>
<script setup lang="ts">
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
import { VideoCameraIcon } from '@heroicons/vue/24/outline'
import { CanonicalView, SpeckleView } from '~~/../viewer/dist'
import { useMixpanel } from '~~/lib/core/composables/mp'
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
@@ -53,13 +53,21 @@
enter-from-class="opacity-0"
enter-active-class="transition duration-1000"
>
<ViewerSelectionSidebar v-show="tourState.showViewerControls" class="z-20" />
<div v-show="tourState.showViewerControls">
<ViewerSelectionSidebar class="z-20 hidden sm:block" />
</div>
</Transition>
<!-- Shows up when filters are applied for an easy return to normality -->
<ViewerGlobalFilterReset class="z-20" />
</ClientOnly>
</div>
</ViewerPostSetupWrapper>
<div
class="sm:hidden shadow-t fixed bottom-0 left-0 max-h-[65vh] w-screen z-50 transition-all duration-300 empty:-bottom-[65vh]"
>
<PortalTarget name="bottomPanel"></PortalTarget>
<PortalTarget name="mobileComments"></PortalTarget>
</div>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
@@ -7,7 +7,7 @@
name="Webhooks"
></HeaderNavLink>
</Portal>
<div class="flex items-center justify-between">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<h1 class="text-2xl font-bold">Webhooks</h1>
<div class="flex gap-2">
<FormButton
@@ -17,10 +17,10 @@
external
target="_blank"
>
Open Docs
Docs
</FormButton>
<FormButton :icon-left="PlusIcon" @click="openCreateWebhookDialog">
Create Webhook
Create
</FormButton>
</div>
</div>
+3 -2
View File
@@ -21,11 +21,12 @@ const config: Config = {
mono: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"
},
extend: {
// Standard Tailwind Shadows add shadows below the element, 'shadow-t" variant adds shadow to the top of the element.
boxShadow: {
t: '0 -1px 3px 0 rgb(0 0 0 / 10%), 0 -1px 2px -1px rgb(0 0 0 / 6%)'
t: '0 -10px 8px 0 rgb(0 0 0 / 8%), 0 -4px 3px -6px rgb(0 0 0 / 10%)'
},
backdropBlur: {
xs: '4px'
xs: '2px'
},
colors: {
foundation: {
@@ -40,6 +40,8 @@
v-bind="$attrs"
@change="$emit('change', { event: $event, value })"
@input="$emit('input', { event: $event, value })"
@focus="$emit('focus')"
@blur="$emit('blur')"
/>
<slot name="input-right">
<a
@@ -233,8 +235,8 @@ const emit = defineEmits<{
(e: 'change', val: { event?: Event; value: string }): void
(e: 'input', val: { event?: Event; value: string }): void
(e: 'clear'): void
(e: 'focusin'): void
(e: 'focusout'): void
(e: 'focus'): void
(e: 'blur'): void
}>()
const slots = useSlots()