refactor(fe): Icons and Buttons throughout viewer
refactor(fe): Icons and Buttons throughout viewer
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
<template>
|
||||
<button
|
||||
v-tippy="getTooltipProps(isIsolated ? 'Unisolate' : 'Isolate')"
|
||||
:aria-label="isIsolated ? 'Unisolate' : 'Isolate'"
|
||||
class="group-hover:opacity-100 rounded-md h-6 w-6 flex items-center justify-center"
|
||||
:class="buttonClasses"
|
||||
@click.stop="$emit('click', $event)"
|
||||
>
|
||||
<IconViewerUnisolate v-if="isIsolated" class="w-3.5 h-3.5" />
|
||||
<IconViewerIsolate v-else class="w-3.5 h-3.5" />
|
||||
</button>
|
||||
<div v-tippy="getTooltipProps(isIsolated ? 'Unisolate' : 'Isolate')">
|
||||
<FormButton
|
||||
color="subtle"
|
||||
size="sm"
|
||||
:icon-left="isIsolated ? FunnelX : Funnel"
|
||||
hide-text
|
||||
@click.stop="$emit('click', $event)"
|
||||
>
|
||||
{{ isIsolated ? 'Unisolate' : 'Isolate' }}
|
||||
</FormButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
import { FunnelX, Funnel } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{
|
||||
isIsolated: boolean
|
||||
forceVisible?: boolean
|
||||
}>()
|
||||
@@ -22,12 +25,4 @@ const { getTooltipProps } = useSmartTooltipDelay()
|
||||
defineEmits<{
|
||||
click: [event: Event]
|
||||
}>()
|
||||
|
||||
const buttonClasses = computed(() => {
|
||||
return {
|
||||
'opacity-100 hover:bg-highlight-1': props.isIsolated,
|
||||
'opacity-100 hover:bg-highlight-3': !props.isIsolated && props.forceVisible,
|
||||
'sm:opacity-0 hover:bg-highlight-3': !props.isIsolated && !props.forceVisible
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
<template>
|
||||
<button
|
||||
v-tippy="getTooltipProps(isHidden ? 'Show' : 'Hide')"
|
||||
:aria-label="isHidden ? 'Show' : 'Hide'"
|
||||
class="group-hover:opacity-100 hover:bg-highlight-3 rounded-md h-6 w-6 flex items-center justify-center"
|
||||
:class="buttonClasses"
|
||||
@click.stop="$emit('click', $event)"
|
||||
>
|
||||
<IconEyeClosed v-if="isHidden" class="w-4 h-4" />
|
||||
<IconEye v-else class="w-4 h-4" />
|
||||
</button>
|
||||
<div v-tippy="getTooltipProps(isHidden ? 'Show' : 'Hide')">
|
||||
<FormButton
|
||||
color="subtle"
|
||||
size="sm"
|
||||
:icon-left="isHidden ? EyeClosed : Eye"
|
||||
hide-text
|
||||
@click.stop="$emit('click', $event)"
|
||||
>
|
||||
{{ isHidden ? 'Show' : 'Hide' }}
|
||||
</FormButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
import { FormButton } from '@speckle/ui-components'
|
||||
import { Eye, EyeClosed } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{
|
||||
isHidden: boolean
|
||||
forceVisible?: boolean
|
||||
}>()
|
||||
@@ -22,11 +26,4 @@ defineEmits<{
|
||||
}>()
|
||||
|
||||
const { getTooltipProps } = useSmartTooltipDelay()
|
||||
|
||||
const buttonClasses = computed(() => {
|
||||
return {
|
||||
'opacity-100': props.isHidden || props.forceVisible,
|
||||
'sm:opacity-0': !props.isHidden && !props.forceVisible
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
class="bg-foundation-2 outline outline-2 outline-primary rounded-tr-full rounded-tl-full rounded-br-full w-8 h-8 -top-10 absolute flex justify-center items-center hover:shadow-md"
|
||||
@click="onThreadClick"
|
||||
>
|
||||
<PlusIcon
|
||||
<Plus
|
||||
:class="`w-5 h-5 text-primary ${
|
||||
modelValue.isExpanded ? 'rotate-45' : ''
|
||||
} transition`"
|
||||
@@ -27,7 +27,7 @@
|
||||
>
|
||||
Add Comment
|
||||
<button v-tippy="'Close'" @click="onThreadClick">
|
||||
<PlusIcon class="w-5 h-5 text-foreground-2 rotate-45" />
|
||||
<Plus class="w-4 h-4 text-foreground-2 rotate-45" />
|
||||
</button>
|
||||
</div>
|
||||
<FormFileUploadZone
|
||||
@@ -60,15 +60,14 @@
|
||||
/>
|
||||
<div class="w-full flex justify-between items-center p-1">
|
||||
<FormButton
|
||||
:icon-left="PaperClipIcon"
|
||||
:icon-left="Paperclip"
|
||||
hide-text
|
||||
:disabled="isPostingNewThread"
|
||||
color="subtle"
|
||||
class="!bg-foundation dark:!bg-foundation-2"
|
||||
@click="trackAttachAndOpenFilePicker()"
|
||||
/>
|
||||
<FormButton
|
||||
:icon-left="PaperAirplaneIcon"
|
||||
:icon-left="SendHorizonal"
|
||||
hide-text
|
||||
:loading="isPostingNewThread"
|
||||
@click="() => onSubmit()"
|
||||
@@ -82,7 +81,6 @@
|
||||
<div v-else></div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { PlusIcon, PaperAirplaneIcon, PaperClipIcon } from '@heroicons/vue/24/solid'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import { onKeyDown } from '@vueuse/core'
|
||||
import { useIsTypingUpdateEmitter } from '~~/lib/viewer/composables/commentBubbles'
|
||||
@@ -100,6 +98,7 @@ import { useServerFileUploadLimit } from '~~/lib/common/composables/serverInfo'
|
||||
import { UniqueFileTypeSpecifier } from '~~/lib/core/helpers/file'
|
||||
import { acceptedFileExtensions } from '@speckle/shared/blobs'
|
||||
import type { UploadableFileItem } from '@speckle/ui-components'
|
||||
import { Paperclip, SendHorizonal, Plus } from 'lucide-vue-next'
|
||||
|
||||
const { isEnabled: isEmbedEnabled } = useEmbed()
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<div class="flex-grow flex items-center gap-x-1">
|
||||
<div class="flex items-center gap-x-0.5">
|
||||
<FormButton
|
||||
:icon-left="ChevronLeftIcon"
|
||||
:icon-left="ChevronLeft"
|
||||
color="outline"
|
||||
hide-text
|
||||
size="sm"
|
||||
@@ -59,7 +59,7 @@
|
||||
@click="emit('prev', modelValue)"
|
||||
/>
|
||||
<FormButton
|
||||
:icon-left="ChevronRightIcon"
|
||||
:icon-left="ChevronRight"
|
||||
color="outline"
|
||||
hide-text
|
||||
size="sm"
|
||||
@@ -82,14 +82,15 @@
|
||||
hide-text
|
||||
size="sm"
|
||||
color="subtle"
|
||||
:icon-left="iconThreeDots"
|
||||
:class="showMenu ? '!bg-highlight-3' : ''"
|
||||
:icon-left="Ellipsis"
|
||||
@click="showMenu = !showMenu"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
</div>
|
||||
<FormButton
|
||||
v-tippy="modelValue.archived ? 'Unresolve' : 'Resolve'"
|
||||
:icon-left="IconCircleCheck"
|
||||
:icon-left="CircleCheck"
|
||||
hide-text
|
||||
:disabled="!canArchiveOrUnarchive"
|
||||
color="subtle"
|
||||
@@ -97,7 +98,7 @@
|
||||
@click="toggleCommentResolvedStatus()"
|
||||
/>
|
||||
<FormButton
|
||||
:icon-left="IconXMark"
|
||||
:icon-left="X"
|
||||
hide-text
|
||||
color="subtle"
|
||||
size="sm"
|
||||
@@ -176,11 +177,14 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ArrowLeftIcon,
|
||||
ArrowUpRightIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
CircleCheck,
|
||||
X,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Ellipsis
|
||||
} from 'lucide-vue-next'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import { onKeyDown, useClipboard, useDraggable, onClickOutside } from '@vueuse/core'
|
||||
@@ -203,7 +207,6 @@ import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useThreadUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { graphql } from '~/lib/common/generated/gql'
|
||||
import type { ConcreteComponent } from 'vue'
|
||||
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
|
||||
|
||||
enum ActionTypes {
|
||||
@@ -259,7 +262,6 @@ const showMenu = ref(false)
|
||||
const commentsContainer = ref(null as Nullable<HTMLElement>)
|
||||
const threadContainer = ref(null as Nullable<HTMLElement>)
|
||||
const threadActivator = ref(null as Nullable<HTMLElement>)
|
||||
const iconThreeDots = resolveComponent('IconThreeDots') as ConcreteComponent
|
||||
|
||||
onClickOutside(threadContainer, (event) => {
|
||||
const viewerElement = document.getElementById('viewer')
|
||||
@@ -276,8 +278,6 @@ onClickOutside(threadContainer, (event) => {
|
||||
|
||||
const handle = ref(null as Nullable<HTMLElement>)
|
||||
const justCreatedReply = ref(false)
|
||||
const IconXMark = resolveComponent('IconXMark') as ConcreteComponent
|
||||
const IconCircleCheck = resolveComponent('IconCircleCheck') as ConcreteComponent
|
||||
|
||||
const comments = computed(() => [
|
||||
props.modelValue,
|
||||
@@ -584,13 +584,13 @@ const bannerButton = computed(() => {
|
||||
if (hasClickedFullContext.value) {
|
||||
return {
|
||||
text: 'Back',
|
||||
icon: ArrowLeftIcon,
|
||||
icon: MoveLeft,
|
||||
action: goBack
|
||||
}
|
||||
}
|
||||
return {
|
||||
text: 'Full context',
|
||||
icon: ArrowUpRightIcon,
|
||||
icon: MoveRight,
|
||||
action: handleContextClick
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
class="text-foreground hover:text-foreground-2 flex items-center gap-x-1"
|
||||
@click="() => onAttachmentClick(attachment)"
|
||||
>
|
||||
<PaperClipIcon class="size-3" />
|
||||
<Paperclip class="size-3" />
|
||||
<span class="truncate relative text-body-3xs">
|
||||
{{ attachment.fileName }}
|
||||
</span>
|
||||
@@ -34,7 +34,7 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="inline-flex space-x-4 items-center">
|
||||
<ExclamationTriangleIcon class="w-6 h-6" />
|
||||
<TriangleAlert class="w-6 h-6" />
|
||||
<span>
|
||||
Please note: This file is user-uploaded and has not been scanned for
|
||||
security. Download at your own discretion.
|
||||
@@ -48,11 +48,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
ExclamationTriangleIcon,
|
||||
PaperClipIcon
|
||||
} from '@heroicons/vue/24/outline'
|
||||
import type { Get } from 'type-fest'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import type { Nullable, Optional } from '@speckle/shared'
|
||||
@@ -62,6 +57,7 @@ import { prettyFileSize } from '~~/lib/core/helpers/file'
|
||||
import { useFileDownload } from '~~/lib/core/composables/fileUpload'
|
||||
import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast'
|
||||
import type { LayoutDialogButton } from '@speckle/ui-components'
|
||||
import { Download, Paperclip, TriangleAlert } from 'lucide-vue-next'
|
||||
|
||||
type AttachmentFile = NonNullable<
|
||||
Get<ThreadCommentAttachmentFragment, 'text.attachments[0]'>
|
||||
@@ -135,7 +131,7 @@ const dialogButtons = computed((): Optional<LayoutDialogButton[]> => {
|
||||
? prettyFileSize(dialogAttachment.value.fileSize)
|
||||
: 'Download',
|
||||
props: {
|
||||
iconLeft: ArrowDownTrayIcon,
|
||||
iconLeft: Download,
|
||||
color: 'outline'
|
||||
},
|
||||
onClick: () => {
|
||||
|
||||
@@ -25,15 +25,14 @@
|
||||
/>
|
||||
<div class="flex justify-between items-center p-1">
|
||||
<FormButton
|
||||
:icon-left="PaperClipIcon"
|
||||
:icon-left="Paperclip"
|
||||
:disabled="loading"
|
||||
color="subtle"
|
||||
hide-text
|
||||
class="!bg-foundation dark:!bg-foundation-2"
|
||||
@click="trackAttachAndOpenFilePicker()"
|
||||
/>
|
||||
<FormButton
|
||||
:icon-left="PaperAirplaneIcon"
|
||||
:icon-left="SendHorizonal"
|
||||
hide-text
|
||||
:disabled="loading"
|
||||
@click="onSubmit"
|
||||
@@ -44,7 +43,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { PaperAirplaneIcon, PaperClipIcon } from '@heroicons/vue/24/solid'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
@@ -60,6 +58,7 @@ import { useServerFileUploadLimit } from '~~/lib/common/composables/serverInfo'
|
||||
import { UniqueFileTypeSpecifier } from '~~/lib/core/helpers/file'
|
||||
import { acceptedFileExtensions } from '@speckle/shared/blobs'
|
||||
import type { UploadableFileItem } from '@speckle/ui-components'
|
||||
import { Paperclip, SendHorizonal } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: CommentBubbleModel
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
hide-text
|
||||
color="subtle"
|
||||
:icon-left="settingsIcon"
|
||||
size="sm"
|
||||
:class="
|
||||
showVisibilityOptions
|
||||
? '!text-primary-focus !dark:text-foreground-on-primary !bg-info-lighter'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #title>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<FormButton
|
||||
:icon-left="ChevronLeftIcon"
|
||||
:icon-left="ChevronLeft"
|
||||
color="subtle"
|
||||
class="-ml-3"
|
||||
hide-text
|
||||
@@ -71,7 +71,7 @@
|
||||
</ViewerLayoutSidePanel>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ChevronLeftIcon } from '@heroicons/vue/24/solid'
|
||||
import { ChevronLeft } from 'lucide-vue-next'
|
||||
import { VisualDiffMode } from '@speckle/viewer'
|
||||
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
||||
import { uniqBy, debounce } from 'lodash-es'
|
||||
|
||||
@@ -67,6 +67,7 @@ import { onKeyStroke, useBreakpoints } from '@vueuse/core'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { Ruler, Scissors, Sun, Layers2, Glasses } from 'lucide-vue-next'
|
||||
|
||||
enum ActivePanel {
|
||||
none = 'none',
|
||||
@@ -111,7 +112,7 @@ const panels = shallowRef({
|
||||
[ActivePanel.measurements]: {
|
||||
id: ActivePanel.measurements,
|
||||
name: 'Measure',
|
||||
icon: 'IconViewerMeasurements',
|
||||
icon: Ruler,
|
||||
tooltip: getShortcutDisplayText(shortcuts.ToggleMeasurements, {
|
||||
format: 'separate'
|
||||
}),
|
||||
@@ -120,28 +121,28 @@ const panels = shallowRef({
|
||||
[ActivePanel.sectionBox]: {
|
||||
id: ActivePanel.sectionBox,
|
||||
name: 'Section',
|
||||
icon: 'IconViewerSectionBox',
|
||||
icon: Scissors,
|
||||
tooltip: getShortcutDisplayText(shortcuts.ToggleSectionBox, { format: 'separate' }),
|
||||
extraClasses: ''
|
||||
},
|
||||
[ActivePanel.explode]: {
|
||||
id: ActivePanel.explode,
|
||||
name: 'Explode',
|
||||
icon: 'IconViewerExplode',
|
||||
icon: Layers2,
|
||||
tooltip: getShortcutDisplayText(shortcuts.ToggleExplode, { format: 'separate' }),
|
||||
extraClasses: 'hidden md:flex'
|
||||
},
|
||||
[ActivePanel.viewModes]: {
|
||||
id: ActivePanel.viewModes,
|
||||
name: 'View modes',
|
||||
icon: 'IconViewerViewModes',
|
||||
icon: Glasses,
|
||||
tooltip: getShortcutDisplayText(shortcuts.ToggleViewModes, { format: 'separate' }),
|
||||
extraClasses: ''
|
||||
},
|
||||
[ActivePanel.lightControls]: {
|
||||
id: ActivePanel.lightControls,
|
||||
name: 'Light controls',
|
||||
icon: 'IconViewerLightControls',
|
||||
icon: Sun,
|
||||
tooltip: getShortcutDisplayText(shortcuts.ToggleLightControls, {
|
||||
format: 'separate'
|
||||
}),
|
||||
|
||||
@@ -23,7 +23,7 @@ import type { ConcreteComponent } from 'vue'
|
||||
|
||||
defineProps<{
|
||||
active?: boolean
|
||||
icon?: string | ConcreteComponent
|
||||
icon?: ConcreteComponent
|
||||
secondary?: boolean
|
||||
dot?: boolean
|
||||
}>()
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
)
|
||||
"
|
||||
:active="activePanel === 'models'"
|
||||
:icon="'IconViewerModels'"
|
||||
:icon="Box"
|
||||
@click="toggleActivePanel('models')"
|
||||
/>
|
||||
<ViewerControlsButtonToggle
|
||||
@@ -33,7 +33,7 @@
|
||||
)
|
||||
"
|
||||
:active="activePanel === 'filters'"
|
||||
:icon="'IconViewerExplorer'"
|
||||
:icon="ListFilter"
|
||||
:dot="hasActiveFilters"
|
||||
@click="toggleActivePanel('filters')"
|
||||
/>
|
||||
@@ -47,7 +47,7 @@
|
||||
)
|
||||
"
|
||||
:active="activePanel === 'discussions'"
|
||||
:icon="'IconViewerDiscussions'"
|
||||
:icon="MessageSquareText"
|
||||
@click="toggleActivePanel('discussions')"
|
||||
/>
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
)
|
||||
"
|
||||
:active="activePanel === 'devMode'"
|
||||
:icon="'IconViewerDev'"
|
||||
:icon="CodeXml"
|
||||
secondary
|
||||
@click="toggleActivePanel('devMode')"
|
||||
/>
|
||||
@@ -98,14 +98,14 @@
|
||||
placement: 'right'
|
||||
})
|
||||
"
|
||||
:icon="'IconDocs'"
|
||||
:icon="BookOpen"
|
||||
secondary
|
||||
@click="openDocs"
|
||||
/>
|
||||
<ViewerControlsButtonToggle
|
||||
v-if="isIntercomEnabled"
|
||||
v-tippy="getTooltipProps('Get help')"
|
||||
:icon="'IconIntercom'"
|
||||
:icon="CircleQuestionMark"
|
||||
secondary
|
||||
@click="openIntercomChat"
|
||||
/>
|
||||
@@ -166,7 +166,15 @@ import { useFunctionRunsStatusSummary } from '~/lib/automate/composables/runStat
|
||||
import { useIntercomEnabled } from '~~/lib/intercom/composables/enabled'
|
||||
import { viewerDocsRoute } from '~~/lib/common/helpers/route'
|
||||
import { useAreSavedViewsEnabled } from '~/lib/viewer/composables/savedViews/general'
|
||||
import { Camera } from 'lucide-vue-next'
|
||||
import {
|
||||
Camera,
|
||||
CodeXml,
|
||||
BookOpen,
|
||||
Box,
|
||||
ListFilter,
|
||||
MessageSquareText,
|
||||
CircleQuestionMark
|
||||
} from 'lucide-vue-next'
|
||||
import { ModelsSubView } from '~~/lib/viewer/helpers/sceneExplorer'
|
||||
|
||||
type ActivePanel =
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
}
|
||||
)
|
||||
"
|
||||
icon="IconViewerZoom"
|
||||
:icon="Fullscreen"
|
||||
@click="trackAndzoomExtentsOrSelection()"
|
||||
/>
|
||||
<ViewerControlsButtonToggle
|
||||
v-tippy="getTooltipProps('Camera controls', { placement: 'left' })"
|
||||
icon="IconViewerCameraControls"
|
||||
:icon="Video"
|
||||
:active="activePanel === 'cameraControls'"
|
||||
@click="toggleActivePanel('cameraControls')"
|
||||
/>
|
||||
@@ -47,6 +47,7 @@ import { onClickOutside, useBreakpoints } from '@vueuse/core'
|
||||
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { Fullscreen, Video } from 'lucide-vue-next'
|
||||
|
||||
type ActivePanel = 'none' | 'cameraControls'
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<FormButton
|
||||
v-tippy="'Toggle coloring'"
|
||||
color="subtle"
|
||||
size="sm"
|
||||
hide-text
|
||||
:icon-right="colors ? 'IconColouring' : 'IconColouringOutline'"
|
||||
@click="toggleColors()"
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
}`"
|
||||
@click.stop="hideOrShowObject"
|
||||
> -->
|
||||
<EyeSlashIcon v-if="isHidden" class="h-3 w-3" />
|
||||
<EyeClosed v-if="isHidden" class="h-3 w-3" />
|
||||
<!-- </button>
|
||||
<button
|
||||
:class="`hover:text-primary px-1 py-2 opacity-0 transition group-hover:opacity-100 ${
|
||||
@@ -60,8 +60,8 @@
|
||||
}`"
|
||||
@click.stop="isolateOrUnisolateObject"
|
||||
> -->
|
||||
<FunnelIconOutline v-if="!isIsolated" class="h-3 w-3" />
|
||||
<FunnelIcon v-else class="h-3 w-3" />
|
||||
<FunnelX v-if="!isIsolated" class="h-3 w-3" />
|
||||
<Funnel v-else class="h-3 w-3" />
|
||||
<!-- </button> -->
|
||||
</div>
|
||||
</button>
|
||||
@@ -72,11 +72,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { EyeSlashIcon, FunnelIcon } from '@heroicons/vue/24/solid'
|
||||
import { FunnelIcon as FunnelIconOutline } from '@heroicons/vue/24/outline'
|
||||
import { containsAll, hasIntersection } from '~~/lib/common/helpers/utils'
|
||||
import { useInjectedViewerState } from '~~/lib/viewer/composables/setup'
|
||||
import { useSelectionUtilities } from '~~/lib/viewer/composables/ui'
|
||||
import { EyeClosed, Funnel, FunnelX } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
item: {
|
||||
|
||||
@@ -1,37 +1,40 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<div v-if="!hideVersions" v-tippy="'Versions'" class="flex">
|
||||
<button
|
||||
class="group-hover:opacity-100 hover:bg-highlight-3 rounded-md h-6 w-6 flex items-center justify-center shrink-0"
|
||||
<FormButton
|
||||
:icon-left="History"
|
||||
hide-text
|
||||
size="sm"
|
||||
color="subtle"
|
||||
@click="$emit('showVersions')"
|
||||
>
|
||||
<IconVersions class="w-4 h-4" />
|
||||
<span class="sr-only">Versions</span>
|
||||
</button>
|
||||
Versions
|
||||
</FormButton>
|
||||
</div>
|
||||
<div v-if="!hideAddModel" v-tippy="'Add model'" class="flex">
|
||||
<button
|
||||
class="group-hover:opacity-100 hover:bg-highlight-3 rounded-md h-6 w-6 flex items-center justify-center shrink-0"
|
||||
<div v-tippy="'Add model'" class="flex">
|
||||
<FormButton
|
||||
:icon-left="Plus"
|
||||
hide-text
|
||||
size="sm"
|
||||
color="subtle"
|
||||
@click="$emit('addModel')"
|
||||
>
|
||||
<IconPlus class="w-4 h-4" />
|
||||
<span class="sr-only">Add model</span>
|
||||
</button>
|
||||
Add model
|
||||
</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormButton } from '@speckle/ui-components'
|
||||
import { Plus, History } from 'lucide-vue-next'
|
||||
|
||||
defineProps<{
|
||||
hideVersions?: boolean
|
||||
hideAddModel?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
showVersions: []
|
||||
addModel: []
|
||||
}>()
|
||||
|
||||
const IconPlus = resolveComponent('IconPlus')
|
||||
const IconVersions = resolveComponent('IconVersions')
|
||||
</script>
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center ml-auto mr-2 w-0 group-hover:w-auto"
|
||||
:class="showActionsMenu ? '!w-auto' : ''"
|
||||
class="flex items-center ml-auto mr-2 w-0 group-hover:w-auto opacity-0 group-hover:opacity-100 transition"
|
||||
:class="showActionsMenu ? '!w-auto !opacity-100' : ''"
|
||||
>
|
||||
<LayoutMenu
|
||||
v-model:open="showActionsMenu"
|
||||
@@ -66,16 +66,16 @@
|
||||
@click.stop.prevent
|
||||
@chosen="onActionChosen"
|
||||
>
|
||||
<button
|
||||
class="group-hover:opacity-100 hover:bg-highlight-3 rounded-md h-6 w-6 flex items-center justify-center"
|
||||
<FormButton
|
||||
hide-text
|
||||
:class="{
|
||||
'opacity-100 bg-highlight-3': showActionsMenu,
|
||||
'sm:opacity-0': !showActionsMenu
|
||||
'!bg-highlight-3': showActionsMenu
|
||||
}"
|
||||
@click.stop="showActionsMenu = !showActionsMenu"
|
||||
>
|
||||
<IconThreeDots class="w-4 h-4" />
|
||||
</button>
|
||||
color="subtle"
|
||||
:icon-left="Ellipsis"
|
||||
size="sm"
|
||||
@click="showActionsMenu = !showActionsMenu"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
<ViewerVisibilityButton
|
||||
:is-hidden="isHidden"
|
||||
@@ -113,6 +113,7 @@ import { getTargetObjectIds } from '~~/lib/object-sidebar/helpers'
|
||||
import { useLoadLatestVersion } from '~~/lib/viewer/composables/resources'
|
||||
import { SpeckleViewer } from '@speckle/shared'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { Ellipsis } from 'lucide-vue-next'
|
||||
|
||||
type ModelItem = NonNullable<Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #title>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<FormButton
|
||||
:icon-left="ChevronLeftIcon"
|
||||
:icon-left="ChevronLeft"
|
||||
color="subtle"
|
||||
class="-ml-3"
|
||||
hide-text
|
||||
@@ -58,7 +58,7 @@ import { SpeckleViewer } from '@speckle/shared'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { ViewerEvent } from '@speckle/viewer'
|
||||
import { useViewerEventListener } from '~~/lib/viewer/composables/viewer'
|
||||
import { ChevronLeftIcon } from '@heroicons/vue/24/solid'
|
||||
import { ChevronLeft } from 'lucide-vue-next'
|
||||
import { useDiffUtilities } from '~~/lib/viewer/composables/ui'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<div
|
||||
class="group relative w-full rounded-md pb-2 text-left pl-5 pt-2"
|
||||
class="group relative w-full rounded-md text-left pl-5 pt-1 pb-2"
|
||||
:class="
|
||||
clickable && !isLimited ? 'hover:bg-highlight-1 cursor-pointer' : 'cursor-default'
|
||||
"
|
||||
@@ -11,14 +11,14 @@
|
||||
<!-- Timeline left border -->
|
||||
<div
|
||||
v-if="showTimeline"
|
||||
class="absolute top-5 left-4 z-10 ml-[2px] w-1 border-l border-outline-3"
|
||||
class="absolute top-4 left-4 z-10 ml-[2px] mt-[2px] w-1 border-l border-outline-3"
|
||||
:class="last ? 'h-0' : 'h-[99%]'"
|
||||
>
|
||||
<div
|
||||
v-if="isLoaded"
|
||||
class="absolute -top-2.5 -left-2 flex items-center justify-center h-4 w-4 bg-foundation-2 rounded-full"
|
||||
class="absolute -top-1.5 -left-2 flex items-center justify-center h-4 w-4 bg-foundation-2 rounded-full"
|
||||
>
|
||||
<IconCheck class="h-4 w-4 text-foreground" />
|
||||
<Check class="h-3 w-3 text-foreground" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
@@ -37,25 +37,29 @@
|
||||
</span>
|
||||
</div>
|
||||
<CommonBadge v-if="isLoaded" rounded>Viewing</CommonBadge>
|
||||
<LayoutMenu
|
||||
v-model:open="showActionsMenu"
|
||||
class="ml-auto mr-2"
|
||||
:items="actionsItems"
|
||||
:menu-position="HorizontalDirection.Left"
|
||||
mount-menu-on-body
|
||||
@click.stop.prevent
|
||||
@chosen="onActionChosen"
|
||||
>
|
||||
<button
|
||||
class="opacity-0 group-hover:opacity-100 hover:bg-highlight-3 rounded-md h-5 w-5 flex items-center justify-center shrink-0"
|
||||
@click.stop="showActionsMenu = !showActionsMenu"
|
||||
<div class="ml-auto mr-2 mt-0.5">
|
||||
<LayoutMenu
|
||||
v-model:open="showActionsMenu"
|
||||
:items="actionsItems"
|
||||
:menu-position="HorizontalDirection.Left"
|
||||
mount-menu-on-body
|
||||
@click.stop.prevent
|
||||
@chosen="onActionChosen"
|
||||
>
|
||||
<IconThreeDots />
|
||||
</button>
|
||||
</LayoutMenu>
|
||||
<FormButton
|
||||
hide-text
|
||||
color="subtle"
|
||||
:icon-left="Ellipsis"
|
||||
size="sm"
|
||||
@click.stop="showActionsMenu = !showActionsMenu"
|
||||
>
|
||||
Menu
|
||||
</FormButton>
|
||||
</LayoutMenu>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Main stuff -->
|
||||
<div class="flex items-center pl-5 gap-2 mt-2">
|
||||
<div class="flex items-center pl-5 gap-2 mt-1">
|
||||
<div
|
||||
class="bg-foundation h-12 w-12 flex-shrink-0 rounded-md border border-outline-3"
|
||||
:class="isLimited ? 'diagonal-stripes' : ''"
|
||||
@@ -64,7 +68,7 @@
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-md bg-foundation border border-outline-3"
|
||||
>
|
||||
<LockClosedIcon class="h-4 w-4 text-foreground-3" />
|
||||
<Lock class="h-4 w-4 text-foreground-3" />
|
||||
</div>
|
||||
</div>
|
||||
<PreviewImage v-else :preview-url="version.previewUrl" />
|
||||
@@ -91,7 +95,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { LockClosedIcon } from '@heroicons/vue/24/solid'
|
||||
import { CommonBadge, keyboardClick } from '@speckle/ui-components'
|
||||
import dayjs from 'dayjs'
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||
@@ -101,6 +104,7 @@ import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
|
||||
import { HorizontalDirection } from '~~/lib/common/composables/window'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useCopyModelLink } from '~/lib/projects/composables/modelManagement'
|
||||
import { Ellipsis, Check, Lock } from 'lucide-vue-next'
|
||||
|
||||
dayjs.extend(localizedFormat)
|
||||
|
||||
@@ -139,8 +143,6 @@ const {
|
||||
} = useInjectedViewerState()
|
||||
const copyModelLink = useCopyModelLink()
|
||||
|
||||
const IconThreeDots = resolveComponent('IconThreeDots')
|
||||
|
||||
const isLoaded = computed(() => props.isLoadedVersion)
|
||||
const isLatest = computed(() => props.isLatestVersion)
|
||||
|
||||
@@ -158,7 +160,6 @@ const createdAt = computed(() => {
|
||||
|
||||
const author = computed(() => props.version.authorUser)
|
||||
|
||||
const IconCheck = resolveComponent('IconCheck')
|
||||
const showActionsMenu = ref(false)
|
||||
|
||||
const canDeleteVersion = computed(() => {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<p>Selected</p>
|
||||
<span>Selected</span>
|
||||
<CommonBadge v-if="objects.length > 1" rounded>
|
||||
{{ objects.length }}
|
||||
</CommonBadge>
|
||||
@@ -21,30 +21,16 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<div class="flex gap-x-0.5 items-center">
|
||||
<div
|
||||
v-tippy="getTooltipProps(isHidden ? 'Show' : 'Hide', { placement: 'top' })"
|
||||
>
|
||||
<FormButton
|
||||
color="subtle"
|
||||
:icon-left="isHidden ? iconEyeClosed : iconEye"
|
||||
hide-text
|
||||
@click.stop="hideOrShowSelection"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-tippy="
|
||||
getTooltipProps(isIsolated ? 'Unisolate' : 'Isolate', {
|
||||
placement: 'top'
|
||||
})
|
||||
"
|
||||
>
|
||||
<FormButton
|
||||
color="subtle"
|
||||
:icon-left="isIsolated ? iconViewerUnisolate : iconViewerIsolate"
|
||||
hide-text
|
||||
@click.stop="isolateOrUnisolateSelection"
|
||||
/>
|
||||
</div>
|
||||
<ViewerVisibilityButton
|
||||
:is-hidden="isHidden"
|
||||
:force-visible="showSubMenu"
|
||||
@click="hideOrShowSelection"
|
||||
/>
|
||||
<ViewerIsolateButton
|
||||
:is-isolated="isIsolated"
|
||||
:force-visible="showSubMenu"
|
||||
@click="isolateOrUnisolateSelection"
|
||||
/>
|
||||
<LayoutMenu
|
||||
v-model:open="showSubMenu"
|
||||
:menu-id="menuId"
|
||||
@@ -56,7 +42,11 @@
|
||||
<FormButton
|
||||
hide-text
|
||||
color="subtle"
|
||||
:icon-left="settingsIcon"
|
||||
size="sm"
|
||||
:icon-left="Ellipsis"
|
||||
:class="{
|
||||
'!bg-highlight-3': showSubMenu
|
||||
}"
|
||||
@click="showSubMenu = !showSubMenu"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
@@ -97,8 +87,8 @@ import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
|
||||
import { modelRoute } from '~/lib/common/helpers/route'
|
||||
import { TailwindBreakpoints } from '~~/lib/common/helpers/tailwind'
|
||||
import type { ConcreteComponent } from 'vue'
|
||||
import type { LayoutMenuItem } from '~~/lib/layout/helpers/components'
|
||||
import { Ellipsis } from 'lucide-vue-next'
|
||||
|
||||
enum ActionTypes {
|
||||
OpenInNewTab = 'open-in-new-tab'
|
||||
@@ -121,17 +111,11 @@ const breakpoints = useBreakpoints(TailwindBreakpoints)
|
||||
const isGreaterThanSm = breakpoints.greater('sm')
|
||||
const menuId = useId()
|
||||
const mp = useMixpanel()
|
||||
const { getTooltipProps } = useSmartTooltipDelay()
|
||||
|
||||
const itemCount = ref(20)
|
||||
const sidebarOpen = ref(false)
|
||||
const sidebarWidth = ref(280)
|
||||
const showSubMenu = ref(false)
|
||||
const iconViewerUnisolate = resolveComponent('IconViewerUnisolate') as ConcreteComponent
|
||||
const iconViewerIsolate = resolveComponent('IconViewerIsolate') as ConcreteComponent
|
||||
const iconEyeClosed = resolveComponent('IconEyeClosed') as ConcreteComponent
|
||||
const iconEye = resolveComponent('IconEye') as ConcreteComponent
|
||||
const settingsIcon = resolveComponent('IconThreeDots') as ConcreteComponent
|
||||
|
||||
const objectsUniqueByAppId = computed(() => {
|
||||
if (!diff.enabled.value) return objects.value
|
||||
|
||||
@@ -270,7 +270,12 @@ const iconClasses = computed(() => {
|
||||
|
||||
switch (props.size) {
|
||||
case 'sm':
|
||||
classParts.push('h-4 w-4 p-0.5')
|
||||
// Dont add padding to icon if hideText is true on sm size
|
||||
if (props.hideText) {
|
||||
classParts.push('h-4 w-4')
|
||||
} else {
|
||||
classParts.push('h-4 w-4 p-0.5')
|
||||
}
|
||||
break
|
||||
case 'lg':
|
||||
classParts.push('h-6 w-6 p-1')
|
||||
|
||||
Reference in New Issue
Block a user