Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9ff8ee5f7 | |||
| 30c2a2002c | |||
| 4502a48098 | |||
| f2ab186bd8 | |||
| fbe9672f81 | |||
| 2691902533 | |||
| 95294c6e6f | |||
| e70aa8ae4b | |||
| e65bf83995 | |||
| 74ade84015 | |||
| abc4bf11fe | |||
| 0bd1218d49 | |||
| e277ce686e | |||
| f3f5eddb7b | |||
| 28ce6b6a76 | |||
| fe483b7b2b | |||
| 72b4b9b589 | |||
| 57c0f198bd | |||
| 439bf56f47 | |||
| 3ecae7f493 | |||
| 843174f5b6 | |||
| 83cfa39be0 | |||
| e4401da357 | |||
| 222a6f8987 | |||
| c44b54616e | |||
| 7b22e929e0 | |||
| e1e6d4e640 | |||
| 7bdb80f801 | |||
| da774b631a | |||
| ca0765c862 | |||
| 87b64b7a11 | |||
| 41c4e642fe | |||
| 76febcfce6 | |||
| 8fbb5a3c9d | |||
| eecde37f8b | |||
| a21a960516 | |||
| 9e76f62ad0 | |||
| ab266fa410 |
@@ -17,6 +17,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install GitVersion
|
||||
uses: gittools/actions/gitversion/setup@v3.0.0
|
||||
with:
|
||||
@@ -26,6 +31,10 @@ jobs:
|
||||
id: gitversion
|
||||
uses: gittools/actions/gitversion/execute@v3.0.0
|
||||
|
||||
- name: Set connector version
|
||||
run: |
|
||||
python patch_version.py ${{steps.gitversion.outputs.AssemblySemVer}}
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ This repo is home to our Power BI connector. The Speckle Server provides all the
|
||||
|
||||
# Installation
|
||||
|
||||
Speckle connector can be installed directly from [Manager for Speckle](https://speckle.systems/download/). Full instructions for [installation](https://speckle.guide/user/powerbi/installation.html) and [configuration](https://speckle.guide/user/powerbi/configuration.html) can be found on our docs.
|
||||
Speckle connector can be installed directly from the [connectors portal](https://app.speckle.systems/connectors/). Full instructions for [installation](https://speckle.guide/user/powerbi/installation.html) and [configuration](https://speckle.guide/user/powerbi/configuration.html) can be found on our docs.
|
||||
|
||||
# Using 3D Visual
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
def sanitize_version(tag):
|
||||
"""Extracts the first three numeric segments from a tag string, because PowerBI is..."""
|
||||
parts = re.findall(r"\d+", tag)
|
||||
return ".".join(parts[:3]) if len(parts) >= 3 else tag
|
||||
|
||||
def patch_connector(tag):
|
||||
"""Patches the connector version within the data connector file"""
|
||||
sanitized_tag = sanitize_version(tag)
|
||||
pq_file = os.path.join(os.path.dirname(__file__), "src", "powerbi-data-connector", "Speckle.pq")
|
||||
|
||||
with open(pq_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
|
||||
for (index, line) in enumerate(lines):
|
||||
if '[Version = "3.0.0"]' in line:
|
||||
lines[index] = f'[Version = "{sanitized_tag}"]\n'
|
||||
print(f"Patched connector version number in {pq_file}")
|
||||
break
|
||||
|
||||
with open(pq_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
return
|
||||
|
||||
tag = sys.argv[1]
|
||||
if not re.match(r"([0-9]+)\.([0-9]+)\.([0-9]+)", tag):
|
||||
raise ValueError(f"Invalid tag provided: {tag}")
|
||||
|
||||
print(f"Patching version: {tag}")
|
||||
patch_connector(tag)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -58,12 +58,23 @@ shared Speckle.GetStructuredData = Value.ReplaceType(
|
||||
type function (url as Uri.Type) as table
|
||||
);
|
||||
|
||||
shared Speckle.GetVersion = Value.ReplaceType(
|
||||
Speckle.LoadFunction("GetVersion.pqm"),
|
||||
type function () as text
|
||||
);
|
||||
|
||||
[DataSource.Kind = "Speckle"]
|
||||
shared Speckle.SendToServer = Value.ReplaceType(
|
||||
Speckle.LoadFunction("SendToServer.pqm"),
|
||||
type function (url as Uri.Type) as table
|
||||
);
|
||||
|
||||
[DataSource.Kind = "Speckle"]
|
||||
shared Speckle.GetWorkspace = Value.ReplaceType(
|
||||
Speckle.LoadFunction("GetWorkspace.pqm"),
|
||||
type function (url as Uri.Type) as record
|
||||
);
|
||||
|
||||
[DataSource.Kind = "Speckle", Publish="GetByUrl.Publish"]
|
||||
shared Speckle.GetByUrl = Value.ReplaceType(
|
||||
Speckle.LoadFunction("GetByUrl.pqm"),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// NOTE! for tests, be make sure you put here a model that in private project to make sure all good.
|
||||
let
|
||||
result = Speckle.GetByUrl(
|
||||
"https://latest.speckle.systems/projects/126cd4b7bb/models/85c44d39c6"
|
||||
"https://app.speckle.systems/projects/b61ab234b0/models/a8166255b5"
|
||||
)
|
||||
in
|
||||
result
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
() as text =>
|
||||
let
|
||||
// read the Speckle.pq file
|
||||
specklePqContent = try
|
||||
Text.FromBinary(Extension.Contents("Speckle.pq"))
|
||||
otherwise
|
||||
error "Could not read Speckle.pq file",
|
||||
|
||||
lines = Text.Split(specklePqContent, "#(lf)"),
|
||||
|
||||
versionLine = List.First(
|
||||
List.Select(
|
||||
lines,
|
||||
each Text.Contains(_, "[Version = ")
|
||||
),
|
||||
null
|
||||
),
|
||||
|
||||
version = if versionLine <> null then
|
||||
let
|
||||
// find the start and end positions of the version string
|
||||
startPos = Text.PositionOf(versionLine, """") + 1,
|
||||
tempText = Text.Middle(versionLine, startPos),
|
||||
endPos = Text.PositionOf(tempText, """"),
|
||||
versionText = Text.Middle(tempText, 0, endPos)
|
||||
in
|
||||
versionText
|
||||
else
|
||||
// fallback version if parsing fails
|
||||
"3.0.0",
|
||||
|
||||
// validate version format
|
||||
isValidVersion =
|
||||
let
|
||||
parts = Text.Split(version, "."),
|
||||
isValid = List.Count(parts) = 3 and
|
||||
List.AllTrue(List.Transform(parts, each try Number.From(_) >= 0 otherwise false))
|
||||
in
|
||||
isValid,
|
||||
|
||||
result = if isValidVersion then
|
||||
version
|
||||
else
|
||||
error "Invalid version format found: " & version
|
||||
in
|
||||
result
|
||||
@@ -0,0 +1,67 @@
|
||||
// function for getting workspace information
|
||||
(url as text) as record =>
|
||||
let
|
||||
ApiFetch = Extension.LoadFunction("Api.Fetch.pqm"),
|
||||
Parser = Extension.LoadFunction("Parser.pqm"),
|
||||
|
||||
// the logic for importing functions from other files
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
binary = Extension.Contents(fileName),
|
||||
asText = Text.FromBinary(binary)
|
||||
in
|
||||
try
|
||||
Expression.Evaluate(asText, #shared)
|
||||
catch (e) =>
|
||||
error
|
||||
[
|
||||
Reason = "Extension.LoadFunction Failure",
|
||||
Message.Format = "Loading '#{0}' failed - '#{1}': '#{2}'",
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
],
|
||||
|
||||
parsedUrl = Parser(url),
|
||||
server = parsedUrl[baseUrl],
|
||||
projectId = parsedUrl[projectId],
|
||||
|
||||
// query to get workspace ID from project
|
||||
projectQuery = "query Project($projectId: String!) {
|
||||
data:project(id: $projectId) {
|
||||
workspaceId
|
||||
}
|
||||
}",
|
||||
|
||||
projectVariables = [
|
||||
projectId = projectId
|
||||
],
|
||||
|
||||
projectResult = ApiFetch(server, projectQuery, projectVariables),
|
||||
workspaceId = projectResult[data][workspaceId],
|
||||
|
||||
// query to get workspace
|
||||
workspaceQuery = "query Workspace($workspaceId: String!, $featureName: WorkspaceFeatureName!) {
|
||||
data:workspace(id: $workspaceId) {
|
||||
logo
|
||||
name
|
||||
hasAccessToFeature(featureName: $featureName)
|
||||
}
|
||||
}",
|
||||
|
||||
workspaceVariables = [
|
||||
workspaceId = workspaceId,
|
||||
featureName = "hideSpeckleBranding"
|
||||
],
|
||||
|
||||
workspaceResult = ApiFetch(server, workspaceQuery, workspaceVariables),
|
||||
|
||||
workspace = workspaceResult[data],
|
||||
|
||||
workspaceInfo = [
|
||||
workspaceId = workspaceId,
|
||||
workspaceLogo = workspace[logo],
|
||||
workspaceName = workspace[name],
|
||||
canHideBranding = workspace[hasAccessToFeature]
|
||||
]
|
||||
in
|
||||
workspaceInfo
|
||||
@@ -4,6 +4,9 @@
|
||||
GetModel = Extension.LoadFunction("GetModel.pqm"),
|
||||
Parser = Extension.LoadFunction("Parser.pqm"),
|
||||
GetUser = Extension.LoadFunction("GetUser.pqm"),
|
||||
GetVersion = Extension.LoadFunction("GetVersion.pqm"),
|
||||
GetWorkspace = Extension.LoadFunction("GetWorkspace.pqm"),
|
||||
|
||||
// the logic for importing functions from other files
|
||||
Extension.LoadFunction = (fileName as text) =>
|
||||
let
|
||||
@@ -20,18 +23,20 @@
|
||||
Message.Parameters = {fileName, e[Reason], e[Message]},
|
||||
Detail = [File = fileName, Error = e]
|
||||
],
|
||||
|
||||
// Get model info and parsed URL
|
||||
|
||||
modelInfo = GetModel(url),
|
||||
parsedUrl = Parser(url),
|
||||
userInfo = GetUser(url),
|
||||
|
||||
// Get API key if available
|
||||
|
||||
apiKey = userInfo[Token],
|
||||
|
||||
// Get user email from credentials
|
||||
userEmail = userInfo[UserEmail],
|
||||
|
||||
|
||||
// get version from Speckle.pq - look GetVersion.pqm
|
||||
connectorVersion = GetVersion(),
|
||||
|
||||
workspaceInfo = GetWorkspace(url),
|
||||
|
||||
// Prepare request data
|
||||
requestData = Json.FromValue([
|
||||
Url = url,
|
||||
@@ -40,7 +45,12 @@
|
||||
ProjectId = parsedUrl[projectId],
|
||||
ObjectId = modelInfo[rootObjectId],
|
||||
SourceApplication = modelInfo[sourceApplication],
|
||||
Token = apiKey
|
||||
Token = apiKey,
|
||||
Version = connectorVersion,
|
||||
WorkspaceId = workspaceInfo[workspaceId],
|
||||
WorkspaceName = workspaceInfo[workspaceName],
|
||||
WorkspaceLogo = workspaceInfo[workspaceLogo],
|
||||
CanHideBranding = workspaceInfo[canHideBranding]
|
||||
]),
|
||||
|
||||
// Send request to local server
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"dataRoles": [
|
||||
{
|
||||
"displayName": "Version Object ID",
|
||||
"kind": "Grouping",
|
||||
"kind": "Measure",
|
||||
"name": "rootObjectId"
|
||||
},
|
||||
{
|
||||
@@ -11,14 +11,14 @@
|
||||
"name": "objectIds"
|
||||
},
|
||||
{
|
||||
"displayName": "Color By",
|
||||
"kind": "Grouping",
|
||||
"name": "objectColorBy"
|
||||
},
|
||||
{
|
||||
"displayName": "Tooltip Data",
|
||||
"displayName": "Object Data (Tooltip)",
|
||||
"kind": "Measure",
|
||||
"name": "tooltipData"
|
||||
},
|
||||
{
|
||||
"displayName": "Color By",
|
||||
"kind": "Grouping",
|
||||
"name": "colorBy"
|
||||
}
|
||||
],
|
||||
"dataViewMappings": [
|
||||
@@ -33,12 +33,7 @@
|
||||
"select": [
|
||||
{
|
||||
"bind": {
|
||||
"to": "rootObjectId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bind": {
|
||||
"to": "objectColorBy"
|
||||
"to": "colorBy"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -51,8 +46,13 @@
|
||||
"values": {
|
||||
"select": [
|
||||
{
|
||||
"bind": {
|
||||
"to": "tooltipData"
|
||||
"for": {
|
||||
"in": "rootObjectId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"for": {
|
||||
"in": "tooltipData"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -60,6 +60,7 @@
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"colorBy": { "max": 1 },
|
||||
"objectIds": { "max": 1 },
|
||||
"rootObjectId": { "max": 1 }
|
||||
}
|
||||
|
||||
Generated
+72
-19
@@ -45,6 +45,7 @@
|
||||
"@types/webpack": "^5.28.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/parser": "^5.59.2",
|
||||
"@vueuse/core": "^13.2.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.2",
|
||||
"base64-inline-loader": "^2.0.1",
|
||||
@@ -3146,6 +3147,48 @@
|
||||
"vue": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@speckle/ui-components/node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@speckle/ui-components/node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@speckle/ui-components/node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@speckle/ui-components/node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@speckle/viewer": {
|
||||
"version": "2.23.23",
|
||||
"resolved": "https://registry.npmjs.org/@speckle/viewer/-/viewer-2.23.23.tgz",
|
||||
@@ -3718,9 +3761,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
|
||||
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
|
||||
"version": "0.0.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/webpack": {
|
||||
"version": "5.28.5",
|
||||
@@ -4185,36 +4230,44 @@
|
||||
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz",
|
||||
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.2.0.tgz",
|
||||
"integrity": "sha512-n5TZoIAxbWAQ3PqdVPDzLgIRQOujFfMlatdI+f7ditSmoEeNpPBvp7h2zamzikCmrhFIePAwdEQB6ENccHr7Rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.16",
|
||||
"@vueuse/metadata": "9.13.0",
|
||||
"@vueuse/shared": "9.13.0",
|
||||
"vue-demi": "*"
|
||||
"@types/web-bluetooth": "^0.0.21",
|
||||
"@vueuse/metadata": "13.2.0",
|
||||
"@vueuse/shared": "13.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz",
|
||||
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.2.0.tgz",
|
||||
"integrity": "sha512-kPpzuQCU0+D8DZCzK0iPpIcXI+6ufWSgwnjJ6//GNpEn+SHViaCtR+XurzORChSgvpHO9YC8gGM97Y1kB+UabA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "9.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz",
|
||||
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
|
||||
"dependencies": {
|
||||
"vue-demi": "*"
|
||||
},
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.2.0.tgz",
|
||||
"integrity": "sha512-vx9ZPDF5HcU9up3Jgt3G62dMUfZEdk6tLyBAHYAG4F4n73vpaA7J5hdncDI/lS9Vm7GA/FPlbOmh9TrDZROTpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@types/webpack": "^5.28.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||
"@typescript-eslint/parser": "^5.59.2",
|
||||
"@vueuse/core": "^13.2.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.2",
|
||||
"base64-inline-loader": "^2.0.1",
|
||||
|
||||
@@ -1,212 +1,119 @@
|
||||
<template>
|
||||
<ButtonGroup>
|
||||
<ButtonSimple flat secondary @click="onZoomExtentsClicked">
|
||||
<ArrowsPointingOutIcon class="h-5 w-5" />
|
||||
</ButtonSimple>
|
||||
<!-- Canonical Views -->
|
||||
<Menu as="div" class="relative z-50">
|
||||
<MenuButton v-slot="{ open }" as="template">
|
||||
<ButtonToggle flat secondary :active="open">
|
||||
<VideoCameraIcon class="h-5 w-5" />
|
||||
</ButtonToggle>
|
||||
</MenuButton>
|
||||
<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"
|
||||
<div class="space-y-2">
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- Zoom extend -->
|
||||
<ViewerControlsButtonToggle v-tippy="'Zoom extends'" flat @click="onZoomExtentsClicked">
|
||||
<ArrowsPointingOutIcon class="h-4 w-4 md:h-5 md:w-5" />
|
||||
</ViewerControlsButtonToggle>
|
||||
<!-- Ghost / Hidden -->
|
||||
<ViewerControlsButtonToggle flat @click="toggleGhostHidden">
|
||||
<Ghost v-if="isGhost" class="h-5 w-5" />
|
||||
<Ghost v-else class="h-5 w-5 opacity-30" />
|
||||
</ViewerControlsButtonToggle>
|
||||
</ViewerControlsButtonGroup>
|
||||
<ViewerControlsButtonGroup>
|
||||
<!-- View Modes -->
|
||||
<ViewerViewModesMenu
|
||||
:open="viewModesOpen"
|
||||
@force-close-others="activeControl = 'none'"
|
||||
@update:open="(value) => toggleActiveControl(value ? 'viewModes' : 'none')"
|
||||
@view-mode-clicked="(value) => $emit('view-mode-clicked', value)"
|
||||
/>
|
||||
<!-- Views -->
|
||||
<ViewerViewsMenu
|
||||
:open="viewsOpen"
|
||||
:views="views"
|
||||
@force-close-others="activeControl = 'none'"
|
||||
@update:open="(value) => toggleActiveControl(value ? 'views' : 'none')"
|
||||
@view-clicked="(view) => $emit('view-clicked', view)"
|
||||
/>
|
||||
<!-- Perspective/Ortho -->
|
||||
<ViewerControlsButtonToggle
|
||||
flat
|
||||
secondary
|
||||
:active="isOrthoProjection"
|
||||
@click="toggleProjection"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute w-20 left-2 mb-8 bottom-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"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="view in canonicalViews"
|
||||
:key="view.name"
|
||||
v-slot="{ active }"
|
||||
as="template"
|
||||
>
|
||||
<button
|
||||
:class="{
|
||||
'bg-primary text-foreground-on-primary': active,
|
||||
'text-foreground': !active,
|
||||
'text-sm py-1 transition': true
|
||||
}"
|
||||
@click="handleCameraViewChange(view.name.toLocaleLowerCase() as CanonicalView)"
|
||||
>
|
||||
{{ view.name }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
<MenuItem v-for="view in views" :key="view.name" v-slot="{ active }" as="template">
|
||||
<button
|
||||
:class="{
|
||||
'bg-primary text-foreground-on-primary': active,
|
||||
'text-foreground': !active,
|
||||
'text-sm py-2 transition': true
|
||||
}"
|
||||
@click="handleCameraViewChange(view)"
|
||||
>
|
||||
{{ view.view.name ?? view.name }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<!-- Speckle Custom Views -->
|
||||
<Menu v-if="visualStore.speckleViews.length" as="div" class="relative z-40">
|
||||
<MenuButton v-slot="{ open }" as="template">
|
||||
<ButtonToggle flat secondary :active="open">
|
||||
<ViewsIcon class="h-5 w-5" />
|
||||
</ButtonToggle>
|
||||
</MenuButton>
|
||||
<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"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute w-24 left-2 mb-8 bottom-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"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="view in visualStore.speckleViews"
|
||||
:key="view.id"
|
||||
v-slot="{ active }"
|
||||
as="template"
|
||||
>
|
||||
<button
|
||||
:class="{
|
||||
'bg-primary text-foreground-on-primary': active,
|
||||
'text-foreground': !active,
|
||||
'text-sm py-2 transition': true
|
||||
}"
|
||||
@click="handleCameraViewChange(view)"
|
||||
>
|
||||
{{ view.name }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<Menu as="div" class="relative z-30">
|
||||
<MenuButton v-slot="{ open }" as="template">
|
||||
<ButtonToggle flat secondary :active="open">
|
||||
<ViewModesIcon class="h-5 w-5" />
|
||||
</ButtonToggle>
|
||||
</MenuButton>
|
||||
<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"
|
||||
>
|
||||
<MenuItems
|
||||
class="absolute w-20 left-2 mb-8 bottom-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"
|
||||
>
|
||||
<MenuItem
|
||||
v-for="(label, mode) in viewModes"
|
||||
:key="mode"
|
||||
v-slot="{ active }"
|
||||
as="template"
|
||||
>
|
||||
<button
|
||||
:class="{
|
||||
'bg-primary text-foreground-on-primary': active,
|
||||
'text-foreground': !active,
|
||||
'text-sm py-1 transition': true
|
||||
}"
|
||||
@click="handleCameraViewModeChange(Number(mode))"
|
||||
>
|
||||
{{ label }}
|
||||
</button>
|
||||
</MenuItem>
|
||||
</MenuItems>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<!--
|
||||
<ButtonToggle
|
||||
flat
|
||||
secondary
|
||||
:active="sectionBox"
|
||||
@click="$emit('update:sectionBox', !sectionBox)"
|
||||
>
|
||||
<CubeIcon class="h-5 w-5" />
|
||||
</ButtonToggle>
|
||||
<ButtonSimple flat secondary @click="onClearPalletteClicked">
|
||||
<PaintBrushIcon class="h-5 w-5" />
|
||||
</ButtonSimple> -->
|
||||
</ButtonGroup>
|
||||
<Perspective v-if="isOrthoProjection" class="h-3.5 md:h-4 w-4" />
|
||||
<PerspectiveMore v-else class="h-3.5 md:h-4 w-4" />
|
||||
</ViewerControlsButtonToggle>
|
||||
</ViewerControlsButtonGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
VideoCameraIcon,
|
||||
CubeIcon,
|
||||
ArrowsPointingOutIcon,
|
||||
PaintBrushIcon
|
||||
} from '@heroicons/vue/24/solid'
|
||||
import ViewModesIcon from 'src/components/icons/ViewModesIcon.vue'
|
||||
import ViewsIcon from 'src/components/icons/ViewsIcon.vue'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
||||
import { CanonicalView, SpeckleView, ViewMode } from '@speckle/viewer'
|
||||
import ButtonToggle from 'src/components/controls/ButtonToggle.vue'
|
||||
import ButtonGroup from 'src/components/controls/ButtonGroup.vue'
|
||||
import ButtonSimple from 'src/components/controls/ButtonSimple.vue'
|
||||
import { inject, watch } from 'vue'
|
||||
import { resetPalette } from 'src/utils/matrixViewUtils'
|
||||
import { ArrowsPointingOutIcon } from '@heroicons/vue/24/solid'
|
||||
import { SpeckleView } from '@speckle/viewer'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useVisualStore } from '@src/store/visualStore'
|
||||
import ViewerControlsButtonGroup from './viewer/controls/ViewerControlsButtonGroup.vue'
|
||||
import ViewerControlsButtonToggle from './viewer/controls/ViewerControlsButtonToggle.vue'
|
||||
|
||||
import ViewerViewModesMenu from './viewer/view-modes/ViewerViewModesMenu.vue'
|
||||
import ViewerViewsMenu from './viewer/views/ViewerViewsMenu.vue'
|
||||
|
||||
import Perspective from '../components/global/icon/Perspective.vue'
|
||||
import PerspectiveMore from '../components/global/icon/PerspectiveMore.vue'
|
||||
|
||||
import Ghost from '../components/global/icon/Ghost.vue'
|
||||
|
||||
const visualStore = useVisualStore()
|
||||
|
||||
const emits = defineEmits([
|
||||
'update:sectionBox',
|
||||
'view-clicked',
|
||||
'toggle-projection',
|
||||
'clear-palette',
|
||||
'view-mode-clicked'
|
||||
])
|
||||
const props = withDefaults(defineProps<{ sectionBox: boolean; views: SpeckleView[] }>(), {
|
||||
withDefaults(defineProps<{ sectionBox: boolean; views: SpeckleView[] }>(), {
|
||||
sectionBox: false
|
||||
})
|
||||
|
||||
const canonicalViews = [
|
||||
{ name: 'Top' },
|
||||
{ name: 'Front' },
|
||||
{ name: 'Left' },
|
||||
{ name: 'Back' },
|
||||
{ name: 'Right' }
|
||||
]
|
||||
const isOrthoProjection = ref(false)
|
||||
const isGhost = ref(true)
|
||||
|
||||
const viewModes = {
|
||||
[ViewMode.DEFAULT]: 'Default',
|
||||
[ViewMode.DEFAULT_EDGES]: 'Edges',
|
||||
[ViewMode.SHADED]: 'Shaded',
|
||||
[ViewMode.PEN]: 'Pen',
|
||||
[ViewMode.ARCTIC]: 'Arctic',
|
||||
[ViewMode.COLORS]: 'Colors'
|
||||
}
|
||||
type ActiveControl =
|
||||
| 'none'
|
||||
| 'viewModes'
|
||||
| 'views'
|
||||
| 'sun'
|
||||
| 'projection'
|
||||
| 'sectionBox'
|
||||
| 'explode'
|
||||
| 'settings'
|
||||
|
||||
const handleCameraViewChange = (view: CanonicalView | SpeckleView) => {
|
||||
emits('view-clicked', view)
|
||||
// visualStore.writeCameraViewToFile(view)
|
||||
}
|
||||
|
||||
const handleCameraViewModeChange = (viewMode: ViewMode) => {
|
||||
emits('view-mode-clicked', viewMode)
|
||||
visualStore.writeViewModeToFile(viewMode)
|
||||
}
|
||||
const activeControl = ref<ActiveControl>('none')
|
||||
|
||||
const onZoomExtentsClicked = (ev: MouseEvent) => {
|
||||
visualStore.viewerEmit('zoomExtends')
|
||||
}
|
||||
|
||||
const onClearPalletteClicked = (ev: MouseEvent) => {
|
||||
console.log('Clear pallette clicked')
|
||||
resetPalette()
|
||||
emits('clear-palette')
|
||||
const toggleActiveControl = (control: ActiveControl) => {
|
||||
activeControl.value = activeControl.value === control ? 'none' : control
|
||||
}
|
||||
|
||||
const toggleProjection = () => {
|
||||
isOrthoProjection.value = !isOrthoProjection.value
|
||||
visualStore.viewerEmit('toggleProjection')
|
||||
}
|
||||
|
||||
const toggleGhostHidden = () => {
|
||||
isGhost.value = !isGhost.value
|
||||
visualStore.viewerEmit('toggleGhostHidden', isGhost.value)
|
||||
}
|
||||
|
||||
const viewModesOpen = computed({
|
||||
get: () => activeControl.value === 'viewModes',
|
||||
set: (value) => {
|
||||
activeControl.value = value ? 'viewModes' : 'none'
|
||||
}
|
||||
})
|
||||
|
||||
const viewsOpen = computed({
|
||||
get: () => activeControl.value === 'views',
|
||||
set: (value) => {
|
||||
activeControl.value = value ? 'views' : 'none'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,34 +1,89 @@
|
||||
<template>
|
||||
<div class="flex flex-col justify-center items-center">
|
||||
<div
|
||||
ref="container"
|
||||
class="fixed h-full w-full z-0"
|
||||
@click="onCanvasClick"
|
||||
@auxclick="onCanvasAuxClick"
|
||||
/>
|
||||
<!-- <div class="z-30 w-1/2 px-10">
|
||||
<common-loading-bar :loading="isLoading" />
|
||||
</div> -->
|
||||
<viewer-controls
|
||||
<transition name="slide-fade">
|
||||
<nav
|
||||
v-show="!isNavbarCollapsed"
|
||||
class="fixed top-0 h-9 flex items-center bg-foundation border-b border-outline-2 w-full transition z-20 shadow-sm hover:shadow cursor-default"
|
||||
>
|
||||
<div class="flex items-center transition-all justify-between w-full">
|
||||
<div class="flex items-center hover:cursor-pointer" @click="goToSpeckleWebsite">
|
||||
<div class="max-[200px]:hidden block ml-2">
|
||||
<img class="w-6 h-auto ml-1 mr-2 my-1" src="@assets/logo-big.png" />
|
||||
</div>
|
||||
<div class="font-sans font-medium">Speckle</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="font-thin text-xs mr-2 text-gray-400">v1.0.0</div>
|
||||
<button
|
||||
class="text-gray-400 hover:text-gray-700 transition"
|
||||
title="Hide navbar"
|
||||
@click="isNavbarCollapsed = true"
|
||||
>
|
||||
<ChevronUpIcon class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</transition>
|
||||
|
||||
<!-- TODO: another transition here needed that below components - but this time it will move to left -->
|
||||
|
||||
<div
|
||||
v-if="!isInteractive"
|
||||
class="absolute top-1 left-1/2 -translate-x-1/2 z-20 bg-white bg-opacity-70 text-black text-center text-xs px-4 py-1 rounded shadow font-medium"
|
||||
>
|
||||
<strong>Object IDs</strong>
|
||||
field is needed for interactivity with other visuals.
|
||||
</div>
|
||||
|
||||
<div v-if="isNavbarCollapsed" class="fixed top-2 right-0 z-20">
|
||||
<button
|
||||
class="transition opacity-50 hover:opacity-100"
|
||||
title="Show navbar"
|
||||
@click="isNavbarCollapsed = false"
|
||||
>
|
||||
<ChevronDownIcon class="w-4 h-4 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- till here -->
|
||||
|
||||
<transition name="slide-left">
|
||||
<ViewerControls
|
||||
v-show="!isNavbarCollapsed"
|
||||
v-model:section-box="bboxActive"
|
||||
:views="views"
|
||||
class="fixed bottom-6"
|
||||
class="fixed top-11 left-1 z-30"
|
||||
@view-clicked="(view) => viewerHandler.setView(view)"
|
||||
@view-mode-clicked="(viewMode) => viewerHandler.setViewMode(viewMode)"
|
||||
/>
|
||||
</transition>
|
||||
|
||||
<div v-if="visualStore.isFilterActive" class="absolute bottom-5 left-1/2 -translate-x-1/2 z-50">
|
||||
<FormButton size="sm" @click="visualStore.resetFilters(), selectionHandler.reset()">
|
||||
Reset filters
|
||||
</FormButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref="container"
|
||||
class="fixed h-full w-full z-0"
|
||||
@click="onCanvasClick"
|
||||
@auxclick="onCanvasAuxClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script async setup lang="ts">
|
||||
import { inject, onBeforeUnmount, onMounted, Ref, ref } from 'vue'
|
||||
import FormButton from '@src/components/form/FormButton.vue'
|
||||
import { computed, inject, onBeforeUnmount, onMounted, Ref, ref } from 'vue'
|
||||
import { currentOS, OS } from '../utils/detectOS'
|
||||
import ViewerControls from 'src/components/ViewerControls.vue'
|
||||
import { SpeckleView } from '@speckle/viewer'
|
||||
import { useClickDragged } from 'src/composables/useClickDragged'
|
||||
import { ContextOption } from 'src/settings/colorSettings'
|
||||
import { useVisualStore } from '@src/store/visualStore'
|
||||
import { ViewerHandler } from '@src/plugins/viewer'
|
||||
import { selectionHandlerKey, tooltipHandlerKey } from '@src/injectionKeys'
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/24/outline'
|
||||
|
||||
const visualStore = useVisualStore()
|
||||
const { dragged } = useClickDragged()
|
||||
@@ -42,6 +97,14 @@ const container = ref<HTMLElement>()
|
||||
let bboxActive = ref(false)
|
||||
let views: Ref<SpeckleView[]> = ref([])
|
||||
|
||||
const isNavbarCollapsed = ref(false)
|
||||
|
||||
const isInteractive = computed(
|
||||
() => visualStore.fieldInputState.rootObjectId && visualStore.fieldInputState.objectIds
|
||||
)
|
||||
|
||||
const goToSpeckleWebsite = () => visualStore.host.launchUrl('https://speckle.systems')
|
||||
|
||||
onMounted(async () => {
|
||||
console.log('Viewer Wrapper mounted')
|
||||
viewerHandler = new ViewerHandler()
|
||||
@@ -62,14 +125,12 @@ function isMultiSelect(e: MouseEvent) {
|
||||
async function onCanvasClick(ev: MouseEvent) {
|
||||
if (dragged.value) return
|
||||
|
||||
// eslint-disable-next-line no-debugger
|
||||
debugger
|
||||
|
||||
const intersectResult = await viewerHandler.intersect({ x: ev.clientX, y: ev.clientY })
|
||||
|
||||
const multi = isMultiSelect(ev)
|
||||
const hit = intersectResult?.hit
|
||||
if (hit) {
|
||||
visualStore.setPostClickSkipNeeded(true)
|
||||
const id = hit.object.id as string
|
||||
if (multi || !selectionHandler.isSelected(id)) {
|
||||
await selectionHandler.select(id, multi)
|
||||
@@ -79,6 +140,7 @@ async function onCanvasClick(ev: MouseEvent) {
|
||||
const ids = selection.map((s) => s.id)
|
||||
await viewerHandler.selectObjects(ids)
|
||||
} else {
|
||||
visualStore.setPostClickSkipNeeded(false)
|
||||
tooltipHandler.hide()
|
||||
if (!multi) {
|
||||
selectionHandler.clear()
|
||||
@@ -93,3 +155,30 @@ async function onCanvasAuxClick(ev: MouseEvent) {
|
||||
await selectionHandler.showContextMenu(ev, intersectResult?.hit)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.slide-fade-enter-active,
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.slide-fade-enter-from,
|
||||
.slide-fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
.slide-fade-enter-to,
|
||||
.slide-fade-leave-from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.slide-left-enter-active,
|
||||
.slide-left-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.slide-left-enter-from,
|
||||
.slide-left-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<template>
|
||||
<button
|
||||
class="bg-foundation text-foreground shadow-md rounded-lg h-10 flex justify-center space-x-2 px-1"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<button
|
||||
ref="button"
|
||||
:class="`transition rounded-lg w-10 h-10 flex items-center justify-center ${shadowClasses} ${colorClasses} active:scale-[0.9] outline-none`"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
let active = ref(false)
|
||||
let button = ref<HTMLElement>()
|
||||
|
||||
const props = defineProps<{
|
||||
flat?: boolean
|
||||
secondary?: boolean
|
||||
}>()
|
||||
|
||||
const shadowClasses = computed(() => (props.flat ? '' : 'shadow-md'))
|
||||
|
||||
const colorClasses = computed(() => {
|
||||
const parts = []
|
||||
if (active.value) {
|
||||
if (props.secondary) parts.push('bg-foundation text-primary')
|
||||
else parts.push('bg-primary text-foreground-on-primary')
|
||||
} else {
|
||||
parts.push('bg-foundation text-foreground')
|
||||
}
|
||||
return parts.join(' ')
|
||||
})
|
||||
|
||||
const onPointerDown = () => (active.value = true)
|
||||
const onPointerUp = () => (active.value = false)
|
||||
|
||||
onMounted(() => {
|
||||
button.value.addEventListener('pointerdown', onPointerDown)
|
||||
button.value.addEventListener('pointerup', onPointerUp)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
button.value.removeEventListener('pointerdown', onPointerDown)
|
||||
button.value.removeEventListener('pointerup', onPointerUp)
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<Component
|
||||
:is="to ? linkComponent : 'button'"
|
||||
:href="to"
|
||||
:to="to"
|
||||
:type="buttonType"
|
||||
:external="external"
|
||||
:class="buttonClasses"
|
||||
:disabled="isDisabled"
|
||||
role="button"
|
||||
:style="
|
||||
color !== 'subtle' && !text
|
||||
? `box-shadow: -1px 1px 4px 0px #0000000a inset; box-shadow: 0px 2px 2px 0px #0000000d;`
|
||||
: ''
|
||||
"
|
||||
@click="onClick"
|
||||
>
|
||||
<Component :is="finalLeftIcon" v-if="finalLeftIcon" :class="iconClasses" />
|
||||
<slot v-if="!hideText">Button</slot>
|
||||
<Component :is="iconRight" v-if="iconRight || !loading" :class="iconClasses" />
|
||||
</Component>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { isObjectLike } from 'lodash'
|
||||
import type { PropAnyComponent } from '../../helpers/common/components'
|
||||
import { computed, resolveDynamicComponent } from 'vue'
|
||||
import type { Nullable } from '@speckle/shared'
|
||||
import type { FormButtonStyle, FormButtonSize } from '../../helpers/form/button'
|
||||
|
||||
const emit = defineEmits<{
|
||||
/**
|
||||
* Emit MouseEvent on click
|
||||
*/
|
||||
(e: 'click', val: MouseEvent): void
|
||||
}>()
|
||||
|
||||
const props = defineProps<{
|
||||
/**
|
||||
* URL to which to navigate - can be a relative (app) path or an absolute link for an external URL
|
||||
*/
|
||||
to?: string
|
||||
/**
|
||||
* Choose from one of 3 button sizes
|
||||
*/
|
||||
size?: FormButtonSize
|
||||
/**
|
||||
* If set, will make the button take up all available space horizontally
|
||||
*/
|
||||
fullWidth?: boolean
|
||||
/**
|
||||
* Similar to "link", but without an underline and possibly in different colors
|
||||
*/
|
||||
text?: boolean
|
||||
/**
|
||||
* Will remove paddings and background. Use for links.
|
||||
*/
|
||||
link?: boolean
|
||||
/**
|
||||
* color:
|
||||
* primary: the default primary blue.
|
||||
* outline: foundation background and outline
|
||||
* subtle: no styling
|
||||
*/
|
||||
color?: FormButtonStyle
|
||||
/**
|
||||
* Should rounded-full be added?:
|
||||
*/
|
||||
rounded?: boolean
|
||||
/**
|
||||
* Whether the target location should be forcefully treated as an external URL
|
||||
* (for relative paths this will likely cause a redirect)
|
||||
*/
|
||||
external?: boolean
|
||||
/**
|
||||
* Whether to disable the button so that it can't be pressed
|
||||
*/
|
||||
disabled?: boolean
|
||||
/**
|
||||
* If set, will have type set to "submit" to enable it to submit any parent forms
|
||||
*/
|
||||
submit?: boolean
|
||||
/**
|
||||
* Add icon to the left from the text
|
||||
*/
|
||||
iconLeft?: Nullable<PropAnyComponent>
|
||||
/**
|
||||
* Add icon to the right from the text
|
||||
*/
|
||||
iconRight?: Nullable<PropAnyComponent>
|
||||
/**
|
||||
* Hide default slot (when you want to show icons only)
|
||||
*/
|
||||
hideText?: boolean
|
||||
/**
|
||||
* Customize component to be used when rendering links.
|
||||
*
|
||||
* The component will try to dynamically resolve NuxtLink and RouterLink and use those, if this is set to null.
|
||||
*/
|
||||
linkComponent?: Nullable<PropAnyComponent>
|
||||
/**
|
||||
* Disables the button and shows a spinning loader
|
||||
*/
|
||||
loading?: boolean
|
||||
}>()
|
||||
|
||||
const NuxtLink = resolveDynamicComponent('NuxtLink')
|
||||
const RouterLink = resolveDynamicComponent('RouterLink')
|
||||
|
||||
const linkComponent = computed(() => {
|
||||
if (props.linkComponent) return props.linkComponent
|
||||
if (props.external) return 'a'
|
||||
if (isObjectLike(NuxtLink)) return NuxtLink
|
||||
if (isObjectLike(RouterLink)) return RouterLink
|
||||
return 'a'
|
||||
})
|
||||
|
||||
const buttonType = computed(() => {
|
||||
if (props.to) return undefined
|
||||
if (props.submit) return 'submit'
|
||||
return 'button'
|
||||
})
|
||||
|
||||
const isDisabled = computed(() => props.disabled || props.loading)
|
||||
const finalLeftIcon = computed(() => props.iconLeft)
|
||||
|
||||
const bgAndBorderClasses = computed(() => {
|
||||
const classParts: string[] = []
|
||||
|
||||
const colorsBgBorder = {
|
||||
subtle: [
|
||||
'bg-transparent border-transparent text-foreground font-medium',
|
||||
'hover:bg-primary-muted disabled:hover:bg-transparent focus-visible:border-foundation'
|
||||
],
|
||||
outline: [
|
||||
'bg-foundation border-outline-2 text-foreground font-medium',
|
||||
'hover:bg-primary-muted disabled:hover:bg-foundation focus-visible:border-foundation'
|
||||
],
|
||||
danger: [
|
||||
'bg-danger border-danger-darker text-foundation font-medium',
|
||||
'hover:bg-danger-darker disabled:hover:bg-danger focus-visible:border-foundation'
|
||||
],
|
||||
primary: [
|
||||
'bg-primary border-outline-1 text-foreground-on-primary font-semibold',
|
||||
'hover:bg-primary-focus disabled:hover:bg-primary focus-visible:border-foundation'
|
||||
]
|
||||
}
|
||||
|
||||
if (props.rounded) {
|
||||
classParts.push('!rounded-full')
|
||||
}
|
||||
|
||||
if (props.text || props.link) {
|
||||
switch (props.color) {
|
||||
case 'subtle':
|
||||
classParts.push('text-foreground')
|
||||
break
|
||||
case 'outline':
|
||||
classParts.push('text-foreground')
|
||||
break
|
||||
case 'danger':
|
||||
classParts.push('text-danger')
|
||||
break
|
||||
case 'primary':
|
||||
default:
|
||||
classParts.push('text-primary')
|
||||
break
|
||||
}
|
||||
} else {
|
||||
switch (props.color) {
|
||||
case 'subtle':
|
||||
classParts.push(...colorsBgBorder.subtle)
|
||||
break
|
||||
case 'outline':
|
||||
classParts.push(...colorsBgBorder.outline)
|
||||
break
|
||||
case 'danger':
|
||||
classParts.push(...colorsBgBorder.danger)
|
||||
break
|
||||
case 'primary':
|
||||
default:
|
||||
classParts.push(...colorsBgBorder.primary)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const sizeClasses = computed(() => {
|
||||
switch (props.size) {
|
||||
case 'sm':
|
||||
return 'h-6 text-body-2xs'
|
||||
case 'lg':
|
||||
return 'h-10 text-body-sm'
|
||||
default:
|
||||
case 'base':
|
||||
return 'h-8 text-body-xs'
|
||||
}
|
||||
})
|
||||
|
||||
const paddingClasses = computed(() => {
|
||||
if (props.text || props.link) {
|
||||
return 'p-0'
|
||||
}
|
||||
|
||||
const hasIconLeft = !!props.iconLeft
|
||||
const hasIconRight = !!props.iconRight
|
||||
const hideText = props.hideText
|
||||
|
||||
switch (props.size) {
|
||||
case 'sm':
|
||||
if (hideText) return 'w-6'
|
||||
if (hasIconLeft) return 'py-1 pr-2 pl-1'
|
||||
if (hasIconRight) return 'py-1 pl-2 pr-1'
|
||||
return 'px-2 py-1'
|
||||
case 'lg':
|
||||
if (hideText) return 'w-10'
|
||||
if (hasIconLeft) return 'py-2 pr-6 pl-4'
|
||||
if (hasIconRight) return 'py-2 pl-6 pr-4'
|
||||
return 'px-6 py-2'
|
||||
case 'base':
|
||||
default:
|
||||
if (hideText) return 'w-8'
|
||||
if (hasIconLeft) return 'py-0 pr-4 pl-2'
|
||||
if (hasIconRight) return 'py-0 pl-4 pr-2'
|
||||
return 'px-4 py-0'
|
||||
}
|
||||
})
|
||||
|
||||
const generalClasses = computed(() => {
|
||||
const baseClasses = [
|
||||
'inline-flex justify-center items-center',
|
||||
'text-center select-none whitespace-nowrap',
|
||||
'outline outline-2 outline-transparent',
|
||||
'transition duration-200 ease-in-out focus-visible:outline-outline-4'
|
||||
]
|
||||
|
||||
const additionalClasses = []
|
||||
|
||||
if (!props.text && !props.link) {
|
||||
additionalClasses.push('rounded-md border')
|
||||
}
|
||||
|
||||
if (props.fullWidth) {
|
||||
additionalClasses.push('w-full')
|
||||
} else if (!props.hideText) {
|
||||
additionalClasses.push('max-w-max')
|
||||
}
|
||||
if (isDisabled.value) {
|
||||
additionalClasses.push('cursor-not-allowed opacity-60')
|
||||
}
|
||||
|
||||
return [...baseClasses, ...additionalClasses].join(' ')
|
||||
})
|
||||
|
||||
const buttonClasses = computed(() => {
|
||||
return [
|
||||
generalClasses.value,
|
||||
sizeClasses.value,
|
||||
bgAndBorderClasses.value,
|
||||
paddingClasses.value
|
||||
].join(' ')
|
||||
})
|
||||
|
||||
const iconClasses = computed(() => {
|
||||
const classParts: string[] = ['shrink-0']
|
||||
|
||||
switch (props.size) {
|
||||
case 'sm':
|
||||
classParts.push('h-4 w-4 p-0.5')
|
||||
break
|
||||
case 'lg':
|
||||
classParts.push('h-6 w-6 p-1')
|
||||
break
|
||||
case 'base':
|
||||
default:
|
||||
classParts.push('h-6 w-6 p-1')
|
||||
break
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const onClick = (e: MouseEvent) => {
|
||||
if (isDisabled.value) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
e.stopImmediatePropagation()
|
||||
return
|
||||
}
|
||||
|
||||
emit('click', e)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 1C6.24288 1 4.81818 2.42334 4.81818 4.18004C4.81818 5.93674 6.24288 7.36008 8 7.36008C9.75712 7.36008 11.1818 5.93674 11.1818 4.18004C11.1818 2.42334 9.75712 1 8 1Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M6.18182 9.17649C4.42465 9.17649 3 10.6005 3 12.3578V14.6281H13V12.3578C13 10.6005 11.5754 9.17649 9.81818 9.17649H6.18182Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
vector-effect="non-scaling-stroke"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13 11.5H15.5V13H13V15.5H11.5V13H9V11.5H11.5V9H13V11.5ZM10.5 0.75C10.9142 0.75 11.25 1.08579 11.25 1.5V2H12C13.6569 2 15 3.34315 15 5V8H13.5V6.75H1.5V12C1.5 12.8284 2.17157 13.5 3 13.5H8V15H3C1.34315 15 0 13.6569 0 12V5C8.05333e-08 3.34315 1.34315 2 3 2H4.75V1.5C4.75 1.08579 5.08579 0.75 5.5 0.75C5.91421 0.75 6.25 1.08579 6.25 1.5V2H9.75V1.5C9.75 1.08579 10.0858 0.75 10.5 0.75ZM3 3.5C2.17157 3.5 1.5 4.17157 1.5 5V5.25H13.5V5C13.5 4.17157 12.8284 3.5 12 3.5H3Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8ZM16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM8.75 3.75C8.75 3.33579 8.41421 3 8 3C7.58579 3 7.25 3.33579 7.25 3.75V8V8.31066L7.46967 8.53033L9.72358 10.7842C10.0165 11.0771 10.4913 11.0771 10.7842 10.7842C11.0771 10.4913 11.0771 10.0165 10.7842 9.72358L8.75 7.68934V3.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12.8849 5.91851L7.25614 11.74L3.99951 8.48337L4.93388 7.549L7.24028 9.8554L11.9349 5L12.8849 5.91851Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -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,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M8 14.5C8.23033 14.5 8.84266 14.2743 9.48679 12.986C9.79275 12.3741 10.0504 11.6156 10.2293 10.75H5.77067C5.94959 11.6156 6.20725 12.3741 6.51321 12.986C7.15734 14.2743 7.76967 14.5 8 14.5ZM5.55361 9.25C5.51859 8.84716 5.5 8.42956 5.5 8C5.5 7.57044 5.51859 7.15284 5.55361 6.75H10.4464C10.4814 7.15284 10.5 7.57044 10.5 8C10.5 8.42956 10.4814 8.84716 10.4464 9.25H5.55361ZM11.7574 10.75C11.5334 11.974 11.1641 13.0579 10.6914 13.9184C12.0984 13.2775 13.2369 12.1496 13.8913 10.75H11.7574ZM14.3799 9.25H11.9515C11.9834 8.84271 12 8.42523 12 8C12 7.57477 11.9834 7.15729 11.9515 6.75H14.3799C14.4587 7.15451 14.5 7.57243 14.5 8C14.5 8.42756 14.4587 8.84549 14.3799 9.25ZM4.04854 9.25H1.62008C1.54128 8.84549 1.5 8.42756 1.5 8C1.5 7.57243 1.54128 7.15451 1.62008 6.75H4.04854C4.01659 7.15729 4 7.57477 4 8C4 8.42523 4.01659 8.84271 4.04854 9.25ZM2.10868 10.75H4.2426C4.46661 11.974 4.83588 13.0579 5.30864 13.9184C3.90156 13.2775 2.7631 12.1496 2.10868 10.75ZM5.77067 5.25H10.2293C10.0504 4.38438 9.79275 3.6259 9.48679 3.01397C8.84266 1.72571 8.23033 1.5 8 1.5C7.76967 1.5 7.15734 1.72571 6.51321 3.01397C6.20725 3.6259 5.94959 4.38438 5.77067 5.25ZM11.7574 5.25H13.8913C13.2369 3.85044 12.0984 2.72251 10.6914 2.08162C11.1641 2.94207 11.5334 4.02603 11.7574 5.25ZM5.30864 2.08162C4.83588 2.94207 4.46661 4.02603 4.2426 5.25H2.10868C2.7631 3.85044 3.90156 2.72251 5.30864 2.08162ZM8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.06301 2.75L13.2511 9.93813L11.4539 11.7354C10.9854 12.2227 10.4243 12.6116 9.80367 12.8795C9.183 13.1473 8.51514 13.2887 7.83918 13.2953C7.16321 13.302 6.49271 13.1737 5.86691 12.9181C5.2411 12.6625 4.67257 12.2846 4.19456 11.8066C3.71656 11.3286 3.33869 10.76 3.08306 10.1342C2.82743 9.50843 2.69918 8.83793 2.70581 8.16196C2.71244 7.486 2.85382 6.81814 3.12167 6.19747C3.38953 5.5768 3.77848 5.01579 4.26576 4.54725L6.06301 2.75Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1 15L4.0625 11.9375"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.625 1L7.5625 4.0625"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15 5.375L11.9375 8.4375"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg width="18" height="19" viewBox="0 0 18 19" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.33333 17L1 1L16 9.25806L8.5 10.5L4.33333 17Z" stroke="#2563eb" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3 2.5C2.17157 2.5 1.5 3.17157 1.5 4V10C1.5 10.8284 2.17157 11.5 3 11.5H3.75H4.5V12.25V13.1004L6.56675 11.6378L6.76146 11.5H7H13C13.8284 11.5 14.5 10.8284 14.5 10V4C14.5 3.17157 13.8284 2.5 13 2.5H3ZM0 4C0 2.34315 1.34315 1 3 1H13C14.6569 1 16 2.34315 16 4V10C16 11.6569 14.6569 13 13 13H7.23854L4.18325 15.1622L3 15.9996V14.55V13C1.34315 13 0 11.6569 0 10V4Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6 1C5.0335 1 4.25 1.7835 4.25 2.75V4H3C1.89543 4 1 4.89543 1 6V13C1 14.1046 1.89543 15 3 15H13C14.1046 15 15 14.1046 15 13V6C15 4.89543 14.1046 4 13 4H11.75V2.75C11.75 1.7835 10.9665 1 10 1H6ZM10.25 4V2.75C10.25 2.61193 10.1381 2.5 10 2.5H6C5.86193 2.5 5.75 2.61193 5.75 2.75V4H10.25ZM3 5.5H13C13.2761 5.5 13.5 5.72386 13.5 6V7H2.5V6C2.5 5.72386 2.72386 5.5 3 5.5ZM2.5 8.5V13C2.5 13.2761 2.72386 13.5 3 13.5H13C13.2761 13.5 13.5 13.2761 13.5 13V8.5H9V10H7V8.5H2.5Z"
|
||||
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="M7.8335 7.83337H7.00016C6.55814 7.83337 6.13421 8.00897 5.82165 8.32153C5.50909 8.63409 5.3335 9.05801 5.3335 9.50004V17C5.3335 17.4421 5.50909 17.866 5.82165 18.1786C6.13421 18.4911 6.55814 18.6667 7.00016 18.6667H14.5002C14.9422 18.6667 15.3661 18.4911 15.6787 18.1786C15.9912 17.866 16.1668 17.4421 16.1668 17V16.1667"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M18.9875 7.48759C19.3157 7.15938 19.5001 6.71424 19.5001 6.25009C19.5001 5.78594 19.3157 5.34079 18.9875 5.01259C18.6593 4.68438 18.2142 4.5 17.75 4.5C17.2858 4.5 16.8407 4.68438 16.5125 5.01259L9.5 12.0001V14.5001H12L18.9875 7.48759Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.3335 6.16663L17.8335 8.66663"
|
||||
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,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3 2.5C2.17157 2.5 1.5 3.17157 1.5 4V10C1.5 10.8284 2.17157 11.5 3 11.5H3.75H4.5V12.25V13.1004L6.56675 11.6378L6.76146 11.5H7H13C13.8284 11.5 14.5 10.8284 14.5 10V4C14.5 3.17157 13.8284 2.5 13 2.5H3ZM0 4C0 2.34315 1.34315 1 3 1H13C14.6569 1 16 2.34315 16 4V10C16 11.6569 14.6569 13 13 13H7.23854L4.18325 15.1622L3 15.9996V14.55V13C1.34315 13 0 11.6569 0 10V4Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg
|
||||
width="18"
|
||||
height="16"
|
||||
viewBox="0 0 18 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M11.4998 3.75H12.2498V3V2.16667C12.2498 1.66177 12.6582 1.25 13.1665 1.25H15.6665C16.1714 1.25 16.5832 1.65832 16.5832 2.16667V5.5C16.5832 6.0049 16.1749 6.41667 15.6665 6.41667H13.1665C12.6641 6.41667 12.2498 6.00245 12.2498 5.5V4.66667V3.91667H11.4998H9.83317H9.08317V4.66667V10.5083C9.08317 11.3725 9.79396 12.0833 10.6582 12.0833H11.4998H12.2498V11.3333V10.5C12.2498 9.9951 12.6582 9.58333 13.1665 9.58333H15.6665C16.1714 9.58333 16.5832 9.99165 16.5832 10.5V13.8333C16.5832 14.3382 16.1749 14.75 15.6665 14.75H13.1665C12.6616 14.75 12.2498 14.3417 12.2498 13.8333V13V12.25H11.4998H10.6582C9.69738 12.25 8.9165 11.4691 8.9165 10.5083V4.66667V3.91667H8.1665H6.49984H5.74984V4.66667V5.5C5.74984 6.0049 5.34152 6.41667 4.83317 6.41667H2.33317C1.82827 6.41667 1.4165 6.00835 1.4165 5.5V2.16667C1.4165 1.66421 1.83072 1.25 2.33317 1.25H4.8415C5.3464 1.25 5.75817 1.65832 5.75817 2.16667V3V3.75H6.50817H11.4998Z"
|
||||
/>
|
||||
</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,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="38"
|
||||
height="37"
|
||||
viewBox="0 0 38 37"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M30.5695 7.84167C30.4459 7.87905 30.4465 8.05327 30.5704 8.08974L30.6008 8.09869C32.0949 8.53857 33.2612 9.70436 33.6956 11.1923C33.7314 11.3148 33.9059 11.3148 33.9425 11.1925C34.386 9.71452 35.5462 8.55207 37.0303 8.10336L37.0652 8.09282C37.1885 8.05553 37.1877 7.88161 37.064 7.84544L37.0283 7.835C35.5338 7.39799 34.3679 6.23128 33.938 4.74249C33.9028 4.62035 33.7287 4.62043 33.6922 4.7422C33.249 6.21893 32.0893 7.38202 30.6065 7.83048L30.5695 7.84167ZM31.4317 23.8166L31.4317 14.0863H31.4318V10.3168H31.4317V10.3162L26.998 10.3162V10.3168H17.2469L11.255 16.849V26.5794H11.2549V30.349H11.255V30.3494H15.6888V30.349L25.4397 30.349L31.4317 23.8166ZM26.998 26.5794L26.998 14.0863H15.6888L15.6888 26.5794H26.998Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<svg
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 36 36"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--twemoji"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<path
|
||||
stroke="#000"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
d="M36 11a2 2 0 0 0-4 0s-.011 3.285-3 3.894V12c0-6.075-4.925-11-11-11S7 5.925 7 12v3.237C1.778 16.806 0 23.231 0 27a2 2 0 0 0 4 0s.002-3.54 3.336-3.958C7.838 27.883 8.954 33 11 33h1c4 0 3 2 7 2s3-2 6-2s2.395 2 6 2a3 3 0 0 0 3-3c0-.675-2.274-4.994-3.755-9.268C35.981 21.348 36 14.58 36 11z"
|
||||
></path>
|
||||
<circle fill="#000" stroke-width="1" cx="13" cy="12" r="2"></circle>
|
||||
<circle fill="#000" cx="23" cy="12" r="3"></circle>
|
||||
<path
|
||||
stroke="#000"
|
||||
stroke-width="2"
|
||||
fill="none"
|
||||
d="M22.192 19.491c2.65 1.987 3.591 5.211 2.1 7.199c-1.491 1.988-4.849 1.988-7.5 0c-2.65-1.987-3.591-5.211-2.1-7.199c1.492-1.989 4.849-1.988 7.5 0z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 13V4.5C8 4.10218 8.15804 3.72064 8.43934 3.43934C8.72064 3.15804 9.10218 3 9.5 3C9.89782 3 10.2794 3.15804 10.5607 3.43934C10.842 3.72064 11 4.10218 11 4.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11 11.5V9.5C11 9.10218 11.158 8.72064 11.4393 8.43934C11.7206 8.15804 12.1022 8 12.5 8C12.8978 8 13.2794 8.15804 13.5607 8.43934C13.842 8.72064 14 9.10218 14 9.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 10.5C14 10.1022 14.158 9.72064 14.4393 9.43934C14.7206 9.15804 15.1022 9 15.5 9C15.8978 9 16.2794 9.15804 16.5607 9.43934C16.842 9.72064 17 10.1022 17 10.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M17.0002 11.5C17.0002 11.1022 17.1582 10.7206 17.4395 10.4393C17.7208 10.158 18.1024 10 18.5002 10C18.898 10 19.2795 10.158 19.5608 10.4393C19.8421 10.7206 20.0002 11.1022 20.0002 11.5V16C20.0002 17.5913 19.368 19.1174 18.2428 20.2426C17.1176 21.3679 15.5915 22 14.0002 22H12.0002H12.2082C11.2145 22.0002 10.2364 21.7535 9.36157 21.2823C8.48676 20.811 7.7427 20.1299 7.19618 19.3L7.00018 19C6.68818 18.521 5.59318 16.612 3.71418 13.272C3.52263 12.9315 3.47147 12.5298 3.57157 12.1522C3.67166 11.7745 3.91513 11.4509 4.25018 11.25C4.60706 11.0359 5.02526 10.9471 5.43834 10.9978C5.85143 11.0486 6.23572 11.2359 6.53018 11.53L8.00018 13"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M2.54102 5.59497C3.30843 5.03394 4.13302 4.55561 5.00102 4.16797"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 3.45703C15.32 3.81103 16.558 4.35903 17.685 5.06903"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<svg
|
||||
width="25"
|
||||
height="24"
|
||||
viewBox="0 0 25 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.5 13V4.5C8.5 4.10218 8.65804 3.72064 8.93934 3.43934C9.22064 3.15804 9.60218 3 10 3C10.3978 3 10.7794 3.15804 11.0607 3.43934C11.342 3.72064 11.5 4.10218 11.5 4.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.5 11.5V9.5C11.5 9.10218 11.658 8.72064 11.9393 8.43934C12.2206 8.15804 12.6022 8 13 8C13.3978 8 13.7794 8.15804 14.0607 8.43934C14.342 8.72064 14.5 9.10218 14.5 9.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.5 10.5C14.5 10.1022 14.658 9.72064 14.9393 9.43934C15.2206 9.15804 15.6022 9 16 9C16.3978 9 16.7794 9.15804 17.0607 9.43934C17.342 9.72064 17.5 10.1022 17.5 10.5V12"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M17.5002 11.5C17.5002 11.1022 17.6582 10.7206 17.9395 10.4393C18.2208 10.158 18.6024 10 19.0002 10C19.398 10 19.7795 10.158 20.0608 10.4393C20.3421 10.7206 20.5002 11.1022 20.5002 11.5V16C20.5002 17.5913 19.868 19.1174 18.7428 20.2426C17.6176 21.3679 16.0915 22 14.5002 22H12.5002H12.7082C11.7145 22.0002 10.7364 21.7535 9.86157 21.2823C8.98676 20.811 8.2427 20.1299 7.69618 19.3L7.50018 19C7.18818 18.521 6.09318 16.612 4.21418 13.272C4.02263 12.9315 3.97147 12.5298 4.07157 12.1522C4.17166 11.7745 4.41513 11.4509 4.75018 11.25C5.10706 11.0359 5.52526 10.9471 5.93834 10.9978C6.35143 11.0486 6.73572 11.2359 7.03018 11.53L8.50018 13"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.5 3L4.5 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.5 7H3.5"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14.5 3L15.5 2"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.5 6H16.5"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_614_5022)">
|
||||
<path
|
||||
d="M11 14V5.5C11 5.10218 11.158 4.72064 11.4393 4.43934C11.7206 4.15804 12.1022 4 12.5 4C12.8978 4 13.2794 4.15804 13.5607 4.43934C13.842 4.72064 14 5.10218 14 5.5V13"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 12.5V10.5C14 10.303 14.0388 10.108 14.1142 9.92597C14.1896 9.74399 14.3001 9.57863 14.4393 9.43934C14.5786 9.30005 14.744 9.18956 14.926 9.11418C15.108 9.0388 15.303 9 15.5 9C15.697 9 15.892 9.0388 16.074 9.11418C16.256 9.18956 16.4214 9.30005 16.5607 9.43934C16.6999 9.57863 16.8104 9.74399 16.8858 9.92597C16.9612 10.108 17 10.303 17 10.5V13"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M17 11.5C17 11.1022 17.158 10.7206 17.4393 10.4393C17.7206 10.158 18.1022 10 18.5 10C18.8978 10 19.2794 10.158 19.5607 10.4393C19.842 10.7206 20 11.1022 20 11.5V13"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M20.0002 12.5C20.0002 12.1022 20.1582 11.7206 20.4395 11.4393C20.7208 11.158 21.1024 11 21.5002 11C21.898 11 22.2795 11.158 22.5608 11.4393C22.8421 11.7206 23.0002 12.1022 23.0002 12.5V17C23.0002 18.5913 22.368 20.1174 21.2428 21.2426C20.1176 22.3679 18.5915 23 17.0002 23H15.0002H15.2082C14.2145 23.0002 13.2364 22.7535 12.3616 22.2823C11.4868 21.811 10.7427 21.1299 10.1962 20.3C10.1306 20.2002 10.0653 20.1002 10.0002 20C9.68818 19.521 8.59318 17.612 6.71418 14.272C6.52263 13.9315 6.47147 13.5298 6.57157 13.1522C6.67166 12.7745 6.91513 12.4509 7.25018 12.25C7.60706 12.0359 8.02526 11.9471 8.43834 11.9978C8.85143 12.0486 9.23572 12.2359 9.53018 12.53L11.0002 14"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.750039 6.36034L3.75004 6.36034L3.75004 9.36034"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.75068 6.36101C1.75068 8.36101 -0.249322 8.89698 2.25068 11.361"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.24999 0.750039L5.24999 3.75004L8.24999 3.75004"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.24969 3.7497C7.24969 1.7497 7.78565 -0.250299 10.2497 2.2497"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_614_5022">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.5 23.9655V3.00003L10.5 10.8621V31.5L1.5 23.9655Z"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 3.00003L22.5 1.50003"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 24L22.5 22.5"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M10.5 31.5L31.5 30"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.5 11.25L31.5 9.75003"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22.5 1.50003L31.5 9.34814V30"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M22.5 1.50003V22.5"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M22.5 22.5L31.5 30"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M6.04926 17.378L27.0493 15.878"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.37823 19.3902L7.10927 15.3896"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M25.6345 17.8902L28.3656 13.8896"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4.96887 14.093L7.03114 20.663"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M26.2251 12.593L28.2874 19.163"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M3 25.4655V4.50003L12 12.3621V33L3 25.4655Z"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 4.50003L24 3.00003"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 25.5L24 24"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M12 33L33 31.5"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12 12.75L33 11.25"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M24 3.00003L33 10.8481V31.5"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M24 3.00003V24"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M24 24L33 31.5"
|
||||
stroke="#CBD5E1"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
<path
|
||||
d="M3 25.5L33 10.5"
|
||||
stroke="#3B82F6"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<circle cx="3" cy="25.5" r="2.25" fill="#3B82F6" />
|
||||
<circle cx="33" cy="10.5" r="2.25" fill="#3B82F6" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="icon icon-tabler icon-tabler-ruler-measure"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M19.875 12c.621 0 1.125 .512 1.125 1.143v5.714c0 .631 -.504 1.143 -1.125 1.143h-15.875a1 1 0 0 1 -1 -1v-5.857c0 -.631 .504 -1.143 1.125 -1.143h15.75z"
|
||||
/>
|
||||
<path d="M9 12v2" />
|
||||
<path d="M6 12v3" />
|
||||
<path d="M12 12v3" />
|
||||
<path d="M18 12v3" />
|
||||
<path d="M15 12v2" />
|
||||
<path d="M3 3v4" />
|
||||
<path d="M3 5h18" />
|
||||
<path d="M21 3v4" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19 11.5C19 17.299 17.5 22 12.5 22C7.5 22 6 17.299 6 11.5C6 5.70101 6 1 12.5 1C19 1 19 5.70101 19 11.5Z"
|
||||
stroke="#94A3B8"
|
||||
/>
|
||||
<path d="M6 9H19" stroke="#94A3B8" />
|
||||
<path d="M19 9C19 6.5 19 1 12.5 1" stroke="#334155" />
|
||||
<path d="M19.5 9H12" stroke="#334155" />
|
||||
<path d="M12.5 0.5V9.5" stroke="#334155" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19 11.5C19 17.299 17.5 22 12.5 22C7.5 22 6 17.299 6 11.5C6 5.70101 6 1 12.5 1C19 1 19 5.70101 19 11.5Z"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
<path d="M6 9C6 6.5 6 1 12.5 1" stroke="#334155" />
|
||||
<path d="M6 9H19" stroke="#94A3B8" />
|
||||
<path d="M5.5 9H13" stroke="#334155" />
|
||||
<path d="M12.5 0.5V9.5" stroke="#334155" />
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<svg
|
||||
width="25"
|
||||
height="24"
|
||||
viewBox="0 0 25 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19.5 11.5C19.5 17.299 18 22 13 22C8 22 6.5 17.299 6.5 11.5C6.5 5.70101 6.5 1 13 1C19.5 1 19.5 5.70101 19.5 11.5Z"
|
||||
stroke="currentColor"
|
||||
/>
|
||||
<path d="M6.5 9H19.5" stroke="#94A3B8" />
|
||||
<rect
|
||||
x="11.5"
|
||||
y="2.5"
|
||||
width="3"
|
||||
height="5"
|
||||
rx="1"
|
||||
stroke="#334155"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M4.16699 4.16667C4.16699 3.72464 4.34259 3.30072 4.65515 2.98816C4.96771 2.67559 5.39163 2.5 5.83366 2.5H14.167C14.609 2.5 15.0329 2.67559 15.3455 2.98816C15.6581 3.30072 15.8337 3.72464 15.8337 4.16667V15.8333C15.8337 16.2754 15.6581 16.6993 15.3455 17.0118C15.0329 17.3244 14.609 17.5 14.167 17.5H5.83366C5.39163 17.5 4.96771 17.3244 4.65515 17.0118C4.34259 16.6993 4.16699 16.2754 4.16699 15.8333V4.16667Z"
|
||||
stroke="#334155"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.5 5.8335H12.5"
|
||||
stroke="#334155"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.5 9.1665H12.5"
|
||||
stroke="#334155"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.5 12.5H10.8333"
|
||||
stroke="#334155"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.648 8.90476L13.784 15.381H2.224L4.352 8.90476H11.648ZM4 0L0.8 3.2381L4 6.47619V4.04762H7.2V2.42857H4V0ZM12 0V2.42857H8.8V4.04762H12V6.47619L15.2 3.2381L12 0ZM12.8 7.28571H3.2L0 17H16L12.8 7.28571Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="17"
|
||||
viewBox="0 0 16 17"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.648 8.90476L13.784 15.381H2.224L4.352 8.90476H11.648ZM12 0L8.8 3.2381L12 6.47619V4.04762H15.2V2.42857H12V0ZM4 0V2.42857H0.8V4.04762H4V6.47619L7.2 3.2381L4 0ZM12.8 7.28571H3.2L0 17H16L12.8 7.28571Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_2965_95098)">
|
||||
<path
|
||||
d="M12.6611 7.51562C13.0107 7.71744 13.0326 8.20331 12.7266 8.44043L12.6611 8.48437L4.58887 13.1445C4.21624 13.3597 3.75025 13.0913 3.75 12.6611V3.33887C3.75023 2.93564 4.15941 2.67432 4.51758 2.82031L4.58887 2.85547L12.6611 7.51562Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2965_95098">
|
||||
<rect width="16" height="16" fill="currentColor" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M8 3V13M3 8H13" stroke="currentColor" stroke-width="1.5" />
|
||||
</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,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M3 2.5H5C5.27614 2.5 5.5 2.72386 5.5 3V5C5.5 5.27614 5.27614 5.5 5 5.5H3C2.72386 5.5 2.5 5.27614 2.5 5V3C2.5 2.72386 2.72386 2.5 3 2.5ZM1 3C1 1.89543 1.89543 1 3 1H5C6.10457 1 7 1.89543 7 3V5C7 6.10457 6.10457 7 5 7H3C1.89543 7 1 6.10457 1 5V3ZM3 10.5H5C5.27614 10.5 5.5 10.7239 5.5 11V13C5.5 13.2761 5.27614 13.5 5 13.5H3C2.72386 13.5 2.5 13.2761 2.5 13V11C2.5 10.7239 2.72386 10.5 3 10.5ZM1 11C1 9.89543 1.89543 9 3 9H5C6.10457 9 7 9.89543 7 11V13C7 14.1046 6.10457 15 5 15H3C1.89543 15 1 14.1046 1 13V11ZM13 2.5H11C10.7239 2.5 10.5 2.72386 10.5 3V5C10.5 5.27614 10.7239 5.5 11 5.5H13C13.2761 5.5 13.5 5.27614 13.5 5V3C13.5 2.72386 13.2761 2.5 13 2.5ZM11 1C9.89543 1 9 1.89543 9 3V5C9 6.10457 9.89543 7 11 7H13C14.1046 7 15 6.10457 15 5V3C15 1.89543 14.1046 1 13 1H11ZM11 10.5H13C13.2761 10.5 13.5 10.7239 13.5 11V13C13.5 13.2761 13.2761 13.5 13 13.5H11C10.7239 13.5 10.5 13.2761 10.5 13V11C10.5 10.7239 10.7239 10.5 11 10.5ZM9 11C9 9.89543 9.89543 9 11 9H13C14.1046 9 15 9.89543 15 11V13C15 14.1046 14.1046 15 13 15H11C9.89543 15 9 14.1046 9 13V11Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 7H4C2.89543 7 2 6.10457 2 5C2 3.89543 2.896 3 4.00057 3H11.9994C13.104 3 14 3.89543 14 5C14 6.10457 13.1046 7 12 7ZM5 5C5 5.55228 4.55228 6 4 6C3.44772 6 3 5.55228 3 5C3 4.44772 3.44772 4 4 4C4.55228 4 5 4.44772 5 5ZM2 12V10C2 8.89543 2.896 8 4.00057 8H11.9994C13.104 8 14 8.89543 14 10V12C14 13.1046 13.1046 14 12 14H4C2.89543 14 2 13.1046 2 12ZM5 10C5 10.5523 4.55228 11 4 11C3.44772 11 3 10.5523 3 10C3 9.44772 3.44772 9 4 9C4.55228 9 5 9.44772 5 10Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M5 15V1H6.5V15H5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M10.9002 8.28183C11.0333 8.1154 11.0333 7.8846 10.9002 7.71817L8.87389 5.18369C8.59132 4.83026 8 5.02096 8 5.46552V10.5345C8 10.979 8.59132 11.1697 8.87389 10.8163L10.9002 8.28183Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<rect
|
||||
x="0.75"
|
||||
y="0.75"
|
||||
width="14.5"
|
||||
height="14.5"
|
||||
rx="3.25"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11 15V1H9.5V15H11Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M5.0998 8.28183C4.96673 8.1154 4.96673 7.8846 5.0998 7.71817L7.12611 5.18369C7.40868 4.83026 8 5.02096 8 5.46552L8 10.5345C8 10.979 7.40868 11.1697 7.12611 10.8163L5.0998 8.28183Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<rect
|
||||
x="-0.75"
|
||||
y="0.75"
|
||||
width="14.5"
|
||||
height="14.5"
|
||||
rx="3.25"
|
||||
transform="matrix(-1 0 0 1 14.5 0)"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.64645 8.35355C9.84171 8.15829 9.84171 7.84171 9.64645 7.64645L6.85355 4.85355C6.53857 4.53857 6 4.76165 6 5.20711V10.7929C6 11.2383 6.53857 11.4614 6.85355 11.1464L9.64645 8.35355Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<svg
|
||||
width="18"
|
||||
height="16"
|
||||
viewBox="0 0 18 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M1.5 13.8154C2.64014 13.1571 3.93347 12.8105 5.25 12.8105C6.56652 12.8105 7.85986 13.1571 9 13.8154C10.1401 13.1571 11.4335 12.8105 12.75 12.8105C14.0665 12.8105 15.3599 13.1571 16.5 13.8154"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 2.98137C2.64014 2.32311 3.93347 1.97656 5.25 1.97656C6.56652 1.97656 7.85986 2.32311 9 2.98137C10.1401 2.32311 11.4335 1.97656 12.75 1.97656C14.0665 1.97656 15.3599 2.32311 16.5 2.98137"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M1.5 2.98242V13.8158"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M9 2.98242V13.8158"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16.5 2.98242V13.8158"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M14.5 8C14.5 11.5899 11.5899 14.5 8 14.5C4.41015 14.5 1.5 11.5899 1.5 8C1.5 4.41015 4.41015 1.5 8 1.5C11.5899 1.5 14.5 4.41015 14.5 8ZM16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8ZM8.75 3.75C8.75 3.33579 8.41421 3 8 3C7.58579 3 7.25 3.33579 7.25 3.75V8V8.31066L7.46967 8.53033L9.72358 10.7842C10.0165 11.0771 10.4913 11.0771 10.7842 10.7842C11.0771 10.4913 11.0771 10.0165 10.7842 9.72358L8.75 7.68934V3.75Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</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="M8 7H6L3 15V17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16 7H18L21 15V17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M10 16H14"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M14 16.5C14 17.4283 14.3687 18.3185 15.0251 18.9749C15.6815 19.6313 16.5717 20 17.5 20C18.4283 20 19.3185 19.6313 19.9749 18.9749C20.6313 18.3185 21 17.4283 21 16.5C21 15.5717 20.6313 14.6815 19.9749 14.0251C19.3185 13.3687 18.4283 13 17.5 13C16.5717 13 15.6815 13.3687 15.0251 14.0251C14.3687 14.6815 14 15.5717 14 16.5Z"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3 16.5C3 16.9596 3.09053 17.4148 3.26642 17.8394C3.44231 18.264 3.70012 18.6499 4.02513 18.9749C4.35013 19.2999 4.73597 19.5577 5.16061 19.7336C5.58525 19.9095 6.04037 20 6.5 20C6.95963 20 7.41475 19.9095 7.83939 19.7336C8.26403 19.5577 8.64987 19.2999 8.97487 18.9749C9.29988 18.6499 9.55769 18.264 9.73358 17.8394C9.90947 17.4148 10 16.9596 10 16.5C10 16.0404 9.90947 15.5852 9.73358 15.1606C9.55769 14.736 9.29988 14.3501 8.97487 14.0251C8.64987 13.7001 8.26403 13.4423 7.83939 13.2664C7.41475 13.0905 6.95963 13 6.5 13C6.04037 13 5.58525 13.0905 5.16061 13.2664C4.73597 13.4423 4.35013 13.7001 4.02513 14.0251C3.70012 14.3501 3.44231 14.736 3.26642 15.1606C3.09053 15.5852 3 16.0404 3 16.5Z"
|
||||
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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_197_13852)">
|
||||
<path
|
||||
d="M4.876 13.61C4.28624 13.9796 3.80311 14.4966 3.47436 15.1101C3.14561 15.7235 2.98261 16.4121 3.00147 17.1079C3.02033 17.8036 3.2204 18.4824 3.58191 19.0771C3.94341 19.6719 4.45385 20.162 5.06277 20.4991C5.67169 20.8361 6.35802 21.0085 7.05395 20.9991C7.74988 20.9897 8.43131 20.7989 9.03092 20.4455C9.63053 20.0922 10.1276 19.5885 10.4729 18.9842C10.8182 18.3799 10.9999 17.696 11 17H17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.066 20.502C15.6003 20.7969 16.1949 20.9656 16.8045 20.9953C17.414 21.0249 18.0222 20.9147 18.5826 20.6731C19.143 20.4315 19.6406 20.0649 20.0375 19.6013C20.4344 19.1377 20.7199 18.5895 20.8722 17.9985C21.0246 17.4076 21.0397 16.7897 20.9164 16.192C20.7931 15.5943 20.5348 15.0328 20.161 14.5504C19.7873 14.0679 19.3082 13.6774 18.7603 13.4087C18.2124 13.14 17.6102 13.0002 17 13C16.294 13 15.576 13.179 15 13.5L12 8"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16 8C16 6.93913 15.5786 5.92172 14.8284 5.17157C14.0783 4.42143 13.0609 4 12 4C10.9391 4 9.92172 4.42143 9.17157 5.17157C8.42143 5.92172 8 6.93913 8 8C8 9.506 8.77 10.818 10 11.5L7 17"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_197_13852">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M4.00065 8.66667C2.53398 8.66667 1.33398 9.86667 1.33398 11.3333C1.33398 12.8 2.53398 14 4.00065 14C5.46732 14 6.66732 12.8 6.66732 11.3333C6.66732 9.86667 5.46732 8.66667 4.00065 8.66667ZM8.00065 2C6.53398 2 5.33398 3.2 5.33398 4.66667C5.33398 6.13333 6.53398 7.33333 8.00065 7.33333C9.46732 7.33333 10.6673 6.13333 10.6673 4.66667C10.6673 3.2 9.46732 2 8.00065 2ZM12.0007 8.66667C10.534 8.66667 9.33398 9.86667 9.33398 11.3333C9.33398 12.8 10.534 14 12.0007 14C13.4673 14 14.6673 12.8 14.6673 11.3333C14.6673 9.86667 13.4673 8.66667 12.0007 8.66667Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-foundation text-foreground rounded-lg w-8 md:w-10 flex flex-col justify-center items-center md:gap-1 border border-outline-2 shadow"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
+5
-4
@@ -1,10 +1,13 @@
|
||||
<template>
|
||||
<button
|
||||
:class="`transition rounded-lg w-10 h-10 flex items-center justify-center ${shadowClasses} ${colorClasses} active:scale-[0.9] outline-none`"
|
||||
:class="`transition rounded-lg w-8 md:w-10 h-8 md:h-10 shrink-0 flex items-center justify-center ${colorClasses} outline-none ${
|
||||
props.flat ? '!w-7 md:!w-9' : 'border border-outline-2 w-8 md:w-10 shadow'
|
||||
}`"
|
||||
>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -14,13 +17,11 @@ const props = defineProps<{
|
||||
secondary?: boolean
|
||||
}>()
|
||||
|
||||
const shadowClasses = computed(() => (props.flat ? '' : 'shadow-md'))
|
||||
|
||||
const colorClasses = computed(() => {
|
||||
const parts = []
|
||||
if (props.active) {
|
||||
if (props.secondary) parts.push('bg-foundation text-primary')
|
||||
else parts.push('bg-primary text-foreground-on-primary')
|
||||
else parts.push('bg-primary text-foreground-on-primary border-primary')
|
||||
} else {
|
||||
parts.push('bg-foundation text-foreground')
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div ref="menuWrapper" class="relative z-30">
|
||||
<ViewerControlsButtonToggle
|
||||
:v-tippy="tooltip"
|
||||
flat
|
||||
secondary
|
||||
:active="open"
|
||||
@click="toggleMenu"
|
||||
>
|
||||
<slot name="trigger-icon" />
|
||||
</ViewerControlsButtonToggle>
|
||||
<div
|
||||
v-if="open"
|
||||
ref="menuContent"
|
||||
class="absolute left-10 sm:left-[46px] -top-0 bg-foundation rounded-md border border-outline-2 flex flex-col overflow-hidden shadow"
|
||||
>
|
||||
<div
|
||||
v-if="$slots.title"
|
||||
class="flex items-center py-2 px-2 border-b border-outline-2 sticky top-0 z-50 bg-foundation"
|
||||
>
|
||||
<div class="flex items-center text-body-2xs text-foreground font-medium">
|
||||
<span class="truncate flex-1">
|
||||
<slot name="title"></slot>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-h-68 simple-scrollbar overflow-y-auto">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onClickOutside } from '@vueuse/core'
|
||||
import { computed, ref } from 'vue'
|
||||
import ViewerControlsButtonToggle from '../controls/ViewerControlsButtonToggle.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
tooltip?: string
|
||||
open: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', value: boolean): void
|
||||
}>()
|
||||
|
||||
const open = computed({
|
||||
get: () => props.open,
|
||||
set: (val) => emit('update:open', val)
|
||||
})
|
||||
|
||||
const menuContent = ref<HTMLElement | null>(null)
|
||||
const menuWrapper = ref<HTMLElement | null>(null)
|
||||
|
||||
const toggleMenu = () => {
|
||||
open.value = !open.value
|
||||
}
|
||||
|
||||
onClickOutside(
|
||||
menuContent,
|
||||
(event) => {
|
||||
if (!menuWrapper.value?.contains(event.target as Node)) {
|
||||
open.value = false
|
||||
}
|
||||
},
|
||||
{ ignore: [menuWrapper] }
|
||||
)
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<button
|
||||
:v-tippy="description ? description : undefined"
|
||||
class="flex items-center justify-between hover:bg-highlight-1 text-foreground w-full h-full text-body-2xs py-1.5 pr-2 pl-1 rounded-md"
|
||||
:class="{ 'bg-highlight-1': active }"
|
||||
>
|
||||
<div v-if="!hideActiveTick" class="w-5">
|
||||
<Check v-if="active" class="h-4 w-4 text-foreground-2" />
|
||||
</div>
|
||||
<div class="flex-1 text-left">{{ label }}</div>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Check from '../../global/icon/Check.vue'
|
||||
defineProps<{
|
||||
label: string
|
||||
description?: string
|
||||
active?: boolean
|
||||
hideActiveTick?: boolean
|
||||
shortcut?: string
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,85 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<ViewerMenu v-model:open="open" tooltip="View modes">
|
||||
<template #trigger-icon>
|
||||
<ViewModes class="h-5 w-5" />
|
||||
</template>
|
||||
<template #title>View modes</template>
|
||||
<div
|
||||
class="p-1.5"
|
||||
@mouseenter="cancelCloseTimer"
|
||||
@mouseleave="isManuallyOpened ? undefined : startCloseTimer"
|
||||
@focusin="cancelCloseTimer"
|
||||
@focusout="isManuallyOpened ? undefined : startCloseTimer"
|
||||
>
|
||||
<div v-for="(label, mode) in viewModes" :key="mode">
|
||||
<ViewerMenuItem
|
||||
:label="label"
|
||||
:active="mode.toString() === visualStore.defaultViewModeInFile"
|
||||
@click="handleViewModeChange(Number(mode))"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ViewerMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTimeoutFn } from '@vueuse/core'
|
||||
import { ViewMode } from '@speckle/viewer'
|
||||
import ViewerMenu from '../menu/ViewerMenu.vue'
|
||||
import ViewerMenuItem from '../menu/ViewerMenuItem.vue'
|
||||
import { onUnmounted, ref, computed, onMounted } from 'vue'
|
||||
import { useVisualStore } from '@src/store/visualStore'
|
||||
import ViewModes from '../../global/icon/ViewModes.vue'
|
||||
|
||||
const viewModes = {
|
||||
[ViewMode.DEFAULT]: 'Default',
|
||||
[ViewMode.DEFAULT_EDGES]: 'Edges',
|
||||
[ViewMode.SHADED]: 'Shaded',
|
||||
[ViewMode.PEN]: 'Pen',
|
||||
[ViewMode.ARCTIC]: 'Arctic',
|
||||
[ViewMode.COLORS]: 'Colors'
|
||||
}
|
||||
|
||||
const visualStore = useVisualStore()
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
}>()
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', value: boolean): void
|
||||
(e: 'force-close-others'): void
|
||||
(e: 'view-mode-clicked', value: ViewMode): void
|
||||
}>()
|
||||
|
||||
// Computed v-model
|
||||
const open = computed({
|
||||
get: () => props.open,
|
||||
set: (val) => emit('update:open', val)
|
||||
})
|
||||
|
||||
// State
|
||||
const isManuallyOpened = ref(false)
|
||||
|
||||
const { start: startCloseTimer, stop: cancelCloseTimer } = useTimeoutFn(
|
||||
() => {
|
||||
open.value = false
|
||||
},
|
||||
3000,
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
const handleViewModeChange = (mode: ViewMode) => {
|
||||
open.value = false
|
||||
visualStore.setDefaultViewModeInFile(mode.toString())
|
||||
visualStore.writeViewModeToFile(mode)
|
||||
emit('view-mode-clicked', mode)
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelCloseTimer()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,88 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<ViewerMenu v-model:open="open" tooltip="Views">
|
||||
<template #trigger-icon>
|
||||
<Views class="w-5 h-5" />
|
||||
</template>
|
||||
<template #title>Views</template>
|
||||
<div
|
||||
class="max-h-64 simple-scrollbar overflow-y-auto flex flex-col p-1.5"
|
||||
@mouseenter="cancelCloseTimer"
|
||||
@mouseleave="isManuallyOpened ? undefined : startCloseTimer"
|
||||
@focusin="cancelCloseTimer"
|
||||
@focusout="isManuallyOpened ? undefined : startCloseTimer"
|
||||
>
|
||||
<div v-for="shortcut in viewShortcuts" :key="shortcut.name">
|
||||
<ViewerMenuItem
|
||||
:label="shortcut.name"
|
||||
hide-active-tick
|
||||
:active="activeView === shortcut.name.toLowerCase()"
|
||||
@click="handleViewChange(shortcut.name.toLowerCase() as CanonicalView)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="views.length !== 0" class="w-full border-b my-1"></div>
|
||||
|
||||
<ViewerMenuItem
|
||||
v-for="view in views"
|
||||
:key="view.id"
|
||||
hide-active-tick
|
||||
:active="activeView === view.id"
|
||||
:label="view.name ? view.name : view.id"
|
||||
@click="handleViewChange(view)"
|
||||
/>
|
||||
</div>
|
||||
</ViewerMenu>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useTimeoutFn } from '@vueuse/core'
|
||||
import type { CanonicalView, SpeckleView } from '@speckle/viewer'
|
||||
import { onUnmounted, ref, computed } from 'vue'
|
||||
import ViewerMenu from '../menu/ViewerMenu.vue'
|
||||
import ViewerMenuItem from '../menu/ViewerMenuItem.vue'
|
||||
import Views from '../../global/icon/Views.vue'
|
||||
import { ViewShortcuts } from '../../../helpers/viewer/shortcuts/shortcuts'
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
views: SpeckleView[]
|
||||
open: boolean
|
||||
}>()
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', value: boolean): void
|
||||
(e: 'force-close-others'): void
|
||||
(e: 'view-clicked', value: CanonicalView | SpeckleView)
|
||||
}>()
|
||||
|
||||
// Computed open for v-model
|
||||
const open = computed({
|
||||
get: () => props.open,
|
||||
set: (val) => emit('update:open', val)
|
||||
})
|
||||
|
||||
// State
|
||||
const isManuallyOpened = ref(false)
|
||||
const activeView = ref<string | null>(null)
|
||||
|
||||
const { start: startCloseTimer, stop: cancelCloseTimer } = useTimeoutFn(
|
||||
() => {
|
||||
open.value = false
|
||||
},
|
||||
3000,
|
||||
{ immediate: false }
|
||||
)
|
||||
|
||||
const handleViewChange = (v: CanonicalView | SpeckleView) => {
|
||||
open.value = false
|
||||
emit('view-clicked', v)
|
||||
}
|
||||
|
||||
const viewShortcuts = Object.values(ViewShortcuts)
|
||||
|
||||
onUnmounted(() => {
|
||||
cancelCloseTimer()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="flex shrink-0 overflow-hidden rounded-md border border-outline-2 bg-foundation-2">
|
||||
<div
|
||||
class="h-full w-full bg-cover bg-center bg-no-repeat flex items-center justify-center"
|
||||
:style="logo ? { backgroundImage: `url('${logo}')` } : {}"
|
||||
>
|
||||
<span v-if="!logo" class="text-foreground-3 uppercase leading-none">
|
||||
{{ name[0] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
logo: string | undefined | null
|
||||
name: string
|
||||
}>()
|
||||
</script>
|
||||
@@ -36,7 +36,7 @@ export default class TooltipHandler {
|
||||
tooltip: tooltipData
|
||||
}
|
||||
|
||||
this.tooltipService.show(tooltipData)
|
||||
// this.tooltipService.show(tooltipData)
|
||||
if (Object.keys(tooltipData.dataItems).length > 0) this.tooltipService.show(tooltipData)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type { ConcreteComponent, FunctionalComponent, DefineComponent } from 'vue'
|
||||
|
||||
export type PropAnyComponent =
|
||||
| ConcreteComponent<any, any, any, any, any>
|
||||
| FunctionalComponent<any, any, any>
|
||||
| DefineComponent
|
||||
| string
|
||||
|
||||
export type HorizontalOrVertical = 'horizontal' | 'vertical'
|
||||
|
||||
export interface StepCoreType {
|
||||
name: string
|
||||
href?: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export type BulletStepType = StepCoreType
|
||||
|
||||
export interface NumberStepType extends BulletStepType {
|
||||
description?: string
|
||||
}
|
||||
|
||||
export type AlertColor = 'success' | 'danger' | 'warning' | 'info' | 'neutral'
|
||||
|
||||
export type AlertAction = {
|
||||
title: string
|
||||
url?: string
|
||||
onClick?: () => void
|
||||
externalUrl?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export type FormButtonStyle = 'primary' | 'outline' | 'subtle' | 'danger'
|
||||
export type FormButtonSize = 'sm' | 'base' | 'lg'
|
||||
@@ -0,0 +1,152 @@
|
||||
import { ViewMode } from '@speckle/viewer'
|
||||
|
||||
export enum ModifierKeys {
|
||||
CtrlOrCmd = 'cmd-or-ctrl',
|
||||
AltOrOpt = 'alt-or-opt',
|
||||
Shift = 'shift'
|
||||
}
|
||||
|
||||
export const PanelShortcuts = {
|
||||
ToggleModels: {
|
||||
name: 'Models',
|
||||
description: 'Toggle models panel',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'M',
|
||||
action: 'ToggleModels'
|
||||
},
|
||||
ToggleExplorer: {
|
||||
name: 'Scene explorer',
|
||||
description: 'Toggle scene explorer panel',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'E',
|
||||
action: 'ToggleExplorer'
|
||||
},
|
||||
ToggleDiscussions: {
|
||||
name: 'Discussions',
|
||||
description: 'Toggle discussions panel',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'D',
|
||||
action: 'ToggleDiscussions'
|
||||
}
|
||||
} as const
|
||||
|
||||
export const ToolShortcuts = {
|
||||
ToggleMeasurements: {
|
||||
name: 'Measure',
|
||||
description: 'Toggle measurement mode',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'R',
|
||||
action: 'ToggleMeasurements'
|
||||
},
|
||||
ToggleProjection: {
|
||||
name: 'Projection',
|
||||
description: 'Toggle between orthographic and perspective projection',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'P',
|
||||
action: 'ToggleProjection'
|
||||
},
|
||||
ToggleSectionBox: {
|
||||
name: 'Section',
|
||||
description: 'Toggle section box',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'B',
|
||||
action: 'ToggleSectionBox'
|
||||
},
|
||||
ZoomExtentsOrSelection: {
|
||||
name: 'Fit',
|
||||
description: 'Zoom to fit selection or entire model',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'space',
|
||||
action: 'ZoomExtentsOrSelection'
|
||||
}
|
||||
} as const
|
||||
|
||||
export const ViewModeShortcuts = {
|
||||
SetViewModeDefault: {
|
||||
name: 'Rendered',
|
||||
description: 'A realistic view of your model rendered with available materials for surfaces.',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'Digit1',
|
||||
action: 'SetViewModeDefault',
|
||||
viewMode: ViewMode.DEFAULT
|
||||
},
|
||||
SetViewModeShaded: {
|
||||
name: 'Shaded',
|
||||
description: 'A shaded view of your model using available colors for surfaces and curves.',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'Digit2',
|
||||
action: 'SetViewModeShaded',
|
||||
viewMode: ViewMode.SHADED
|
||||
},
|
||||
SetViewModeArctic: {
|
||||
name: 'Arctic',
|
||||
description: 'A white conceptual view of your model without any materials or colors.',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'Digit3',
|
||||
action: 'SetViewModeArctic',
|
||||
viewMode: ViewMode.ARCTIC
|
||||
},
|
||||
// SetViewModeSolid: {
|
||||
// name: 'Solid',
|
||||
// description:
|
||||
// 'A basic shaded view of your model using our default material, with edges.',
|
||||
// modifiers: [ModifierKeys.Shift],
|
||||
// key: 'Digit4',
|
||||
// action: 'SetViewModeSolid',
|
||||
// viewMode: ViewMode.SOLID
|
||||
// },
|
||||
SetViewModePen: {
|
||||
name: 'Pen',
|
||||
description:
|
||||
'A stylized black and white drawing view of your model, without any lighting or shadows.',
|
||||
modifiers: [ModifierKeys.Shift],
|
||||
key: 'Digit5',
|
||||
action: 'SetViewModePen',
|
||||
viewMode: ViewMode.PEN
|
||||
}
|
||||
} as const
|
||||
|
||||
export const ViewShortcuts = {
|
||||
SetViewTop: {
|
||||
name: 'Top',
|
||||
description: 'Set view to Top',
|
||||
modifiers: [ModifierKeys.AltOrOpt],
|
||||
key: 'Digit1',
|
||||
action: 'SetViewTop'
|
||||
},
|
||||
SetViewFront: {
|
||||
name: 'Front',
|
||||
description: 'Set view to Front',
|
||||
modifiers: [ModifierKeys.AltOrOpt],
|
||||
key: 'Digit2',
|
||||
action: 'SetViewFront'
|
||||
},
|
||||
SetViewLeft: {
|
||||
name: 'Left',
|
||||
description: 'Set view to Left',
|
||||
modifiers: [ModifierKeys.AltOrOpt],
|
||||
key: 'Digit3',
|
||||
action: 'SetViewLeft'
|
||||
},
|
||||
SetViewBack: {
|
||||
name: 'Back',
|
||||
description: 'Set view to Back',
|
||||
modifiers: [ModifierKeys.AltOrOpt],
|
||||
key: 'Digit4',
|
||||
action: 'SetViewBack'
|
||||
},
|
||||
SetViewRight: {
|
||||
name: 'Right',
|
||||
description: 'Set view to Right',
|
||||
modifiers: [ModifierKeys.AltOrOpt],
|
||||
key: 'Digit5',
|
||||
action: 'SetViewRight'
|
||||
}
|
||||
} as const
|
||||
|
||||
export const ViewerShortcuts = {
|
||||
...ViewModeShortcuts,
|
||||
...PanelShortcuts,
|
||||
...ToolShortcuts,
|
||||
...ViewShortcuts
|
||||
} as const
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { ViewMode } from '@speckle/viewer'
|
||||
import type { ModifierKeys, ViewerShortcuts } from './shortcuts'
|
||||
|
||||
export type BaseShortcut = {
|
||||
name: string
|
||||
description: string
|
||||
modifiers: readonly ModifierKeys[]
|
||||
key: string
|
||||
action: string
|
||||
}
|
||||
|
||||
export type ViewModeShortcut = BaseShortcut & {
|
||||
viewMode: ViewMode
|
||||
}
|
||||
|
||||
export type ViewerShortcut = (typeof ViewerShortcuts)[keyof typeof ViewerShortcuts]
|
||||
export type ViewerShortcutAction = keyof typeof ViewerShortcuts
|
||||
@@ -17,7 +17,6 @@ import { SpeckleObjectsOfflineLoader } from '@src/laoder/SpeckleObjectsOfflineLo
|
||||
import { useVisualStore } from '@src/store/visualStore'
|
||||
import { Tracker } from '@src/utils/mixpanel'
|
||||
import { createNanoEvents, Emitter } from 'nanoevents'
|
||||
import { ColorPicker } from 'powerbi-visuals-utils-formattingmodel/lib/FormattingSettingsComponents'
|
||||
import { Vector3 } from 'three'
|
||||
|
||||
export interface IViewer {
|
||||
@@ -36,20 +35,28 @@ export interface Hit {
|
||||
export interface IViewerEvents {
|
||||
ping: (message: string) => void
|
||||
setSelection: (objectIds: string[]) => void
|
||||
resetFilter: (objectIds: string[]) => void
|
||||
filterSelection: (objectIds: string[], ghost: boolean) => void
|
||||
setViewMode: (viewMode: ViewMode) => void
|
||||
colorObjectsByGroup: (
|
||||
colorById: {
|
||||
objectIds: string[]
|
||||
slice: ColorPicker
|
||||
color: string
|
||||
}[]
|
||||
) => void
|
||||
isolateObjects: (objectIds: string[]) => void
|
||||
unIsolateObjects: () => void
|
||||
zoomExtends: () => void
|
||||
toggleProjection: () => void
|
||||
toggleGhostHidden: (ghost: boolean) => void
|
||||
loadObjects: (objects: object[]) => void
|
||||
}
|
||||
|
||||
export type ColorBy = {
|
||||
objectIds: string[]
|
||||
color: string
|
||||
}
|
||||
|
||||
export class ViewerHandler {
|
||||
public emitter: Emitter
|
||||
public viewer: Viewer
|
||||
@@ -62,6 +69,8 @@ export class ViewerHandler {
|
||||
this.emitter = createNanoEvents()
|
||||
this.emit = this.emit.bind(this)
|
||||
this.emitter.on('ping', this.handlePing)
|
||||
this.emitter.on('filterSelection', this.filterSelection)
|
||||
this.emitter.on('resetFilter', this.resetFilter)
|
||||
this.emitter.on('setSelection', this.selectObjects)
|
||||
this.emitter.on('setViewMode', this.setViewMode)
|
||||
this.emitter.on('colorObjectsByGroup', this.colorObjectsByGroup)
|
||||
@@ -70,6 +79,8 @@ export class ViewerHandler {
|
||||
this.emitter.on('zoomExtends', this.zoomExtends)
|
||||
this.emitter.on('zoomObjects', this.zoomObjects)
|
||||
this.emitter.on('loadObjects', this.loadObjects)
|
||||
this.emitter.on('toggleProjection', this.toggleProjection)
|
||||
this.emitter.on('toggleGhostHidden', this.toggleGhostHidden)
|
||||
}
|
||||
|
||||
async init(parent: HTMLElement) {
|
||||
@@ -78,14 +89,15 @@ export class ViewerHandler {
|
||||
this.filtering = this.viewer.getExtension(FilteringExtension)
|
||||
this.selection = this.viewer.getExtension(SelectionExtension)
|
||||
|
||||
this.cameraControls.on(CameraEvent.Stationary, () => {
|
||||
console.log('🎬 Storing the camera position into file')
|
||||
const cameraController = this.viewer.getExtension(CameraController)
|
||||
const position = cameraController.getPosition()
|
||||
const target = cameraController.getTarget()
|
||||
const store = useVisualStore()
|
||||
store.writeCameraPositionToFile(position, target)
|
||||
})
|
||||
// NOTE: storing camera position into file triggers `update` function. even if I early return according to flag - it slows down the usage a lot.
|
||||
// this.cameraControls.on(CameraEvent.Stationary, () => {
|
||||
// console.log('🎬 Storing the camera position into file')
|
||||
// const cameraController = this.viewer.getExtension(CameraController)
|
||||
// const position = cameraController.getPosition()
|
||||
// const target = cameraController.getTarget()
|
||||
// const store = useVisualStore()
|
||||
// store.writeCameraPositionToFile(position, target)
|
||||
// })
|
||||
}
|
||||
|
||||
emit<E extends keyof IViewerEvents>(event: E, ...payload: Parameters<IViewerEvents[E]>): void {
|
||||
@@ -98,6 +110,7 @@ export class ViewerHandler {
|
||||
}
|
||||
|
||||
public zoomExtends = () => this.cameraControls.setCameraView(undefined, false)
|
||||
public toggleProjection = () => this.cameraControls.toggleCameras()
|
||||
|
||||
public setView = (view: CanonicalView) => this.cameraControls.setCameraView(view, false)
|
||||
|
||||
@@ -118,12 +131,24 @@ export class ViewerHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public colorObjectsByGroup = (
|
||||
colorByIds: {
|
||||
objectIds: string[]
|
||||
color: string
|
||||
}[]
|
||||
) => {
|
||||
public filterSelection = (objectIds: string[], ghost: boolean) => {
|
||||
console.log('🔗 Handling filterSelection inside ViewerHandler')
|
||||
if (objectIds) {
|
||||
this.unIsolateObjects()
|
||||
this.filteringState = this.filtering.isolateObjects(objectIds, 'powerbi', true, ghost)
|
||||
this.zoomObjects(objectIds, true)
|
||||
}
|
||||
}
|
||||
|
||||
public resetFilter = (objectIds: string[]) => {
|
||||
console.log('🔗 Handling filterSelection inside ViewerHandler')
|
||||
if (objectIds) {
|
||||
this.isolateObjects(objectIds, true)
|
||||
this.zoomObjects(objectIds, true)
|
||||
}
|
||||
}
|
||||
|
||||
public colorObjectsByGroup = (colorByIds: ColorBy[]) => {
|
||||
this.filteringState = this.filtering.setUserObjectColors(colorByIds ?? [])
|
||||
}
|
||||
|
||||
@@ -132,6 +157,15 @@ export class ViewerHandler {
|
||||
this.filteringState = this.filtering.isolateObjects(objectIds, 'powerbi', true, ghost)
|
||||
}
|
||||
|
||||
public toggleGhostHidden = (ghost: boolean) => {
|
||||
this.filteringState = this.filtering.isolateObjects(
|
||||
this.filteringState.isolatedObjects,
|
||||
'powerbi',
|
||||
true,
|
||||
ghost
|
||||
)
|
||||
}
|
||||
|
||||
public unIsolateObjects = () => {
|
||||
if (this.filteringState && this.filteringState.isolatedObjects) {
|
||||
this.filteringState = this.filtering.unIsolateObjects(
|
||||
@@ -181,6 +215,7 @@ export class ViewerHandler {
|
||||
|
||||
// Since you are setting another camera position, maybe you want the second argument to false
|
||||
await this.viewer.loadObject(loader, true)
|
||||
this.viewer.getRenderer().shadowcatcher.shadowcatcherMesh.visible = false // works fine only right after loadObjects
|
||||
})
|
||||
|
||||
store.setSpeckleViews(speckleViews)
|
||||
@@ -188,7 +223,10 @@ export class ViewerHandler {
|
||||
this.setViewMode(Number(store.defaultViewModeInFile))
|
||||
}
|
||||
|
||||
Tracker.dataLoaded({ sourceHostApp: store.receiveInfo.sourceApplication })
|
||||
Tracker.dataLoaded({
|
||||
sourceHostApp: store.receiveInfo.sourceApplication,
|
||||
workspace_id: store.receiveInfo.workspaceId
|
||||
})
|
||||
// camera need to be set after objects loaded
|
||||
if (store.cameraPosition) {
|
||||
const position = new Vector3(
|
||||
|
||||
@@ -40,7 +40,7 @@ export class ColorSettings extends fs.SimpleCard {
|
||||
|
||||
name = 'color'
|
||||
displayName = 'Object Display'
|
||||
slices: fs.Slice[] = [this.context, this.fill]
|
||||
slices: fs.Slice[] = [this.fill]
|
||||
}
|
||||
|
||||
export class ColorSelectorSettings extends fs.SimpleCard {
|
||||
|
||||
@@ -9,9 +9,9 @@ export class SpeckleVisualSettingsModel extends fs.Model {
|
||||
|
||||
public colorSelector: ColorSelectorSettings = new ColorSelectorSettings()
|
||||
|
||||
public camera: CameraSettings = new CameraSettings()
|
||||
// public camera: CameraSettings = new CameraSettings()
|
||||
|
||||
public lighting: LightingSettings = new LightingSettings()
|
||||
// public lighting: LightingSettings = new LightingSettings()
|
||||
|
||||
cards = [this.color, this.camera, this.lighting]
|
||||
cards = [this.color]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CanonicalView, SpeckleView, ViewMode } from '@speckle/viewer'
|
||||
import { IViewerEvents } from '@src/plugins/viewer'
|
||||
import { ColorBy, IViewerEvents } from '@src/plugins/viewer'
|
||||
import { SpeckleVisualSettingsModel } from '@src/settings/visualSettingsModel'
|
||||
import { SpeckleDataInput } from '@src/types'
|
||||
import { zipJSONChunks, zipModelObjects } from '@src/utils/compression'
|
||||
import { zipModelObjects } from '@src/utils/compression'
|
||||
import { ReceiveInfo } from '@src/utils/matrixViewUtils'
|
||||
import { defineStore } from 'pinia'
|
||||
import { Vector3 } from 'three'
|
||||
@@ -18,9 +19,15 @@ export type FieldInputState = {
|
||||
|
||||
export const useVisualStore = defineStore('visualStore', () => {
|
||||
const host = shallowRef<powerbi.extensibility.visual.IVisualHost>()
|
||||
const formattingSettings = ref<SpeckleVisualSettingsModel>()
|
||||
const loadingProgress = ref<{ summary: string; progress: number }>(undefined)
|
||||
const objectsFromStore = ref<object[]>(undefined)
|
||||
|
||||
const postFileSaveSkipNeeded = ref<boolean>(false)
|
||||
const postClickSkipNeeded = ref<boolean>(false)
|
||||
|
||||
const isFilterActive = ref<boolean>(false)
|
||||
|
||||
// once you see this shit, you might freak out and you are right. All of them needed because of "update" function trigger by API.
|
||||
// most of the time we need to know what we are doing to treat operations accordingly. Ask for more to me (Ogu), but the answers will make both of us unhappy.
|
||||
const isViewerInitialized = ref<boolean>(false)
|
||||
@@ -54,6 +61,7 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
// TODO: investigate about shallow ref? https://vuejs.org/api/reactivity-advanced.html#shallowref
|
||||
const dataInput = shallowRef<SpeckleDataInput | null>()
|
||||
const dataInputStatus = ref<InputState>('incomplete')
|
||||
const latestColorBy = ref<ColorBy[] | null | undefined>([])
|
||||
|
||||
/**
|
||||
* Ideally one time setup on initialization.
|
||||
@@ -100,8 +108,9 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
id: string
|
||||
}
|
||||
|
||||
const loadObjectsFromFile = async (objects: object[]) => {
|
||||
lastLoadedRootObjectId.value = (objects[0] as SpeckleObject).id // TODO fix
|
||||
const loadObjectsFromFile = async (objects: object[][]) => {
|
||||
const savedVersionObjectId = objects.map((o) => (o[0] as SpeckleObject).id).join(',')
|
||||
lastLoadedRootObjectId.value = savedVersionObjectId
|
||||
viewerReloadNeeded.value = false
|
||||
console.log(`📦 Loading viewer from cached data with ${lastLoadedRootObjectId.value} id.`)
|
||||
await viewerEmit.value('loadObjects', objects)
|
||||
@@ -131,14 +140,19 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
}
|
||||
|
||||
if (dataInput.value.selectedIds.length > 0) {
|
||||
viewerEmit.value('isolateObjects', dataInput.value.selectedIds)
|
||||
isFilterActive.value = true
|
||||
viewerEmit.value('filterSelection', dataInput.value.selectedIds, true)
|
||||
} else {
|
||||
viewerEmit.value('isolateObjects', dataInput.value.objectIds)
|
||||
isFilterActive.value = false
|
||||
latestColorBy.value = dataInput.value.colorByIds
|
||||
viewerEmit.value('resetFilter', dataInput.value.objectIds)
|
||||
}
|
||||
viewerEmit.value('colorObjectsByGroup', dataInput.value.colorByIds)
|
||||
}
|
||||
|
||||
const writeObjectsToFile = (modelObjects: object[][]) => {
|
||||
// NOTE: need skipping the update function, it resets the viewer state unneccessarily.
|
||||
postFileSaveSkipNeeded.value = true
|
||||
const compressedChunks = zipModelObjects(modelObjects, 10000) // Compress in chunks
|
||||
|
||||
host.value.persistProperties({
|
||||
@@ -156,6 +170,8 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
}
|
||||
|
||||
const writeCameraViewToFile = (view: CanonicalView) => {
|
||||
// NOTE: need skipping the update function, it resets the viewer state unneccessarily.
|
||||
postFileSaveSkipNeeded.value = true
|
||||
host.value.persistProperties({
|
||||
merge: [
|
||||
{
|
||||
@@ -170,6 +186,8 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
}
|
||||
|
||||
const writeViewModeToFile = (viewMode: ViewMode) => {
|
||||
// NOTE: need skipping the update function, it resets the viewer state unneccessarily.
|
||||
postFileSaveSkipNeeded.value = true
|
||||
host.value.persistProperties({
|
||||
merge: [
|
||||
{
|
||||
@@ -184,22 +202,24 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
}
|
||||
|
||||
const writeCameraPositionToFile = (position: Vector3, target: Vector3) => {
|
||||
host.value.persistProperties({
|
||||
merge: [
|
||||
{
|
||||
objectName: 'cameraPosition',
|
||||
properties: {
|
||||
positionX: position.x,
|
||||
positionY: position.y,
|
||||
positionZ: position.z,
|
||||
targetX: target.x,
|
||||
targetY: target.y,
|
||||
targetZ: target.z
|
||||
},
|
||||
selector: null
|
||||
}
|
||||
]
|
||||
})
|
||||
// NOTE: need skipping the update function, it resets the viewer state unneccessarily.
|
||||
// postFileSaveSkipNeeded.value = true
|
||||
// host.value.persistProperties({
|
||||
// merge: [
|
||||
// {
|
||||
// objectName: 'cameraPosition',
|
||||
// properties: {
|
||||
// positionX: position.x,
|
||||
// positionY: position.y,
|
||||
// positionZ: position.z,
|
||||
// targetX: target.x,
|
||||
// targetY: target.y,
|
||||
// targetZ: target.z
|
||||
// },
|
||||
// selector: null
|
||||
// }
|
||||
// ]
|
||||
// })
|
||||
}
|
||||
|
||||
const setFieldInputState = (newFieldInputState: FieldInputState) =>
|
||||
@@ -213,10 +233,23 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
|
||||
const setViewerReloadNeeded = () => (viewerReloadNeeded.value = true)
|
||||
|
||||
const setPostFileSaveSkipNeeded = (newValue: boolean) => (postFileSaveSkipNeeded.value = newValue)
|
||||
const setPostClickSkipNeeded = (newValue: boolean) => (postClickSkipNeeded.value = newValue)
|
||||
|
||||
const setCameraPositionInFile = (newValue: number[]) => (cameraPosition.value = newValue)
|
||||
const setDefaultViewModeInFile = (newValue: string) => (defaultViewModeInFile.value = newValue)
|
||||
|
||||
const setSpeckleViews = (newSpeckleViews: SpeckleView[]) => (speckleViews.value = newSpeckleViews)
|
||||
const setFormattingSettings = (newFormattingSettings: SpeckleVisualSettingsModel) =>
|
||||
(formattingSettings.value = newFormattingSettings)
|
||||
|
||||
const resetFilters = () => {
|
||||
viewerEmit.value('resetFilter', dataInput.value.objectIds)
|
||||
if (latestColorBy.value !== null) {
|
||||
viewerEmit.value('colorObjectsByGroup', latestColorBy.value)
|
||||
}
|
||||
isFilterActive.value = false
|
||||
}
|
||||
|
||||
return {
|
||||
host,
|
||||
@@ -236,6 +269,14 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
cameraPosition,
|
||||
defaultViewModeInFile,
|
||||
speckleViews,
|
||||
postFileSaveSkipNeeded,
|
||||
postClickSkipNeeded,
|
||||
isFilterActive,
|
||||
latestColorBy,
|
||||
formattingSettings,
|
||||
setFormattingSettings,
|
||||
setPostClickSkipNeeded,
|
||||
setPostFileSaveSkipNeeded,
|
||||
setCameraPositionInFile,
|
||||
setDefaultViewModeInFile,
|
||||
setSpeckleViews,
|
||||
@@ -255,6 +296,7 @@ export const useVisualStore = defineStore('visualStore', () => {
|
||||
setViewerReadyToLoad,
|
||||
setLoadingProgress,
|
||||
clearLoadingProgress,
|
||||
setIsLoadingFromFile
|
||||
setIsLoadingFromFile,
|
||||
resetFilters
|
||||
}
|
||||
})
|
||||
|
||||
@@ -15,6 +15,5 @@ export interface SpeckleDataInput {
|
||||
selectedIds: string[]
|
||||
colorByIds: { objectIds: string[]; slice: fs.ColorPicker; color: string }[]
|
||||
objectTooltipData: Map<string, IViewerTooltip>
|
||||
view: powerbi.DataViewMatrix
|
||||
isFromStore: boolean
|
||||
}
|
||||
|
||||
@@ -39,11 +39,14 @@ export function validateMatrixView(options: VisualUpdateOptions): FieldInputStat
|
||||
hasColorFilter = false,
|
||||
hasTooltipData = false
|
||||
|
||||
matrixVew.valueSources.forEach((level) => {
|
||||
if (!hasRootObjectId) hasRootObjectId = level.roles['rootObjectId'] != undefined
|
||||
})
|
||||
|
||||
matrixVew.rows.levels.forEach((level) => {
|
||||
level.sources.forEach((source) => {
|
||||
if (!hasRootObjectId) hasRootObjectId = source.roles['rootObjectId'] != undefined
|
||||
if (!hasObjectIds) hasObjectIds = source.roles['objectIds'] != undefined
|
||||
if (!hasColorFilter) hasColorFilter = source.roles['objectColorBy'] != undefined
|
||||
if (!hasColorFilter) hasColorFilter = source.roles['colorBy'] != undefined
|
||||
})
|
||||
})
|
||||
|
||||
@@ -85,12 +88,16 @@ function processObjectValues(
|
||||
shouldColor = true
|
||||
}
|
||||
const propData: IViewerTooltipData = {
|
||||
displayName: colInfo.displayName,
|
||||
value: value.value.toString()
|
||||
displayName: colInfo.displayName.replace('First ', ''),
|
||||
value: value.value === null ? '<not set>' : value.value.toString()
|
||||
}
|
||||
objectData.push(propData)
|
||||
})
|
||||
return { data: objectData, shouldColor, shouldSelect }
|
||||
return {
|
||||
data: objectData.length > 0 ? objectData.slice(1) : [],
|
||||
shouldColor,
|
||||
shouldSelect
|
||||
}
|
||||
}
|
||||
|
||||
function processObjectNode(
|
||||
@@ -145,6 +152,11 @@ export type ReceiveInfo = {
|
||||
userEmail: string
|
||||
serverUrl: string
|
||||
sourceApplication?: string
|
||||
workspaceId?: string
|
||||
workspaceLogo?: string
|
||||
workspaceName?: string
|
||||
canHideBranding: boolean
|
||||
version?: string
|
||||
}
|
||||
|
||||
async function getReceiveInfo(id) {
|
||||
@@ -270,8 +282,16 @@ export async function processMatrixView(
|
||||
|
||||
console.log('🪜 Processing Matrix View', matrixView)
|
||||
|
||||
const localMatrixView = matrixView.rows.root.children[0]
|
||||
const id = localMatrixView.value as unknown as string
|
||||
const localMatrixView = matrixView.rows.root.children
|
||||
let id = null
|
||||
|
||||
if (hasColorFilter) {
|
||||
id = localMatrixView[0].children[0].values[0].value as unknown as string
|
||||
} else {
|
||||
id = localMatrixView[0].values[0].value as unknown as string
|
||||
}
|
||||
|
||||
// const id = localMatrixView[0].values[0].value as unknown as string
|
||||
console.log('🗝️ Root Object Id: ', id)
|
||||
console.log('Last laoded root object id', visualStore.lastLoadedRootObjectId)
|
||||
|
||||
@@ -294,7 +314,12 @@ export async function processMatrixView(
|
||||
visualStore.setReceiveInfo({
|
||||
userEmail: receiveInfo.email,
|
||||
serverUrl: receiveInfo.server,
|
||||
sourceApplication: getSlugFromHostAppNameAndVersion(receiveInfo.sourceApplication)
|
||||
sourceApplication: getSlugFromHostAppNameAndVersion(receiveInfo.sourceApplication),
|
||||
workspaceId: receiveInfo.workspaceId,
|
||||
workspaceName: receiveInfo.workspaceName,
|
||||
workspaceLogo: receiveInfo.workspaceLogo,
|
||||
version: receiveInfo.version,
|
||||
canHideBranding: receiveInfo.canHideBranding
|
||||
})
|
||||
}
|
||||
|
||||
@@ -303,34 +328,31 @@ export async function processMatrixView(
|
||||
console.log(`🚀 Upload is completed in ${(performance.now() - start) / 1000} s!`)
|
||||
}
|
||||
|
||||
// NOTE: matrix view gave us already filtered out rows from tooltip data if it is assigned
|
||||
localMatrixView.children?.forEach((obj) => {
|
||||
// otherwise there is no point to collect objects
|
||||
const processedObjectIdLevels = processObjectIdLevel(obj, host, matrixView)
|
||||
// If colors assigned, data arrives nested
|
||||
if (hasColorFilter) {
|
||||
// const start = performance.now()
|
||||
// console.log('Sorting the colors started...')
|
||||
// // powerbi sorts the objects alphabetically for color legends
|
||||
// const sortedMatrix = localMatrixView.sort((a, b) => {
|
||||
// return (a.levelValues[0].value as string).localeCompare(b.levelValues[0].value as string)
|
||||
// })
|
||||
// const end = performance.now()
|
||||
// console.log(`Sorted in: ${(end - start) / 1000} s`)
|
||||
|
||||
objectIds.push(processedObjectIdLevels.id)
|
||||
onSelectionPair(processedObjectIdLevels.id, processedObjectIdLevels.selectionId)
|
||||
if (processedObjectIdLevels.shouldSelect) {
|
||||
selectedIds.push(processedObjectIdLevels.id)
|
||||
}
|
||||
objectTooltipData.set(processedObjectIdLevels.id, {
|
||||
selectionId: processedObjectIdLevels.selectionId,
|
||||
data: processedObjectIdLevels.data
|
||||
})
|
||||
if (previousPalette) host.colorPalette['colorPalette'] = previousPalette
|
||||
|
||||
if (hasColorFilter) {
|
||||
if (previousPalette) host.colorPalette['colorPalette'] = previousPalette
|
||||
obj.children.forEach((child) => {
|
||||
localMatrixView.forEach((colorObjects) => {
|
||||
colorObjects.children.forEach((obj) => {
|
||||
const colorSelectionId = host
|
||||
.createSelectionIdBuilder()
|
||||
.withMatrixNode(child, matrixView.rows.levels)
|
||||
.withMatrixNode(obj, matrixView.rows.levels)
|
||||
.createSelectionId()
|
||||
|
||||
const color = host.colorPalette.getColor(child.values[0].value as string)
|
||||
|
||||
const value = colorObjects.value as string
|
||||
const color = host.colorPalette.getColor(value)
|
||||
const colorSlice = new fs.ColorPicker({
|
||||
name: 'selectorFill',
|
||||
displayName: child.value?.toString(),
|
||||
displayName: value,
|
||||
value: {
|
||||
value: color.value
|
||||
},
|
||||
@@ -343,7 +365,7 @@ export async function processMatrixView(
|
||||
objectIds: []
|
||||
}
|
||||
|
||||
const processedObjectIdLevels = processObjectIdLevel(child, host, matrixView)
|
||||
const processedObjectIdLevels = processObjectIdLevel(obj, host, matrixView)
|
||||
|
||||
objectIds.push(processedObjectIdLevels.id)
|
||||
onSelectionPair(processedObjectIdLevels.id, processedObjectIdLevels.selectionId)
|
||||
@@ -358,8 +380,86 @@ export async function processMatrixView(
|
||||
|
||||
if (colorGroup.objectIds.length > 0) colorByIds.push(colorGroup)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
localMatrixView.forEach((obj) => {
|
||||
const processedObjectIdLevels = processObjectIdLevel(obj, host, matrixView)
|
||||
|
||||
if (processedObjectIdLevels.color) {
|
||||
let group = colorByIds.find((g) => g.color === processedObjectIdLevels.color)
|
||||
if (!group) {
|
||||
group = {
|
||||
color: processedObjectIdLevels.color,
|
||||
objectIds: []
|
||||
}
|
||||
colorByIds.push(group)
|
||||
}
|
||||
group.objectIds.push(processedObjectIdLevels.id)
|
||||
}
|
||||
|
||||
objectIds.push(processedObjectIdLevels.id)
|
||||
onSelectionPair(processedObjectIdLevels.id, processedObjectIdLevels.selectionId)
|
||||
if (processedObjectIdLevels.shouldSelect) {
|
||||
selectedIds.push(processedObjectIdLevels.id)
|
||||
}
|
||||
objectTooltipData.set(processedObjectIdLevels.id, {
|
||||
selectionId: processedObjectIdLevels.selectionId,
|
||||
data: processedObjectIdLevels.data
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// if (hasColorFilter) {
|
||||
// const start = performance.now()
|
||||
// console.log('Sorting the colors started...')
|
||||
// // powerbi sorts the objects alphabetically for color legends
|
||||
// const sortedMatrix = localMatrixView.sort((a, b) => {
|
||||
// return (a.levelValues[0].value as string).localeCompare(b.levelValues[0].value as string)
|
||||
// })
|
||||
// const end = performance.now()
|
||||
// console.log(`Sorted in: ${(end - start) / 1000} s`)
|
||||
|
||||
// sortedMatrix.forEach((obj) => {
|
||||
// if (previousPalette) host.colorPalette['colorPalette'] = previousPalette
|
||||
|
||||
// const colorSelectionId = host
|
||||
// .createSelectionIdBuilder()
|
||||
// .withMatrixNode(obj, matrixView.rows.levels)
|
||||
// .createSelectionId()
|
||||
|
||||
// const value = obj.levelValues[0].value as string
|
||||
// const color = host.colorPalette.getColor(value)
|
||||
// const colorSlice = new fs.ColorPicker({
|
||||
// name: 'selectorFill',
|
||||
// displayName: value,
|
||||
// value: {
|
||||
// value: color.value
|
||||
// },
|
||||
// selector: colorSelectionId.getSelector()
|
||||
// })
|
||||
|
||||
// const colorGroup = {
|
||||
// color: color.value,
|
||||
// slice: colorSlice,
|
||||
// objectIds: []
|
||||
// }
|
||||
|
||||
// const processedObjectIdLevels = processObjectIdLevel(obj, host, matrixView)
|
||||
|
||||
// objectIds.push(processedObjectIdLevels.id)
|
||||
// onSelectionPair(processedObjectIdLevels.id, processedObjectIdLevels.selectionId)
|
||||
// if (processedObjectIdLevels.shouldSelect) selectedIds.push(processedObjectIdLevels.id)
|
||||
// if (processedObjectIdLevels.shouldColor) {
|
||||
// colorGroup.objectIds.push(processedObjectIdLevels.id)
|
||||
// }
|
||||
// objectTooltipData.set(processedObjectIdLevels.id, {
|
||||
// selectionId: processedObjectIdLevels.selectionId,
|
||||
// data: processedObjectIdLevels.data
|
||||
// })
|
||||
|
||||
// if (colorGroup.objectIds.length > 0) colorByIds.push(colorGroup)
|
||||
// })
|
||||
// }
|
||||
|
||||
previousPalette = host.colorPalette['colorPalette']
|
||||
|
||||
@@ -369,7 +469,6 @@ export async function processMatrixView(
|
||||
selectedIds,
|
||||
colorByIds: colorByIds.length > 0 ? colorByIds : null,
|
||||
objectTooltipData,
|
||||
view: matrixView,
|
||||
isFromStore: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
id="speckle-home-view"
|
||||
class="flex flex-col justify-center items-center h-full w-full text-center space-y-4 p-2"
|
||||
class="flex flex-col justify-center items-center h-full w-full text-center space-y-4 p-2 cursor-default"
|
||||
>
|
||||
<div class="flex flex-col justify-center items-center h-full w-full text-center space-y-4">
|
||||
<div class="flex flex-row justify-center items-center space-x-3">
|
||||
@@ -33,20 +33,21 @@
|
||||
</div>
|
||||
<div class="flex flex-row space-x-2">
|
||||
<ChatBubbleLeftIcon class="w-6"></ChatBubbleLeftIcon>
|
||||
<p><b>Tooltip Data</b></p>
|
||||
<p><b>Object Data</b></p>
|
||||
<ArrowRightIcon class="w-4"></ArrowRightIcon>
|
||||
<p>Tooltip and interactivity</p>
|
||||
<p>Tooltips for selected object</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-1">
|
||||
<button :class="buttonClass" @click="goToGuide">Getting started</button>
|
||||
<button :class="buttonClass" @click="goToForum">Help</button>
|
||||
<FormButton size="sm" @click="goToGuide">Getting started</FormButton>
|
||||
<FormButton size="sm" @click="goToForum">Help</FormButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FormButton from '@src/components/form/FormButton.vue'
|
||||
import { useVisualStore } from '../store/visualStore'
|
||||
// import { FormButton } from '@speckle/ui-components'
|
||||
import {
|
||||
|
||||
@@ -1,31 +1,4 @@
|
||||
<template>
|
||||
<div
|
||||
class="absolute top-0 left-0 z-10 cursor-pointer flex items-center"
|
||||
@click="goToSpeckleWebsite"
|
||||
>
|
||||
<img class="w-8 h-auto mx-2 my-1" src="@assets/logo-big.png" />
|
||||
<div class="font-medium">Speckle</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isInteractive"
|
||||
class="absolute top-2 left-1/2 -translate-x-1/2 z-20 bg-white bg-opacity-70 text-black text-center text-sm px-4 py-2 rounded shadow"
|
||||
>
|
||||
<div v-if="bothFieldsMissing">
|
||||
<strong>Object IDs</strong>
|
||||
and
|
||||
<strong>Tooltip Data</strong>
|
||||
fields are needed for interactivity with other visuals.
|
||||
</div>
|
||||
<div v-else-if="onlyObjectIdsMissing">
|
||||
<strong>Object IDs</strong>
|
||||
field is needed for interactivity with other visuals.
|
||||
</div>
|
||||
<div v-else-if="onlyTooltipDataMissing">
|
||||
<strong>Tooltip Data</strong>
|
||||
field is needed for interactivity with other visuals.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="visualStore.loadingProgress"
|
||||
class="absolute top-1/2 left-1/2 w-1/2 -translate-x-1/2 z-20 text-center text-sm"
|
||||
@@ -34,32 +7,13 @@
|
||||
<LoadingBar :loading="!!visualStore.loadingProgress"></LoadingBar>
|
||||
</div>
|
||||
|
||||
<viewer-wrapper id="speckle-3d-view" class="h-full w-full"></viewer-wrapper>
|
||||
<viewer-wrapper id="speckle-3d-view" class="h-full w-full cursor-default"></viewer-wrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ViewerWrapper from 'src/components/ViewerWrapper.vue'
|
||||
import { useVisualStore } from '../store/visualStore'
|
||||
import { computed } from 'vue'
|
||||
import LoadingBar from '@src/components/loading/LoadingBar.vue'
|
||||
|
||||
const visualStore = useVisualStore()
|
||||
|
||||
const onlyObjectIdsMissing = computed(
|
||||
() => !visualStore.fieldInputState.objectIds && visualStore.fieldInputState.tooltipData
|
||||
)
|
||||
|
||||
const onlyTooltipDataMissing = computed(
|
||||
() => visualStore.fieldInputState.objectIds && !visualStore.fieldInputState.tooltipData
|
||||
)
|
||||
|
||||
const bothFieldsMissing = computed(
|
||||
() => !visualStore.fieldInputState.objectIds && !visualStore.fieldInputState.tooltipData
|
||||
)
|
||||
|
||||
const isInteractive = computed(
|
||||
() => !visualStore.fieldInputState.objectIds || !visualStore.fieldInputState.tooltipData
|
||||
)
|
||||
|
||||
const goToSpeckleWebsite = () => visualStore.host.launchUrl('https://speckle.systems')
|
||||
</script>
|
||||
|
||||
@@ -63,6 +63,19 @@ export class Visual implements IVisual {
|
||||
|
||||
public async update(options: VisualUpdateOptions) {
|
||||
const visualStore = useVisualStore()
|
||||
|
||||
if (visualStore.postFileSaveSkipNeeded) {
|
||||
visualStore.setPostFileSaveSkipNeeded(false)
|
||||
console.log('Skipping unneccessary update function after file save.')
|
||||
return
|
||||
}
|
||||
|
||||
if (visualStore.postClickSkipNeeded) {
|
||||
visualStore.setPostClickSkipNeeded(false)
|
||||
console.log('Skipping unneccessary update function canvas click.')
|
||||
return
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
console.log('⤴️ Update type 👉', powerbi.VisualUpdateType[options.type])
|
||||
this.formattingSettings = this.formattingSettingsService.populateFormattingSettingsModel(
|
||||
@@ -70,6 +83,7 @@ export class Visual implements IVisual {
|
||||
options.dataViews[0]
|
||||
)
|
||||
|
||||
visualStore.setFormattingSettings(this.formattingSettings)
|
||||
console.log('Selector colors', this.formattingSettings.colorSelector)
|
||||
|
||||
try {
|
||||
@@ -132,7 +146,9 @@ export class Visual implements IVisual {
|
||||
console.warn(error)
|
||||
console.log('missing mixpanel info')
|
||||
}
|
||||
if (visualStore.lastLoadedRootObjectId !== objectsFromFile[0].id) {
|
||||
const savedVersionObjectId = objectsFromFile.map((o) => o[0].id).join(',')
|
||||
|
||||
if (visualStore.lastLoadedRootObjectId !== savedVersionObjectId) {
|
||||
this.tryReadFromFile(objectsFromFile, visualStore)
|
||||
}
|
||||
}
|
||||
@@ -195,7 +211,7 @@ export class Visual implements IVisual {
|
||||
}
|
||||
}
|
||||
|
||||
private tryReadFromFile(objectsFromFile: object[], visualStore) {
|
||||
private tryReadFromFile(objectsFromFile: object[][], visualStore) {
|
||||
visualStore.setViewerReadyToLoad()
|
||||
visualStore.setIsLoadingFromFile(true) // to block unnecessary streaming data if bg service is running
|
||||
setTimeout(() => {
|
||||
|
||||
Reference in New Issue
Block a user