mirror of
https://github.com/penpot/penpot.git
synced 2026-01-28 08:13:29 -05:00
Compare commits
44 Commits
eva-extrac
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ca82821c1 | ||
|
|
a90f672a5e | ||
|
|
f76598f638 | ||
|
|
eacc033567 | ||
|
|
71c349479f | ||
|
|
fda31624c1 | ||
|
|
7f640569bd | ||
|
|
91f1323802 | ||
|
|
dbd4a2366f | ||
|
|
cbb6d098a7 | ||
|
|
b6f5000d1c | ||
|
|
0527124f2f | ||
|
|
faf91ac70d | ||
|
|
9ca76c745f | ||
|
|
89935e2174 | ||
|
|
7f27e0326d | ||
|
|
9c539dfb2f | ||
|
|
50a4cf8b99 | ||
|
|
f5996a7235 | ||
|
|
e8fd4698c9 | ||
|
|
0ab126748f | ||
|
|
71a5ab9913 | ||
|
|
61969f3eb5 | ||
|
|
bd2ef8057e | ||
|
|
9808b6ca57 | ||
|
|
2523096fdd | ||
|
|
de41cb5488 | ||
|
|
8e63c4e3e8 | ||
|
|
b40ccaf030 | ||
|
|
7d3ac38749 | ||
|
|
d5abc52dac | ||
|
|
8d1bc6c50c | ||
|
|
3112b240a0 | ||
|
|
56fd66b91a | ||
|
|
3b96eb5476 | ||
|
|
2a7c24f6fd | ||
|
|
947aa22dee | ||
|
|
1ce0b60e3d | ||
|
|
5209a8b423 | ||
|
|
f4f4f5bbb5 | ||
|
|
ef80901400 | ||
|
|
5306bed548 | ||
|
|
92a319ddd1 | ||
|
|
68a6d4c9a8 |
@@ -45,6 +45,15 @@
|
|||||||
:potok/reify-type
|
:potok/reify-type
|
||||||
{:level :error}
|
{:level :error}
|
||||||
|
|
||||||
|
:redundant-primitive-coercion
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unused-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unresolved-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
:missing-protocol-method
|
:missing-protocol-method
|
||||||
{:level :off}
|
{:level :off}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
:remove-multiple-non-indenting-spaces? false
|
:remove-multiple-non-indenting-spaces? false
|
||||||
:remove-surrounding-whitespace? true
|
:remove-surrounding-whitespace? true
|
||||||
:remove-consecutive-blank-lines? false
|
:remove-consecutive-blank-lines? false
|
||||||
|
:indent-line-comments? true
|
||||||
|
:parallel? true
|
||||||
|
:align-form-columns? false
|
||||||
|
;; :align-map-columns? false
|
||||||
|
;; :align-single-column-lines? false
|
||||||
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
||||||
cljs.test/async [[:inner 0]]
|
cljs.test/async [[:inner 0]]
|
||||||
promesa.exec/thread [[:inner 0]]
|
promesa.exec/thread [[:inner 0]]
|
||||||
|
|||||||
21
.github/workflows/build-nitrate-module.yml
vendored
21
.github/workflows/build-nitrate-module.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: _NITRATE MODULE
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '36 5-20 * * 1-5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-bundle:
|
|
||||||
uses: ./.github/workflows/build-bundle.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
|
||||||
needs: build-bundle
|
|
||||||
uses: ./.github/workflows/build-docker.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
28
.github/workflows/plugins-deploy-api-doc.yml
vendored
28
.github/workflows/plugins-deploy-api-doc.yml
vendored
@@ -7,11 +7,11 @@ on:
|
|||||||
- staging
|
- staging
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- "plugins/libs/plugin-types/index.d.ts"
|
- 'plugins/libs/plugin-types/index.d.ts'
|
||||||
- "plugins/libs/plugin-types/REAME.md"
|
- 'plugins/libs/plugin-types/REAME.md'
|
||||||
- "plugins/tools/typedoc.css"
|
- 'plugins/tools/typedoc.css'
|
||||||
- "plugins/CHANGELOG.md"
|
- 'plugins/CHANGELOG.md'
|
||||||
- "plugins/wrangler-penpot-plugins-api-doc.toml"
|
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
gh_ref:
|
gh_ref:
|
||||||
@@ -86,12 +86,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
REF="${{ steps.vars.outputs.gh_ref }}"
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
case "$REF" in
|
case "$REF" in
|
||||||
main) echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV ;;
|
main)
|
||||||
staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;;
|
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
|
||||||
develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;;
|
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
|
||||||
|
|
||||||
- name: Deploy to Cloudflare Workers
|
- name: Deploy to Cloudflare Workers
|
||||||
uses: cloudflare/wrangler-action@v3
|
uses: cloudflare/wrangler-action@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
name: Plugins/styles-doc deployer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'plugins/apps/example-styles/**'
|
||||||
|
- 'plugins/libs/plugins-styles/**'
|
||||||
|
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
options:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Extract some useful variables
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ steps.vars.outputs.gh_ref }}
|
||||||
|
|
||||||
|
# START: Setup Node and PNPM enabling cache
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
|
||||||
|
- name: Enable PNPM
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
corepack enable;
|
||||||
|
corepack install;
|
||||||
|
|
||||||
|
- name: Get pnpm store path
|
||||||
|
id: pnpm-store
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
# END: Setup Node and PNPM enabling cache
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pnpm install --no-frozen-lockfile;
|
||||||
|
pnpm add -D -w wrangler@latest;
|
||||||
|
|
||||||
|
- name: Build styles
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: npx nx run example-styles:build
|
||||||
|
|
||||||
|
- name: Select Worker name
|
||||||
|
run: |
|
||||||
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
|
case "$REF" in
|
||||||
|
main)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
|
||||||
|
|
||||||
|
- name: Deploy to Cloudflare Workers
|
||||||
|
uses: cloudflare/wrangler-action@v3
|
||||||
|
with:
|
||||||
|
workingDirectory: plugins
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
command: deploy --config wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
if: failure()
|
||||||
|
uses: mattermost/action-mattermost-notify@master
|
||||||
|
with:
|
||||||
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
|
TEXT: |
|
||||||
|
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
|
||||||
|
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||||
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
@infra
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,6 +21,7 @@
|
|||||||
.rebel_readline_history
|
.rebel_readline_history
|
||||||
.repl
|
.repl
|
||||||
.shadow-cljs
|
.shadow-cljs
|
||||||
|
.pnpm-store/
|
||||||
/*.jpg
|
/*.jpg
|
||||||
/*.md
|
/*.md
|
||||||
/*.png
|
/*.png
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
/backend/resources/public/media
|
/backend/resources/public/media
|
||||||
/backend/target/
|
/backend/target/
|
||||||
/backend/experiments
|
/backend/experiments
|
||||||
|
/backend/scripts/_env.local
|
||||||
/bundle*
|
/bundle*
|
||||||
/cd.md
|
/cd.md
|
||||||
/clj-profiler/
|
/clj-profiler/
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
/library/target/
|
/library/target/
|
||||||
/library/*.zip
|
/library/*.zip
|
||||||
/external
|
/external
|
||||||
|
/penpot-nitrate
|
||||||
|
|
||||||
clj-profiler/
|
clj-profiler/
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
||||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
|
- Fix boolean operators in menu for boards [Taiga #13174](https://tree.taiga.io/project/penpot/issue/13174)
|
||||||
|
|
||||||
## 2.13.0 (Unreleased)
|
## 2.13.0 (Unreleased)
|
||||||
|
|
||||||
@@ -69,7 +70,8 @@
|
|||||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||||
|
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
|
||||||
|
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
|
||||||
|
|
||||||
## 2.12.1
|
## 2.12.1
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
export PENPOT_NITRATE_SHARED_KEY=super-secret-nitrate-api-key
|
||||||
|
export PENPOT_EXPORTER_SHARED_KEY=super-secret-exporter-api-key
|
||||||
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
||||||
|
|
||||||
|
# DEPRECATED: only used for subscriptions
|
||||||
|
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
||||||
|
|
||||||
export PENPOT_HOST=devenv
|
export PENPOT_HOST=devenv
|
||||||
export PENPOT_PUBLIC_URI=https://localhost:3449
|
export PENPOT_PUBLIC_URI=https://localhost:3449
|
||||||
|
|
||||||
@@ -13,6 +18,7 @@ export PENPOT_FLAGS="\
|
|||||||
disable-login-with-google \
|
disable-login-with-google \
|
||||||
disable-login-with-github \
|
disable-login-with-github \
|
||||||
disable-login-with-gitlab \
|
disable-login-with-gitlab \
|
||||||
|
disable-telemetry \
|
||||||
enable-backend-worker \
|
enable-backend-worker \
|
||||||
enable-backend-asserts \
|
enable-backend-asserts \
|
||||||
disable-feature-fdata-pointer-map \
|
disable-feature-fdata-pointer-map \
|
||||||
@@ -55,6 +61,8 @@ export PENPOT_OBJECTS_STORAGE_BACKEND=s3
|
|||||||
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
||||||
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
||||||
|
|
||||||
|
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/control-center
|
||||||
|
|
||||||
export JAVA_OPTS="\
|
export JAVA_OPTS="\
|
||||||
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
||||||
-Djdk.attach.allowAttachSelf \
|
-Djdk.attach.allowAttachSelf \
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
|
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
export OPTIONS="-A:dev"
|
export OPTIONS="-A:dev"
|
||||||
|
|
||||||
entrypoint=${1:-app.main};
|
entrypoint=${1:-app.main};
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -873,11 +873,8 @@
|
|||||||
(import-storage-objects cfg)
|
(import-storage-objects cfg)
|
||||||
|
|
||||||
(let [files (get manifest :files)
|
(let [files (get manifest :files)
|
||||||
result (reduce (fn [result {:keys [id] :as file}]
|
result (reduce (fn [result file]
|
||||||
(let [name' (get file :name)
|
(let [name' (get file :name)
|
||||||
name' (if (map? name)
|
|
||||||
(get name id)
|
|
||||||
name')
|
|
||||||
file (assoc file :name name')]
|
file (assoc file :name name')]
|
||||||
(conj result (import-file cfg file))))
|
(conj result (import-file cfg file))))
|
||||||
[]
|
[]
|
||||||
|
|||||||
@@ -102,6 +102,8 @@
|
|||||||
[:http-server-io-threads {:optional true} ::sm/int]
|
[:http-server-io-threads {:optional true} ::sm/int]
|
||||||
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:exporter-shared-key {:optional true} :string]
|
||||||
|
[:nitrate-shared-key {:optional true} :string]
|
||||||
[:management-api-key {:optional true} :string]
|
[:management-api-key {:optional true} :string]
|
||||||
|
|
||||||
[:telemetry-uri {:optional true} :string]
|
[:telemetry-uri {:optional true} :string]
|
||||||
@@ -225,6 +227,8 @@
|
|||||||
[:netty-io-threads {:optional true} ::sm/int]
|
[:netty-io-threads {:optional true} ::sm/int]
|
||||||
[:executor-threads {:optional true} ::sm/int]
|
[:executor-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:nitrate-backend-uri {:optional true} ::sm/uri]
|
||||||
|
|
||||||
;; DEPRECATED
|
;; DEPRECATED
|
||||||
[:assets-storage-backend {:optional true} :keyword]
|
[:assets-storage-backend {:optional true} :keyword]
|
||||||
[:storage-assets-fs-directory {:optional true} :string]
|
[:storage-assets-fs-directory {:optional true} :string]
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http.middleware :as mw]
|
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.rpc.commands.profile :as cmd.profile]
|
[app.rpc.commands.profile :as cmd.profile]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.worker :as-alias wrk]
|
[app.worker :as-alias wrk]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
|
[yetti.request :as yreq]
|
||||||
[yetti.response :as-alias yres]))
|
[yetti.response :as-alias yres]))
|
||||||
|
|
||||||
;; ---- ROUTES
|
;; ---- ROUTES
|
||||||
@@ -49,13 +49,25 @@
|
|||||||
(fn [cfg request]
|
(fn [cfg request]
|
||||||
(db/tx-run! cfg handler request)))))})
|
(db/tx-run! cfg handler request)))))})
|
||||||
|
|
||||||
|
(def ^:private shared-key-auth
|
||||||
|
{:name ::shared-key-auth
|
||||||
|
:compile
|
||||||
|
(fn [_ _]
|
||||||
|
(fn [handler key]
|
||||||
|
(if key
|
||||||
|
(fn [request]
|
||||||
|
(if-let [key' (yreq/get-header request "x-shared-key")]
|
||||||
|
(if (= key key')
|
||||||
|
(handler request)
|
||||||
|
{::yres/status 403})
|
||||||
|
{::yres/status 403}))
|
||||||
|
(fn [_ _]
|
||||||
|
{::yres/status 403}))))})
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::setup/props] :as cfg}]
|
[_ cfg]
|
||||||
|
|
||||||
(let [management-key (or (cf/get :management-api-key)
|
["" {:middleware [[shared-key-auth (cf/get :management-api-key)]
|
||||||
(get props :management-key))]
|
|
||||||
|
|
||||||
["" {:middleware [[mw/shared-key-auth management-key]
|
|
||||||
[default-system cfg]
|
[default-system cfg]
|
||||||
[transaction]]}
|
[transaction]]}
|
||||||
["/authenticate"
|
["/authenticate"
|
||||||
@@ -70,7 +82,7 @@
|
|||||||
["/update-customer"
|
["/update-customer"
|
||||||
{:handler update-customer
|
{:handler update-customer
|
||||||
:allowed-methods #{:post}
|
:allowed-methods #{:post}
|
||||||
:transaction true}]]))
|
:transaction true}]])
|
||||||
|
|
||||||
;; ---- HELPERS
|
;; ---- HELPERS
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
[app.http.errors :as errors]
|
[app.http.errors :as errors]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
[buddy.core.codecs :as bc]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[yetti.adapter :as yt]
|
[yetti.adapter :as yt]
|
||||||
[yetti.middleware :as ymw]
|
[yetti.middleware :as ymw]
|
||||||
@@ -301,16 +300,20 @@
|
|||||||
:compile (constantly wrap-auth)})
|
:compile (constantly wrap-auth)})
|
||||||
|
|
||||||
(defn- wrap-shared-key-auth
|
(defn- wrap-shared-key-auth
|
||||||
[handler shared-key]
|
[handler keys]
|
||||||
(if shared-key
|
(if (seq keys)
|
||||||
(let [shared-key (if (string? shared-key)
|
|
||||||
shared-key
|
|
||||||
(bc/bytes->b64-str shared-key true))]
|
|
||||||
(fn [request]
|
(fn [request]
|
||||||
(let [key (yreq/get-header request "x-shared-key")]
|
(if-let [[key-id key] (some-> (yreq/get-header request "x-shared-key")
|
||||||
(if (= key shared-key)
|
(str/split #"\s+" 2))]
|
||||||
(handler (assoc request ::http/auth-with-shared-key true))
|
(let [key-id (-> key-id str/lower keyword)]
|
||||||
{::yres/status 403}))))
|
(if (and (string? key)
|
||||||
|
(contains? keys key-id)
|
||||||
|
(= key (get keys key-id)))
|
||||||
|
(-> request
|
||||||
|
(assoc ::http/auth-key-id key-id)
|
||||||
|
(handler))
|
||||||
|
{::yres/status 403}))
|
||||||
|
{::yres/status 403}))
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
{::yres/status 403})))
|
{::yres/status 403})))
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
(ns app.http.sse
|
(ns app.http.sse
|
||||||
"SSE (server sent events) helpers"
|
"SSE (server sent events) helpers"
|
||||||
(:refer-clojure :exclude [tap])
|
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -140,10 +140,14 @@
|
|||||||
client-version (get-client-version request)
|
client-version (get-client-version request)
|
||||||
client-user-agent (get-client-user-agent request)
|
client-user-agent (get-client-user-agent request)
|
||||||
session-id (get-external-session-id request)
|
session-id (get-external-session-id request)
|
||||||
token-id (::actoken/id request)]
|
key-id (::http/auth-key-id request)
|
||||||
|
token-id (::actoken/id request)
|
||||||
|
token-type (::actoken/type request)]
|
||||||
(d/without-nils
|
(d/without-nils
|
||||||
{:external-session-id session-id
|
{:external-session-id session-id
|
||||||
|
:initiator (or key-id "app")
|
||||||
:access-token-id (some-> token-id str)
|
:access-token-id (some-> token-id str)
|
||||||
|
:access-token-type (some-> token-type str)
|
||||||
:client-event-origin client-event-origin
|
:client-event-origin client-event-origin
|
||||||
:client-user-agent client-user-agent
|
:client-user-agent client-user-agent
|
||||||
:client-version client-version
|
:client-version client-version
|
||||||
|
|||||||
@@ -275,8 +275,7 @@
|
|||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
::mgmt/routes
|
::mgmt/routes
|
||||||
{::db/pool (ig/ref ::db/pool)
|
{::db/pool (ig/ref ::db/pool)}
|
||||||
::setup/props (ig/ref ::setup/props)}
|
|
||||||
|
|
||||||
:app.http/router
|
:app.http/router
|
||||||
{::session/manager (ig/ref ::session/manager)
|
{::session/manager (ig/ref ::session/manager)
|
||||||
@@ -323,6 +322,7 @@
|
|||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::rds/pool (ig/ref ::rds/pool)
|
::rds/pool (ig/ref ::rds/pool)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::wrk/executor (ig/ref ::wrk/netty-executor)
|
::wrk/executor (ig/ref ::wrk/netty-executor)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::ldap/provider (ig/ref ::ldap/provider)
|
::ldap/provider (ig/ref ::ldap/provider)
|
||||||
@@ -339,6 +339,10 @@
|
|||||||
::email/blacklist (ig/ref ::email/blacklist)
|
::email/blacklist (ig/ref ::email/blacklist)
|
||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
|
:app.nitrate/client
|
||||||
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
:app.rpc/management-methods
|
:app.rpc/management-methods
|
||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
@@ -348,6 +352,7 @@
|
|||||||
::sto/storage (ig/ref ::sto/storage)
|
::sto/storage (ig/ref ::sto/storage)
|
||||||
::mtx/metrics (ig/ref ::mtx/metrics)
|
::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::rds/client (ig/ref ::rds/client)
|
::rds/client (ig/ref ::rds/client)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/props (ig/ref ::setup/props)}
|
||||||
|
|
||||||
@@ -358,7 +363,7 @@
|
|||||||
;; FIXME: revisit if db/pool is necessary here
|
;; FIXME: revisit if db/pool is necessary here
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
::wrk/registry
|
::wrk/registry
|
||||||
{::mtx/metrics (ig/ref ::mtx/metrics)
|
{::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
@@ -446,6 +451,11 @@
|
|||||||
;; module requires the migrations to run before initialize.
|
;; module requires the migrations to run before initialize.
|
||||||
::migrations (ig/ref :app.migrations/migrations)}
|
::migrations (ig/ref :app.migrations/migrations)}
|
||||||
|
|
||||||
|
::setup/shared-keys
|
||||||
|
{::setup/props (ig/ref ::setup/props)
|
||||||
|
:nitrate (cf/get :nitrate-shared-key)
|
||||||
|
:exporter (cf/get :exporter-shared-key)}
|
||||||
|
|
||||||
::setup/clock
|
::setup/clock
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
130
backend/src/app/nitrate.clj
Normal file
130
backend/src/app/nitrate.clj
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
(ns app.nitrate
|
||||||
|
"Module that make calls to the external nitrate aplication"
|
||||||
|
(:require
|
||||||
|
[app.common.logging :as l]
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.config :as cf]
|
||||||
|
[app.http.client :as http]
|
||||||
|
[app.rpc :as-alias rpc]
|
||||||
|
[app.setup :as-alias setup]
|
||||||
|
[app.util.json :as json]
|
||||||
|
[clojure.core :as c]
|
||||||
|
[integrant.core :as ig]))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; HELPERS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn- request-builder
|
||||||
|
[cfg method uri shared-key profile-id]
|
||||||
|
(fn []
|
||||||
|
(http/req! cfg {:method method
|
||||||
|
:headers {"content-type" "application/json"
|
||||||
|
"accept" "application/json"
|
||||||
|
"x-shared-key" shared-key
|
||||||
|
"x-profile-id" (str profile-id)}
|
||||||
|
:uri uri
|
||||||
|
:version :http1.1})))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- with-retries
|
||||||
|
[handler max-retries]
|
||||||
|
(fn []
|
||||||
|
(loop [attempt 1]
|
||||||
|
(let [result (try
|
||||||
|
(handler)
|
||||||
|
(catch Exception e
|
||||||
|
(if (< attempt max-retries)
|
||||||
|
::retry
|
||||||
|
(do
|
||||||
|
;; TODO Error handling
|
||||||
|
(l/error :hint "request fail after multiple retries" :cause e)
|
||||||
|
nil))))]
|
||||||
|
(if (= result ::retry)
|
||||||
|
(recur (inc attempt))
|
||||||
|
result)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- with-validate [handler uri schema]
|
||||||
|
(fn []
|
||||||
|
(let [coercer-http (sm/coercer schema
|
||||||
|
:type :validation
|
||||||
|
:hint (str "invalid data received calling " uri))]
|
||||||
|
(try
|
||||||
|
(coercer-http (-> (handler) :body json/decode))
|
||||||
|
(catch Exception e
|
||||||
|
;; TODO Error handling
|
||||||
|
(l/error :hint "error validating json response" :cause e)
|
||||||
|
nil)))))
|
||||||
|
|
||||||
|
(defn- request-to-nitrate
|
||||||
|
[cfg method uri schema {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [shared-key (-> cfg ::setup/shared-keys :nitrate)
|
||||||
|
full-http-call (-> (request-builder cfg method uri shared-key profile-id)
|
||||||
|
(with-retries 3)
|
||||||
|
(with-validate uri schema))]
|
||||||
|
(full-http-call)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; API
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn call
|
||||||
|
[cfg method params]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
(let [client (get cfg ::client)
|
||||||
|
method (get client method)]
|
||||||
|
(method params))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(def ^:private schema:organization
|
||||||
|
[:map
|
||||||
|
[:id ::sm/text]
|
||||||
|
[:name ::sm/text]])
|
||||||
|
|
||||||
|
(def ^:private schema:user
|
||||||
|
[:map
|
||||||
|
[:valid ::sm/boolean]])
|
||||||
|
|
||||||
|
(defn- get-team-org
|
||||||
|
[cfg {:keys [team-id] :as params}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params)))
|
||||||
|
|
||||||
|
(defn- is-valid-user
|
||||||
|
[cfg {:keys [profile-id] :as params}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; INITIALIZATION
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::client
|
||||||
|
[_ cfg]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
{:get-team-org (partial get-team-org cfg)
|
||||||
|
:is-valid-user (partial is-valid-user cfg)}))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; UTILS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn add-nitrate-licence-to-profile
|
||||||
|
[cfg profile]
|
||||||
|
(try
|
||||||
|
(let [nitrate-licence (call cfg :is-valid-user {:profile-id (:id profile)})]
|
||||||
|
(assoc profile :nitrate-licence (:valid nitrate-licence)))
|
||||||
|
(catch Throwable cause
|
||||||
|
(l/error :hint "failed to get nitrate licence"
|
||||||
|
:profile-id (:id profile)
|
||||||
|
:cause cause)
|
||||||
|
profile)))
|
||||||
|
|
||||||
|
(defn add-org-to-team
|
||||||
|
[cfg team params]
|
||||||
|
(let [params (assoc (or params {}) :team-id (:id team))
|
||||||
|
org (call cfg :get-team-org params)]
|
||||||
|
(assoc team :organization-id (:id org) :organization-name (:name org))))
|
||||||
@@ -92,11 +92,11 @@
|
|||||||
(fn [{:keys [params path-params method] :as request}]
|
(fn [{:keys [params path-params method] :as request}]
|
||||||
(let [handler-name (:type path-params)
|
(let [handler-name (:type path-params)
|
||||||
etag (yreq/get-header request "if-none-match")
|
etag (yreq/get-header request "if-none-match")
|
||||||
|
|
||||||
|
key-id (get request ::http/auth-key-id)
|
||||||
profile-id (or (::session/profile-id request)
|
profile-id (or (::session/profile-id request)
|
||||||
(::actoken/profile-id request)
|
(::actoken/profile-id request)
|
||||||
(if (::http/auth-with-shared-key request)
|
(if key-id uuid/zero nil))
|
||||||
uuid/zero
|
|
||||||
nil))
|
|
||||||
|
|
||||||
ip-addr (inet/parse-request request)
|
ip-addr (inet/parse-request request)
|
||||||
|
|
||||||
@@ -298,10 +298,12 @@
|
|||||||
|
|
||||||
(defn- resolve-management-methods
|
(defn- resolve-management-methods
|
||||||
[cfg]
|
[cfg]
|
||||||
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
|
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)
|
||||||
(->> (sv/scan-ns
|
mods (cond->> (list 'app.rpc.management.exporter)
|
||||||
'app.rpc.management.subscription
|
(contains? cf/flags :nitrate)
|
||||||
'app.rpc.management.exporter)
|
(cons 'app.rpc.management.nitrate))]
|
||||||
|
|
||||||
|
(->> (apply sv/scan-ns mods)
|
||||||
(map (partial process-method cfg "management" wrap-management))
|
(map (partial process-method cfg "management" wrap-management))
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|
||||||
@@ -345,23 +347,20 @@
|
|||||||
|
|
||||||
(defmethod ig/assert-key ::routes
|
(defmethod ig/assert-key ::routes
|
||||||
[_ params]
|
[_ params]
|
||||||
|
(assert (map? (::setup/shared-keys params)))
|
||||||
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
||||||
(assert (some? (::setup/props params)))
|
|
||||||
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
||||||
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
||||||
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::methods ::management-methods ::setup/props] :as cfg}]
|
[_ {:keys [::methods ::management-methods ::setup/shared-keys] :as cfg}]
|
||||||
|
|
||||||
(let [public-uri (cf/get :public-uri)
|
|
||||||
management-key (or (cf/get :management-api-key)
|
|
||||||
(get props :management-key))]
|
|
||||||
|
|
||||||
|
(let [public-uri (cf/get :public-uri)]
|
||||||
["/api"
|
["/api"
|
||||||
["/management"
|
["/management"
|
||||||
["/methods/:type"
|
["/methods/:type"
|
||||||
{:middleware [[mw/shared-key-auth management-key]
|
{:middleware [[mw/shared-key-auth shared-keys]
|
||||||
[session/authz cfg]]
|
[session/authz cfg]]
|
||||||
:handler (make-rpc-handler management-methods)}]
|
:handler (make-rpc-handler management-methods)}]
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.climit :as climit]
|
[app.rpc.climit :as climit]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -88,6 +89,8 @@
|
|||||||
|
|
||||||
;; --- QUERY: Get profile (own)
|
;; --- QUERY: Get profile (own)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(sv/defmethod ::get-profile
|
(sv/defmethod ::get-profile
|
||||||
{::rpc/auth false
|
{::rpc/auth false
|
||||||
::doc/added "1.18"
|
::doc/added "1.18"
|
||||||
@@ -98,9 +101,13 @@
|
|||||||
;; no profile-id is in session, and when db call raises not found. In all other
|
;; no profile-id is in session, and when db call raises not found. In all other
|
||||||
;; cases we need to reraise the exception.
|
;; cases we need to reraise the exception.
|
||||||
(try
|
(try
|
||||||
(-> (get-profile pool profile-id)
|
(let [profile (-> (get-profile pool profile-id)
|
||||||
(strip-private-attrs)
|
(strip-private-attrs)
|
||||||
(update :props filter-props))
|
(update :props filter-props))]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(nitrate/add-nitrate-licence-to-profile cfg profile)
|
||||||
|
profile))
|
||||||
|
|
||||||
(catch Throwable _
|
(catch Throwable _
|
||||||
{:id uuid/zero :fullname "Anonymous User"})))
|
{:id uuid/zero :fullname "Anonymous User"})))
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
[app.msgbus :as mbus]
|
[app.msgbus :as mbus]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.profile :as profile]
|
[app.rpc.commands.profile :as profile]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -190,7 +191,9 @@
|
|||||||
::sm/params schema:get-teams}
|
::sm/params schema:get-teams}
|
||||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(dm/with-open [conn (db/open pool)]
|
||||||
(get-teams conn profile-id)))
|
(cond->> (get-teams conn profile-id)
|
||||||
|
(contains? cf/flags :nitrate)
|
||||||
|
(map #(nitrate/add-org-to-team cfg % params)))))
|
||||||
|
|
||||||
(def ^:private sql:get-owned-teams
|
(def ^:private sql:get-owned-teams
|
||||||
"SELECT t.id, t.name,
|
"SELECT t.id, t.name,
|
||||||
|
|||||||
82
backend/src/app/rpc/management/nitrate.clj
Normal file
82
backend/src/app/rpc/management/nitrate.clj
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.rpc.management.nitrate
|
||||||
|
"Internal Nitrate HTTP RPC API. Provides authenticated access to
|
||||||
|
organization management and token validation endpoints."
|
||||||
|
(:require
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.common.types.profile :refer [schema:profile]]
|
||||||
|
[app.common.types.team :refer [schema:team]]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.db :as db]
|
||||||
|
[app.msgbus :as mbus]
|
||||||
|
[app.rpc :as-alias rpc]
|
||||||
|
[app.rpc.commands.files :as files]
|
||||||
|
[app.rpc.commands.profile :as profile]
|
||||||
|
[app.rpc.doc :as doc]
|
||||||
|
[app.util.services :as sv]))
|
||||||
|
|
||||||
|
;; ---- API: authenticate
|
||||||
|
|
||||||
|
(sv/defmethod ::authenticate
|
||||||
|
"Authenticate the current user"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::sm/result schema:profile}
|
||||||
|
[cfg {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [profile (profile/get-profile cfg profile-id)]
|
||||||
|
{:id (get profile :id)
|
||||||
|
:name (get profile :fullname)
|
||||||
|
:email (get profile :email)
|
||||||
|
:photo-url (files/resolve-public-uri (get profile :photo-id))}))
|
||||||
|
|
||||||
|
;; ---- API: get-teams
|
||||||
|
|
||||||
|
(def ^:private sql:get-teams
|
||||||
|
"SELECT t.*
|
||||||
|
FROM team AS t
|
||||||
|
JOIN team_profile_rel AS tpr ON t.id = tpr.team_id
|
||||||
|
WHERE tpr.profile_id = ?
|
||||||
|
AND tpr.is_owner IS TRUE
|
||||||
|
AND t.is_default IS FALSE
|
||||||
|
AND t.deleted_at IS NULL;")
|
||||||
|
|
||||||
|
(def ^:private schema:get-teams-result
|
||||||
|
[:vector schema:team])
|
||||||
|
|
||||||
|
(sv/defmethod ::get-teams
|
||||||
|
"List teams for which current user is owner"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::sm/result schema:get-teams-result}
|
||||||
|
[cfg {:keys [::rpc/profile-id]}]
|
||||||
|
(let [current-user-id (-> (profile/get-profile cfg profile-id) :id)]
|
||||||
|
(->> (db/exec! cfg [sql:get-teams current-user-id])
|
||||||
|
(map #(select-keys % [:id :name])))))
|
||||||
|
|
||||||
|
;; ---- API: notify-team-change
|
||||||
|
|
||||||
|
(def ^:private schema:notify-team-change
|
||||||
|
[:map
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:organization-id ::sm/text]])
|
||||||
|
|
||||||
|
(sv/defmethod ::notify-team-change
|
||||||
|
"Notify to Penpot a team change from nitrate"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params schema:notify-team-change
|
||||||
|
::rpc/auth false}
|
||||||
|
[cfg {:keys [id organization-id organization-name]}]
|
||||||
|
(let [msgbus (::mbus/msgbus cfg)]
|
||||||
|
(mbus/pub! msgbus
|
||||||
|
;;TODO There is a bug on dashboard with teams notifications.
|
||||||
|
;;For now we send it to uuid/zero instead of team-id
|
||||||
|
:topic uuid/zero
|
||||||
|
:message {:type :team-org-change
|
||||||
|
:team-id id
|
||||||
|
:organization-id organization-id
|
||||||
|
:organization-name organization-name})))
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) KALEIDOS INC
|
|
||||||
|
|
||||||
(ns app.rpc.management.subscription
|
|
||||||
(:require
|
|
||||||
[app.common.logging :as l]
|
|
||||||
[app.common.schema :as sm]
|
|
||||||
[app.common.schema.generators :as sg]
|
|
||||||
[app.common.time :as ct]
|
|
||||||
[app.db :as db]
|
|
||||||
[app.rpc :as-alias rpc]
|
|
||||||
[app.rpc.commands.profile :as profile]
|
|
||||||
[app.rpc.doc :as doc]
|
|
||||||
[app.util.services :as sv]))
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: AUTHENTICATE
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-params
|
|
||||||
[:map {:title "authenticate-params"}])
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-result
|
|
||||||
[:map {:title "authenticate-result"}
|
|
||||||
[:profile-id ::sm/uuid]])
|
|
||||||
|
|
||||||
(sv/defmethod ::auth
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:authenticate-params
|
|
||||||
::sm/result schema:authenticate-result}
|
|
||||||
[_ {:keys [::rpc/profile-id]}]
|
|
||||||
{:profile-id profile-id})
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
;; FIXME: move to app.common.time
|
|
||||||
(def ^:private schema:timestamp
|
|
||||||
(sm/type-schema
|
|
||||||
{:type ::timestamp
|
|
||||||
:pred ct/inst?
|
|
||||||
:type-properties
|
|
||||||
{:title "inst"
|
|
||||||
:description "The same as :app.common.time/inst but encodes to epoch"
|
|
||||||
:error/message "should be an instant"
|
|
||||||
:gen/gen (->> (sg/small-int)
|
|
||||||
(sg/fmap (fn [v] (ct/inst v))))
|
|
||||||
:decode/string #(some-> % ct/inst)
|
|
||||||
:encode/string #(some-> % inst-ms)
|
|
||||||
:decode/json #(some-> % ct/inst)
|
|
||||||
:encode/json #(some-> % inst-ms)}}))
|
|
||||||
|
|
||||||
(def ^:private schema:subscription
|
|
||||||
[:map {:title "Subscription"}
|
|
||||||
[:id ::sm/text]
|
|
||||||
[:customer-id ::sm/text]
|
|
||||||
[:type [:enum
|
|
||||||
"unlimited"
|
|
||||||
"professional"
|
|
||||||
"enterprise"]]
|
|
||||||
[:status [:enum
|
|
||||||
"active"
|
|
||||||
"canceled"
|
|
||||||
"incomplete"
|
|
||||||
"incomplete_expired"
|
|
||||||
"past_due"
|
|
||||||
"paused"
|
|
||||||
"trialing"
|
|
||||||
"unpaid"]]
|
|
||||||
|
|
||||||
[:billing-period [:enum
|
|
||||||
"month"
|
|
||||||
"day"
|
|
||||||
"week"
|
|
||||||
"year"]]
|
|
||||||
[:quantity :int]
|
|
||||||
[:description [:maybe ::sm/text]]
|
|
||||||
[:created-at schema:timestamp]
|
|
||||||
[:start-date [:maybe schema:timestamp]]
|
|
||||||
[:ended-at [:maybe schema:timestamp]]
|
|
||||||
[:trial-end [:maybe schema:timestamp]]
|
|
||||||
[:trial-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at [:maybe schema:timestamp]]
|
|
||||||
[:canceled-at [:maybe schema:timestamp]]
|
|
||||||
[:current-period-end [:maybe schema:timestamp]]
|
|
||||||
[:current-period-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at-period-end :boolean]
|
|
||||||
|
|
||||||
[:cancellation-details
|
|
||||||
[:map {:title "CancellationDetails"}
|
|
||||||
[:comment [:maybe ::sm/text]]
|
|
||||||
[:reason [:maybe ::sm/text]]
|
|
||||||
[:feedback [:maybe
|
|
||||||
[:enum
|
|
||||||
"customer_service"
|
|
||||||
"low_quality"
|
|
||||||
"missing_feature"
|
|
||||||
"other"
|
|
||||||
"switched_service"
|
|
||||||
"too_complex"
|
|
||||||
"too_expensive"
|
|
||||||
"unused"]]]]]])
|
|
||||||
|
|
||||||
(def ^:private sql:get-customer-slots
|
|
||||||
"WITH teams AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
tpr.profile_id AS profile_id
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.is_owner IS true
|
|
||||||
AND tpr.profile_id = ?
|
|
||||||
), teams_with_slots AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
count(*) AS total
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.team_id IN (SELECT id FROM teams)
|
|
||||||
AND tpr.can_edit IS true
|
|
||||||
GROUP BY 1
|
|
||||||
ORDER BY 2
|
|
||||||
)
|
|
||||||
SELECT max(total) AS total FROM teams_with_slots;")
|
|
||||||
|
|
||||||
(defn- get-customer-slots
|
|
||||||
[cfg profile-id]
|
|
||||||
(let [result (db/exec-one! cfg [sql:get-customer-slots profile-id])]
|
|
||||||
(:total result)))
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-params
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-result
|
|
||||||
[:map
|
|
||||||
[:id ::sm/uuid]
|
|
||||||
[:name :string]
|
|
||||||
[:num-editors ::sm/int]
|
|
||||||
[:subscription {:optional true} schema:subscription]])
|
|
||||||
|
|
||||||
(sv/defmethod ::get-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:get-customer-params
|
|
||||||
::sm/result schema:get-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id]}]
|
|
||||||
(let [profile (profile/get-profile cfg profile-id)]
|
|
||||||
{:id (get profile :id)
|
|
||||||
:name (get profile :fullname)
|
|
||||||
:email (get profile :email)
|
|
||||||
:num-editors (get-customer-slots cfg profile-id)
|
|
||||||
:subscription (-> profile :props :subscription)}))
|
|
||||||
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-params
|
|
||||||
[:map
|
|
||||||
[:subscription [:maybe schema:subscription]]])
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-result
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(sv/defmethod ::update-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:update-customer-params
|
|
||||||
::sm/result schema:update-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id subscription]}]
|
|
||||||
(let [{:keys [props] :as profile}
|
|
||||||
(profile/get-profile cfg profile-id ::db/for-update true)
|
|
||||||
|
|
||||||
props
|
|
||||||
(assoc props :subscription subscription)]
|
|
||||||
|
|
||||||
(l/dbg :hint "update customer"
|
|
||||||
:profile-id (str profile-id)
|
|
||||||
:subscription-type (get subscription :type)
|
|
||||||
:subscription-status (get subscription :status)
|
|
||||||
:subscription-quantity (get subscription :quantity))
|
|
||||||
|
|
||||||
(db/update! cfg :profile
|
|
||||||
{:props (db/tjson props)}
|
|
||||||
{:id profile-id}
|
|
||||||
{::db/return-keys false})
|
|
||||||
|
|
||||||
nil))
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
[app.setup.templates]
|
[app.setup.templates]
|
||||||
[buddy.core.codecs :as bc]
|
[buddy.core.codecs :as bc]
|
||||||
[buddy.core.nonce :as bn]
|
[buddy.core.nonce :as bn]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[integrant.core :as ig]))
|
[integrant.core :as ig]))
|
||||||
|
|
||||||
(defn- generate-random-key
|
(defn- generate-random-key
|
||||||
@@ -88,7 +89,38 @@
|
|||||||
(-> (get-all-props conn)
|
(-> (get-all-props conn)
|
||||||
(assoc :secret-key secret)
|
(assoc :secret-key secret)
|
||||||
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
||||||
(assoc :management-key (keys/derive secret :salt "management"))
|
|
||||||
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
||||||
|
|
||||||
(sm/register! ::props [:map-of :keyword ::sm/any])
|
(sm/register! ::props [:map-of :keyword ::sm/any])
|
||||||
|
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::shared-keys
|
||||||
|
[_ {:keys [::props] :as cfg}]
|
||||||
|
(let [secret (get props :secret-key)]
|
||||||
|
(d/without-nils
|
||||||
|
{:exporter
|
||||||
|
(let [key (or (get cfg :exporter)
|
||||||
|
(-> (keys/derive secret :salt "exporter")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "exporter key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))
|
||||||
|
|
||||||
|
:nitrate
|
||||||
|
(let [key (or (get cfg :nitrate)
|
||||||
|
(-> (keys/derive secret :salt "nitrate")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "nitrate key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "nitrate key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))})))
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"A generic asynchronous events notifications subsystem; used mainly
|
"A generic asynchronous events notifications subsystem; used mainly
|
||||||
for mark event points in functions and be able to attach listeners
|
for mark event points in functions and be able to attach listeners
|
||||||
to them. Mainly used in http.sse for progress reporting."
|
to them. Mainly used in http.sse for progress reporting."
|
||||||
(:refer-clojure :exclude [tap run!])
|
(:refer-clojure :exclude [run!])
|
||||||
(:require
|
(:require
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
(t/deftest shared-key-auth
|
(t/deftest shared-key-auth
|
||||||
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
||||||
(fn [req] {::yres/status 200})
|
(fn [req] {::yres/status 200})
|
||||||
"secret-key")]
|
{:test1 "secret-key"})]
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {} {}))]
|
(let [response (handler (->DummyRequest {} {}))]
|
||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
@@ -95,6 +95,9 @@
|
|||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
||||||
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
|
(let [response (handler (->DummyRequest {"x-shared-key" "test1 secret-key"} {}))]
|
||||||
(t/is (= 200 (::yres/status response))))))
|
(t/is (= 200 (::yres/status response))))))
|
||||||
|
|
||||||
(t/deftest access-token-authz
|
(t/deftest access-token-authz
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -241,7 +241,20 @@
|
|||||||
(with-out-str
|
(with-out-str
|
||||||
(print-all cause)))))
|
(print-all cause)))))
|
||||||
|
|
||||||
#?(:clj
|
|
||||||
(defn print-throwable
|
(defn print-throwable
|
||||||
[cause & {:as opts}]
|
[cause & {:as opts}]
|
||||||
(println (format-throwable cause opts))))
|
#?(:clj
|
||||||
|
(println (format-throwable cause opts))
|
||||||
|
:cljs
|
||||||
|
(let [prefix (get opts :prefix "exception")
|
||||||
|
title (str prefix ": " (ex-message cause))
|
||||||
|
exdata (ex-data cause)]
|
||||||
|
(js/console.group title)
|
||||||
|
(when-let [explain (get exdata ::sm/explain)]
|
||||||
|
(println (sm/humanize-explain explain)))
|
||||||
|
|
||||||
|
(js/console.log "\nData:")
|
||||||
|
(pp/pprint (dissoc exdata ::sm/explain))
|
||||||
|
|
||||||
|
(js/console.log "\nTrace:")
|
||||||
|
(js/console.error (.-stack cause)))))
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set parent to root frame.
|
;; Set parent to root frame.
|
||||||
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
||||||
(assoc shape :parent-id uuid/zero))]
|
(assoc shape :parent-id uuid/zero))]
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [parent-shape]
|
(fn [parent-shape]
|
||||||
; Add shape to parent's children list
|
;; Add shape to parent's children list
|
||||||
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
||||||
(update parent-shape :shapes conj (:id shape)))]
|
(update parent-shape :shapes conj (:id shape)))]
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove duplicated
|
;; Remove duplicated
|
||||||
(log/debug :hint " -> remove duplicated children")
|
(log/debug :hint " -> remove duplicated children")
|
||||||
(update shape :shapes distinct))]
|
(update shape :shapes distinct))]
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set the :shape as main instance root
|
;; Set the :shape as main instance root
|
||||||
(log/debug :hint " -> set :main-instance")
|
(log/debug :hint " -> set :main-instance")
|
||||||
(assoc shape :main-instance true))]
|
(assoc shape :main-instance true))]
|
||||||
|
|
||||||
@@ -147,10 +147,11 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set :component-file to local file
|
;; Set :component-file to local file
|
||||||
(log/debug :hint " -> set :component-file to local file")
|
(log/debug :hint " -> set :component-file to local file")
|
||||||
(assoc shape :component-file (:id file-data)))]
|
(assoc shape :component-file (:id file-data)))]
|
||||||
; There is no solution that may recover it with confidence
|
|
||||||
|
;; There is no solution that may recover it with confidence
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
;; shape)]
|
;; shape)]
|
||||||
|
|
||||||
@@ -166,10 +167,10 @@
|
|||||||
|
|
||||||
repair-shape
|
repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
;; shape)]
|
;; shape)]
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@
|
|||||||
|
|
||||||
repair-component
|
repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
||||||
(assoc component :main-instance-id (:id shape)))
|
(assoc component :main-instance-id (:id shape)))
|
||||||
|
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
||||||
(assoc component :main-instance-page page-id))]
|
(assoc component :main-instance-page page-id))]
|
||||||
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -232,7 +233,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Unset the :shape as main instance root
|
;; Unset the :shape as main instance root
|
||||||
(log/debug :hint " -> unset :main-instance")
|
(log/debug :hint " -> unset :main-instance")
|
||||||
(dissoc shape :main-instance))]
|
(dissoc shape :main-instance))]
|
||||||
|
|
||||||
@@ -245,7 +246,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy root.
|
;; Convert the shape in a nested copy root.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -307,8 +308,8 @@
|
|||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
; If the shape still refers to the remote component, try to find the corresponding near one
|
;; If the shape still refers to the remote component, try to find the corresponding near one
|
||||||
; and link to it. If not, detach the shape.
|
;; and link to it. If not, detach the shape.
|
||||||
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(if (some? matching-shape)
|
(if (some? matching-shape)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -324,7 +325,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a normal copy, removing nested copy status
|
;; Convert shape in a normal copy, removing nested copy status
|
||||||
(log/debug :hint " -> unhead shape")
|
(log/debug :hint " -> unhead shape")
|
||||||
(ctk/unhead-shape shape))]
|
(ctk/unhead-shape shape))]
|
||||||
|
|
||||||
@@ -337,7 +338,7 @@
|
|||||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a nested head, adding component info
|
;; Convert shape in a nested head, adding component info
|
||||||
(log/debug :hint " -> reroot shape")
|
(log/debug :hint " -> reroot shape")
|
||||||
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
||||||
|
|
||||||
@@ -350,7 +351,8 @@
|
|||||||
[_ {:keys [shape args] :as error} file-data _]
|
[_ {:keys [shape args] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
(let [objects (:objects component) ;; we only have encounter this on deleted components,
|
(let [objects (:objects component)
|
||||||
|
;; we only have encounter this on deleted components,
|
||||||
;; so the relevant objects are inside the component
|
;; so the relevant objects are inside the component
|
||||||
to-detach (->> (:cycles-ids args)
|
to-detach (->> (:cycles-ids args)
|
||||||
(map #(get objects %))
|
(map #(get objects %))
|
||||||
@@ -378,7 +380,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove shape-ref
|
;; Remove shape-ref
|
||||||
(log/debug :hint " -> unset :shape-ref")
|
(log/debug :hint " -> unset :shape-ref")
|
||||||
(dissoc shape :shape-ref))]
|
(dissoc shape :shape-ref))]
|
||||||
|
|
||||||
@@ -391,7 +393,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested main head.
|
;; Convert the shape in a nested main head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -404,7 +406,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top main head.
|
;; Convert the shape in a top main head.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -418,7 +420,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy head.
|
;; Convert the shape in a nested copy head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -431,7 +433,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -444,7 +446,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -457,7 +459,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -470,7 +472,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -483,7 +485,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a frame.
|
;; Convert the shape in a frame.
|
||||||
(log/debug :hint " -> set :type :frame")
|
(log/debug :hint " -> set :type :frame")
|
||||||
(assoc shape :type :frame
|
(assoc shape :type :frame
|
||||||
:fills []
|
:fills []
|
||||||
@@ -502,7 +504,7 @@
|
|||||||
[_ {:keys [shape] :as error} file-data _]
|
[_ {:keys [shape] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Remove the objects key, or set it to {} if the component is deleted
|
;; Remove the objects key, or set it to {} if the component is deleted
|
||||||
(if (:deleted component)
|
(if (:deleted component)
|
||||||
(do
|
(do
|
||||||
(log/debug :hint " -> set :objects {}")
|
(log/debug :hint " -> set :objects {}")
|
||||||
|
|||||||
@@ -148,7 +148,10 @@
|
|||||||
|
|
||||||
;; A temporal flag, enables backend code use more extensivelly
|
;; A temporal flag, enables backend code use more extensivelly
|
||||||
;; redis for caching data
|
;; redis for caching data
|
||||||
:redis-cache})
|
:redis-cache
|
||||||
|
|
||||||
|
;; Activates the nitrate module
|
||||||
|
:nitrate})
|
||||||
|
|
||||||
(def all-flags
|
(def all-flags
|
||||||
(set/union email login varia))
|
(set/union email login varia))
|
||||||
|
|||||||
@@ -124,22 +124,23 @@
|
|||||||
|
|
||||||
(defn adjust-to-viewport
|
(defn adjust-to-viewport
|
||||||
([viewport srect] (adjust-to-viewport viewport srect nil))
|
([viewport srect] (adjust-to-viewport viewport srect nil))
|
||||||
([viewport srect {:keys [padding] :or {padding 0}}]
|
([viewport srect {:keys [padding min-zoom] :or {padding 0 min-zoom nil}}]
|
||||||
(let [gprop (/ (:width viewport)
|
(let [gprop (/ (:width viewport)
|
||||||
(:height viewport))
|
(:height viewport))
|
||||||
srect (-> srect
|
srect-padded (-> srect
|
||||||
(update :x #(- % padding))
|
(update :x #(- % padding))
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(update :width #(+ % padding padding))
|
(update :width #(+ % padding padding))
|
||||||
(update :height #(+ % padding padding)))
|
(update :height #(+ % padding padding)))
|
||||||
width (:width srect)
|
width (:width srect-padded)
|
||||||
height (:height srect)
|
height (:height srect-padded)
|
||||||
lprop (/ width height)]
|
lprop (/ width height)
|
||||||
|
adjusted-rect
|
||||||
(cond
|
(cond
|
||||||
(> gprop lprop)
|
(> gprop lprop)
|
||||||
(let [width' (* (/ width lprop) gprop)
|
(let [width' (* (/ width lprop) gprop)
|
||||||
padding (/ (- width' width) 2)]
|
padding (/ (- width' width) 2)]
|
||||||
(-> srect
|
(-> srect-padded
|
||||||
(update :x #(- % padding))
|
(update :x #(- % padding))
|
||||||
(assoc :width width')
|
(assoc :width width')
|
||||||
(grc/update-rect :position)))
|
(grc/update-rect :position)))
|
||||||
@@ -147,10 +148,27 @@
|
|||||||
(< gprop lprop)
|
(< gprop lprop)
|
||||||
(let [height' (/ (* height lprop) gprop)
|
(let [height' (/ (* height lprop) gprop)
|
||||||
padding (/ (- height' height) 2)]
|
padding (/ (- height' height) 2)]
|
||||||
(-> srect
|
(-> srect-padded
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(assoc :height height')
|
(assoc :height height')
|
||||||
(grc/update-rect :position)))
|
(grc/update-rect :position)))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(grc/update-rect srect :position)))))
|
(grc/update-rect srect-padded :position))]
|
||||||
|
;; If min-zoom is specified and the resulting zoom would be below it,
|
||||||
|
;; return a rect with the original top-left corner centered in the viewport
|
||||||
|
;; instead of using the aspect-ratio-adjusted rect (which can push coords
|
||||||
|
;; extremely far with extreme aspect ratios).
|
||||||
|
(if (and (some? min-zoom)
|
||||||
|
(< (/ (:width viewport) (:width adjusted-rect)) min-zoom))
|
||||||
|
(let [anchor-x (:x srect)
|
||||||
|
anchor-y (:y srect)
|
||||||
|
vbox-width (/ (:width viewport) min-zoom)
|
||||||
|
vbox-height (/ (:height viewport) min-zoom)]
|
||||||
|
(-> adjusted-rect
|
||||||
|
(assoc :x (- anchor-x (/ vbox-width 2))
|
||||||
|
:y (- anchor-y (/ vbox-height 2))
|
||||||
|
:width vbox-width
|
||||||
|
:height vbox-height)
|
||||||
|
(grc/update-rect :position)))
|
||||||
|
adjusted-rect))))
|
||||||
|
|||||||
@@ -1898,10 +1898,10 @@
|
|||||||
(gsh/absolute-move shape new-pos)))
|
(gsh/absolute-move shape new-pos)))
|
||||||
|
|
||||||
(defn- switch-path-change-value
|
(defn- switch-path-change-value
|
||||||
[prev-shape ;; The shape before the switch
|
[prev-shape ; The shape before the switch
|
||||||
current-shape ;; The shape after the switch (a clean copy)
|
current-shape ; The shape after the switch (a clean copy)
|
||||||
ref-shape ;; The referenced shape on the main component
|
ref-shape ; The referenced shape on the main component
|
||||||
;; before the switch
|
; before the switch
|
||||||
attr]
|
attr]
|
||||||
(let [old-width (-> ref-shape :selrect :width)
|
(let [old-width (-> ref-shape :selrect :width)
|
||||||
new-width (-> prev-shape :selrect :width)
|
new-width (-> prev-shape :selrect :width)
|
||||||
@@ -1918,10 +1918,9 @@
|
|||||||
|
|
||||||
|
|
||||||
(defn- switch-text-change-value
|
(defn- switch-text-change-value
|
||||||
[prev-content ;; The :content of the text before the switch
|
[prev-content ; The :content of the text before the switch
|
||||||
current-content ;; The :content of the text after the switch (a clean copy)
|
current-content ; The :content of the text after the switch (a clean copy)
|
||||||
ref-content touched] ;; The :content of the referenced text on the main component
|
ref-content touched] ; The :content of the referenced text on the main component before the switch
|
||||||
;; before the switch
|
|
||||||
(let [;; We need the differences between the contents on the main
|
(let [;; We need the differences between the contents on the main
|
||||||
;; components. current-content is the content of a clean copy,
|
;; components. current-content is the content of a clean copy,
|
||||||
;; so for all effects its the same as the content on its main
|
;; so for all effects its the same as the content on its main
|
||||||
|
|||||||
@@ -13,14 +13,15 @@
|
|||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.schema.generators :as sg]))
|
[app.common.schema.generators :as sg]))
|
||||||
|
|
||||||
;; WARNING: options are not deleted when changing event or action type, so it can be
|
;; WARNING: options are not deleted when changing event or action
|
||||||
;; restored if the user changes it back later.
|
;; type, so it can be restored if the user changes it back later.
|
||||||
;;
|
;;
|
||||||
;; But that means that an interaction may have for example a delay or
|
;; But that means that an interaction may have for example a delay or
|
||||||
;; destination, even if its type does not require it (but a previous type did).
|
;; destination, even if its type does not require it (but a previous
|
||||||
|
;; type did).
|
||||||
;;
|
;;
|
||||||
;; So make sure to use has-delay/has-destination... functions, or similar,
|
;; So make sure to use has-delay/has-destination... functions, or
|
||||||
;; before reading them.
|
;; similar, before reading them.
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMA
|
;; SCHEMA
|
||||||
@@ -452,16 +453,15 @@
|
|||||||
(gpt/point 0 0)))
|
(gpt/point 0 0)))
|
||||||
|
|
||||||
(defn calc-overlay-position
|
(defn calc-overlay-position
|
||||||
[interaction ;; interaction data
|
[interaction ; interaction data
|
||||||
shape ;; Shape with the interaction
|
shape ; Shape with the interaction
|
||||||
objects ;; the objects tree
|
objects ; the objects tree
|
||||||
relative-to-shape ;; the interaction position is realtive to this
|
relative-to-shape ; the interaction position is realtive to this shape
|
||||||
;; sape
|
base-frame ; the base frame of the current interaction
|
||||||
base-frame ;; the base frame of the current interaction
|
dest-frame ; the frame to display with this interaction
|
||||||
dest-frame ;; the frame to display with this interaction
|
frame-offset] ; if this interaction starts in a frame opened
|
||||||
frame-offset] ;; if this interaction starts in a frame opened
|
; on another interaction, this is the position
|
||||||
;; on another interaction, this is the position
|
; of that frame
|
||||||
;; of that frame
|
|
||||||
(assert (check-interaction interaction))
|
(assert (check-interaction interaction))
|
||||||
(assert (has-overlay-opts interaction)
|
(assert (has-overlay-opts interaction)
|
||||||
"expected compatible interaction map")
|
"expected compatible interaction map")
|
||||||
|
|||||||
@@ -19,3 +19,10 @@
|
|||||||
|
|
||||||
(def schema:role
|
(def schema:role
|
||||||
[::sm/one-of {:title "TeamRole"} valid-roles])
|
[::sm/one-of {:title "TeamRole"} valid-roles])
|
||||||
|
|
||||||
|
;; FIXME: specify more fields
|
||||||
|
(def schema:team
|
||||||
|
[:map {:title "Team"}
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:name :string]])
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
(def token-name-ref
|
(def token-name-ref
|
||||||
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
||||||
#"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"])
|
#"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$"])
|
||||||
|
|
||||||
(def ^:private schema:color
|
(def ^:private schema:color
|
||||||
[:map
|
[:map
|
||||||
@@ -474,8 +474,6 @@
|
|||||||
:height #{:sizing :dimensions}
|
:height #{:sizing :dimensions}
|
||||||
:max-width #{:sizing :dimensions}
|
:max-width #{:sizing :dimensions}
|
||||||
:max-height #{:sizing :dimensions}
|
:max-height #{:sizing :dimensions}
|
||||||
:min-width #{:sizing :dimensions}
|
|
||||||
:min-height #{:sizing :dimensions}
|
|
||||||
:x #{:dimensions}
|
:x #{:dimensions}
|
||||||
:y #{:dimensions}
|
:y #{:dimensions}
|
||||||
:rotation #{:number :rotation}
|
:rotation #{:number :rotation}
|
||||||
|
|||||||
@@ -179,9 +179,9 @@ RUN set -eux; \
|
|||||||
|
|
||||||
FROM base AS setup-utils
|
FROM base AS setup-utils
|
||||||
|
|
||||||
ENV CLJKONDO_VERSION=2025.07.28 \
|
ENV CLJKONDO_VERSION=2026.01.19 \
|
||||||
BABASHKA_VERSION=1.12.208 \
|
BABASHKA_VERSION=1.12.208 \
|
||||||
CLJFMT_VERSION=0.13.1
|
CLJFMT_VERSION=0.15.6
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
ARCH="$(dpkg --print-architecture)"; \
|
ARCH="$(dpkg --print-architecture)"; \
|
||||||
@@ -398,7 +398,6 @@ COPY files/Caddyfile /home/
|
|||||||
COPY files/selfsigned.crt /home/
|
COPY files/selfsigned.crt /home/
|
||||||
COPY files/selfsigned.key /home/
|
COPY files/selfsigned.key /home/
|
||||||
COPY files/start-tmux.sh /home/start-tmux.sh
|
COPY files/start-tmux.sh /home/start-tmux.sh
|
||||||
COPY files/start-tmux-back.sh /home/start-tmux-back.sh
|
|
||||||
COPY files/entrypoint.sh /home/entrypoint.sh
|
COPY files/entrypoint.sh /home/entrypoint.sh
|
||||||
COPY files/init.sh /home/init.sh
|
COPY files/init.sh /home/init.sh
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
localhost:3449 {
|
localhost:3449 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
tls /home/selfsigned.crt /home/selfsigned.key
|
tls /home/selfsigned.crt /home/selfsigned.key
|
||||||
|
header -Strict-Transport-Security
|
||||||
}
|
}
|
||||||
|
|
||||||
http://localhost:3450 {
|
http://localhost:3450 {
|
||||||
|
|||||||
@@ -141,8 +141,14 @@ http {
|
|||||||
proxy_pass http://127.0.0.1:5000;
|
proxy_pass http://127.0.0.1:5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /nitrate/ {
|
location /control-center {
|
||||||
proxy_pass http://127.0.0.1:3000/;
|
proxy_pass http://127.0.0.1:3000;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /wasm-playground {
|
location /wasm-playground {
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ update_flags /var/www/app/js/config.js
|
|||||||
|
|
||||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
||||||
|
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
|
||||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
||||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
||||||
|
|
||||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||||
|
|||||||
@@ -139,6 +139,14 @@ http {
|
|||||||
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /control-center {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $http_cf_connecting_ip;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_pass $PENPOT_NITRATE_URI$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
include /etc/nginx/overrides/server.d/*.conf;
|
include /etc/nginx/overrides/server.d/*.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ desc: Create, deploy, and use the Penpot plugin API with our comprehensive docum
|
|||||||
|
|
||||||
# Penpot plugins API
|
# Penpot plugins API
|
||||||
|
|
||||||
We've got all the documentation you need for the API right <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/">here</a>.
|
We've got all the documentation you need for the API right <a target="_blank" href="https://doc.plugins.penpot.app/">here</a>.
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ desc: See the Penpot plugin API changelog for version 1.0! Find breaking changes
|
|||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
||||||
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
||||||
- We’ve redone the documentation. You can check the API here:
|
- We’ve redone the documentation. You can check the API here:
|
||||||
[https://penpot-plugins-api-doc.pages.dev/](https://penpot-plugins-api-doc.pages.dev/)
|
[https://doc.plugins.penpot.app/](https://doc.plugins.penpot.app/)
|
||||||
- New samples repository with lots of samples to use the API:
|
- New samples repository with lots of samples to use the API:
|
||||||
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
||||||
|
|
||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
||||||
|
|
||||||
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://penpot-plugins-api-doc.pages.dev/) for more details.
|
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://doc.plugins.penpot.app/) for more details.
|
||||||
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
||||||
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ There are two libraries that can help you with your plugin's development. They a
|
|||||||
|
|
||||||
### Plugin styles
|
### Plugin styles
|
||||||
|
|
||||||
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">Plugin styles</a>.
|
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://styles-doc.plugins.penpot.app/">Plugin styles</a>.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @penpot/plugin-styles
|
npm install @penpot/plugin-styles
|
||||||
@@ -139,7 +139,7 @@ parent.postMessage(responseMessage, targetOrigin);
|
|||||||
|
|
||||||
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
||||||
|
|
||||||
For more detailed information, refer to the [Penpot Plugins API Documentation](https://penpot-plugins-api-doc.pages.dev/).
|
For more detailed information, refer to the [Penpot Plugins API Documentation](https://doc.plugins.penpot.app/).
|
||||||
|
|
||||||
## 2.5. Step 5. Build the plugin file
|
## 2.5. Step 5. Build the plugin file
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ penpot.library.local.createTypography();
|
|||||||
|
|
||||||
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
||||||
|
|
||||||
Just a heads-up: if you use the <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
Just a heads-up: if you use the <a target="_blank" href="https://styles-doc.plugins.penpot.app/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
||||||
|
|
||||||
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ The plugin <a target="_blank" href="https://www.npmjs.com/package/@penpot/plugin
|
|||||||
|
|
||||||
### Is the API ready to use the prototyping features?
|
### Is the API ready to use the prototyping features?
|
||||||
|
|
||||||
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotFlow">PenpotFlow</a> or <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotInteraction">PenpotInteraction</a> interfaces.
|
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Flow">Flow</a> or <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Interaction">Interaction</a> interfaces.
|
||||||
|
|
||||||
### Are there any security or quality criteria I should be aware of?
|
### Are there any security or quality criteria I should be aware of?
|
||||||
|
|
||||||
@@ -48,7 +48,8 @@ There are no set requirements. However, we can recommend the use of <a target="_
|
|||||||
|
|
||||||
### Is it necessary to create plugins with a UI?
|
### Is it necessary to create plugins with a UI?
|
||||||
|
|
||||||
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette-penpot-plugin.pages.dev/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot-plugins/tree/main/apps/create-palette-plugin">here</a>
|
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette.plugins.penpot.app/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot/tree/main/plugins/apps/create-palette-plugin">here</a>
|
||||||
|
|
||||||
|
|
||||||
### Can I create components?
|
### Can I create components?
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ Yes, it is possible to create components using:
|
|||||||
createComponent(shapes: Shape[]): LibraryComponent;
|
createComponent(shapes: Shape[]): LibraryComponent;
|
||||||
```
|
```
|
||||||
|
|
||||||
Take a look at the Penpot Library methods in the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
Take a look at the Penpot Library methods in the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
||||||
|
|
||||||
### Is there a place where I can share my plugin?
|
### Is there a place where I can share my plugin?
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ You need to provide the plugin's manifest URL for the installation. If there are
|
|||||||
|
|
||||||
| Name | URL |
|
| Name | URL |
|
||||||
| ------------- | ------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------- |
|
||||||
| Lorem Ipsum | https://lorem-ipsum-penpot-plugin.pages.dev/assets/manifest.json |
|
| Color palette | https://create-palette.plugins.penpot.app/assets/manifest.json |
|
||||||
| Contrast | https://contrast-penpot-plugin.pages.dev/assets/manifest.json |
|
| Contrast | https://contrast.plugins.penpot.app/assets/manifest.json |
|
||||||
| Feather icons | https://icons-penpot-plugin.pages.dev/assets/manifest.json |
|
| Feather icons | https://icons.plugins.penpot.app/assets/manifest.json |
|
||||||
| Tables | https://table-penpot-plugin.pages.dev/assets/manifest.json |
|
| Lorem ipsum | https://lorem-ipsum.plugins.penpot.app/assets/manifest.json |
|
||||||
| Color palette | https://create-palette-penpot-plugin.pages.dev/assets/manifest.json |
|
| Rename layers | https://rename-layers.plugins.penpot.app/assets/manifest.json |
|
||||||
| Rename layers | https://rename-layers-penpot-plugin.pages.dev/assets/manifest.json |
|
| Tables | https://table.plugins.penpot.app/assets/manifest.json |
|
||||||
|
|
||||||
|
|
||||||
## 1.4. Plugin's basics
|
## 1.4. Plugin's basics
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/penpot/penpot"
|
"url": "https://github.com/penpot/penpot"
|
||||||
|
|||||||
@@ -12,11 +12,14 @@
|
|||||||
["node:process" :as process]
|
["node:process" :as process]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.flags :as flags]
|
[app.common.flags :as flags]
|
||||||
|
[app.common.logging :as l]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.version :as v]
|
[app.common.version :as v]
|
||||||
[cljs.core :as c]
|
[cljs.core :as c]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(l/set-level! :info)
|
||||||
|
|
||||||
(def ^:private defaults
|
(def ^:private defaults
|
||||||
{:public-uri "http://localhost:3449"
|
{:public-uri "http://localhost:3449"
|
||||||
:tenant "default"
|
:tenant "default"
|
||||||
@@ -30,7 +33,7 @@
|
|||||||
[:map {:title "config"}
|
[:map {:title "config"}
|
||||||
[:secret-key :string]
|
[:secret-key :string]
|
||||||
[:public-uri {:optional true} ::sm/uri]
|
[:public-uri {:optional true} ::sm/uri]
|
||||||
[:management-api-key {:optional true} :string]
|
[:exporter-shared-key {:optional true} :string]
|
||||||
[:host {:optional true} :string]
|
[:host {:optional true} :string]
|
||||||
[:tenant {:optional true} :string]
|
[:tenant {:optional true} :string]
|
||||||
[:flags {:optional true} [::sm/set :keyword]]
|
[:flags {:optional true} [::sm/set :keyword]]
|
||||||
@@ -98,8 +101,10 @@
|
|||||||
(c/get config key default)))
|
(c/get config key default)))
|
||||||
|
|
||||||
(def management-key
|
(def management-key
|
||||||
(or (c/get config :management-api-key)
|
(let [key (or (c/get config :exporter-shared-key)
|
||||||
(let [secret-key (c/get config :secret-key)
|
(let [secret-key (c/get config :secret-key)
|
||||||
derived-key (crypto/hkdfSync "blake2b512" secret-key, "management" "" 32)]
|
derived-key (crypto/hkdfSync "blake2b512" secret-key, "exporter" "" 32)]
|
||||||
(-> (.from buffer/Buffer derived-key)
|
(-> (.from buffer/Buffer derived-key)
|
||||||
(.toString "base64url")))))
|
(.toString "base64url"))))]
|
||||||
|
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
|
||||||
|
key))
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
(p/mcat (fn [blob]
|
(p/mcat (fn [blob]
|
||||||
(let [fdata (new http/FormData)
|
(let [fdata (new http/FormData)
|
||||||
agent (new http/Agent #js {:connect #js {:rejectUnauthorized false}})
|
agent (new http/Agent #js {:connect #js {:rejectUnauthorized false}})
|
||||||
headers #js {"X-Shared-Key" cf/management-key
|
headers #js {"X-Shared-Key" (str "exporter " cf/management-key)
|
||||||
"Authorization" (str "Bearer " auth-token)}
|
"Authorization" (str "Bearer " auth-token)}
|
||||||
|
|
||||||
request #js {:headers headers
|
request #js {:headers headers
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"defaults"
|
"defaults"
|
||||||
],
|
],
|
||||||
|
|||||||
155
frontend/playwright/data/render-wasm/get-file-flex-layouts.json
Normal file
155
frontend/playwright/data/render-wasm/get-file-flex-layouts.json
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
{
|
||||||
|
"~:features": {
|
||||||
|
"~#set": [
|
||||||
|
"fdata/path-data",
|
||||||
|
"design-tokens/v1",
|
||||||
|
"variants/v1",
|
||||||
|
"layout/grid",
|
||||||
|
"fdata/objects-map",
|
||||||
|
"components/v2",
|
||||||
|
"fdata/shape-data-type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:team-id": "~ud7430f09-4f59-8049-8007-6277bb7586f6",
|
||||||
|
"~:permissions": {
|
||||||
|
"~:type": "~:membership",
|
||||||
|
"~:is-owner": true,
|
||||||
|
"~:is-admin": true,
|
||||||
|
"~:can-edit": true,
|
||||||
|
"~:can-read": true,
|
||||||
|
"~:is-logged": true
|
||||||
|
},
|
||||||
|
"~:has-media-trimmed": false,
|
||||||
|
"~:comment-thread-seqn": 0,
|
||||||
|
"~:name": "flex_index_position",
|
||||||
|
"~:revn": 114,
|
||||||
|
"~:modified-at": "~m1769430362161",
|
||||||
|
"~:vern": 0,
|
||||||
|
"~:id": "~u31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
"~:is-shared": false,
|
||||||
|
"~:migrations": {
|
||||||
|
"~#ordered-set": [
|
||||||
|
"legacy-2",
|
||||||
|
"legacy-3",
|
||||||
|
"legacy-5",
|
||||||
|
"legacy-6",
|
||||||
|
"legacy-7",
|
||||||
|
"legacy-8",
|
||||||
|
"legacy-9",
|
||||||
|
"legacy-10",
|
||||||
|
"legacy-11",
|
||||||
|
"legacy-12",
|
||||||
|
"legacy-13",
|
||||||
|
"legacy-14",
|
||||||
|
"legacy-16",
|
||||||
|
"legacy-17",
|
||||||
|
"legacy-18",
|
||||||
|
"legacy-19",
|
||||||
|
"legacy-25",
|
||||||
|
"legacy-26",
|
||||||
|
"legacy-27",
|
||||||
|
"legacy-28",
|
||||||
|
"legacy-29",
|
||||||
|
"legacy-31",
|
||||||
|
"legacy-32",
|
||||||
|
"legacy-33",
|
||||||
|
"legacy-34",
|
||||||
|
"legacy-36",
|
||||||
|
"legacy-37",
|
||||||
|
"legacy-38",
|
||||||
|
"legacy-39",
|
||||||
|
"legacy-40",
|
||||||
|
"legacy-41",
|
||||||
|
"legacy-42",
|
||||||
|
"legacy-43",
|
||||||
|
"legacy-44",
|
||||||
|
"legacy-45",
|
||||||
|
"legacy-46",
|
||||||
|
"legacy-47",
|
||||||
|
"legacy-48",
|
||||||
|
"legacy-49",
|
||||||
|
"legacy-50",
|
||||||
|
"legacy-51",
|
||||||
|
"legacy-52",
|
||||||
|
"legacy-53",
|
||||||
|
"legacy-54",
|
||||||
|
"legacy-55",
|
||||||
|
"legacy-56",
|
||||||
|
"legacy-57",
|
||||||
|
"legacy-59",
|
||||||
|
"legacy-62",
|
||||||
|
"legacy-65",
|
||||||
|
"legacy-66",
|
||||||
|
"legacy-67",
|
||||||
|
"0001-remove-tokens-from-groups",
|
||||||
|
"0002-normalize-bool-content-v2",
|
||||||
|
"0002-clean-shape-interactions",
|
||||||
|
"0003-fix-root-shape",
|
||||||
|
"0003-convert-path-content-v2",
|
||||||
|
"0005-deprecate-image-type",
|
||||||
|
"0006-fix-old-texts-fills",
|
||||||
|
"0008-fix-library-colors-v4",
|
||||||
|
"0009-clean-library-colors",
|
||||||
|
"0009-add-partial-text-touched-flags",
|
||||||
|
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||||
|
"0011-fix-invalid-text-touched-flags",
|
||||||
|
"0012-fix-position-data",
|
||||||
|
"0013-fix-component-path",
|
||||||
|
"0013-clear-invalid-strokes-and-fills",
|
||||||
|
"0014-fix-tokens-lib-duplicate-ids",
|
||||||
|
"0014-clear-components-nil-objects",
|
||||||
|
"0015-fix-text-attrs-blank-strings",
|
||||||
|
"0015-clean-shadow-color",
|
||||||
|
"0016-copy-fills-from-position-data-to-text-node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:version": 67,
|
||||||
|
"~:project-id": "~ud7430f09-4f59-8049-8007-6277bb765abd",
|
||||||
|
"~:created-at": "~m1769007798998",
|
||||||
|
"~:backend": "legacy-db",
|
||||||
|
"~:data": {
|
||||||
|
"~:pages": [
|
||||||
|
"~u02e9633d-4ce7-80da-8007-736558496fa8"
|
||||||
|
],
|
||||||
|
"~:pages-index": {
|
||||||
|
"~u02e9633d-4ce7-80da-8007-736558496fa8": {
|
||||||
|
"~:id": "~u02e9633d-4ce7-80da-8007-736558496fa8",
|
||||||
|
"~:name": "Page 1",
|
||||||
|
"~:objects": {
|
||||||
|
"~#penpot/objects-map/v2": {
|
||||||
|
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^I\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~u77c71dba-32ee-804c-8007-736561cf857f\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff457": "[\"~#shape\",[\"^ \",\"~:y\",396.00000357564704,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",396.00000357564704]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",476.00000357564704]],[\"^>\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",476.00000357564704]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff457\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704,\"^9\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",396.00000357564704,\"~:x2\",768.9999775886536,\"~:y2\",476.00000357564704]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2885": "[\"~#shape\",[\"^ \",\"~:y\",612.0000188344361,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",612.0000188344361]],[\"^>\",[\"^ \",\"~:x\",684.9999165534973,\"~:y\",612.0000188344361]],[\"^>\",[\"^ \",\"~:x\",684.9999165534973,\"~:y\",692.0000188344361]],[\"^>\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",692.0000188344361]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2885\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:strokes\",[],\"~:x\",604.9999165534973,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",612.0000188344361,\"^9\",80,\"~:height\",80,\"~:x1\",604.9999165534973,\"~:y1\",612.0000188344361,\"~:x2\",684.9999165534973,\"~:y2\",692.0000188344361]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2886": "[\"~#shape\",[\"^ \",\"~:y\",636.0000188344361,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",636.0000188344361]],[\"^K\",[\"^ \",\"~:x\",677.9999165534973,\"~:y\",636.0000188344361]],[\"^K\",[\"^ \",\"~:x\",677.9999165534973,\"~:y\",668.0000188344361]],[\"^K\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",668.0000188344361]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:strokes\",[],\"~:x\",611.9999165534973,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",636.0000188344361,\"^E\",66,\"~:height\",32,\"~:x1\",611.9999165534973,\"~:y1\",636.0000188344361,\"~:x2\",677.9999165534973,\"~:y2\",668.0000188344361]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2887\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2887": "[\"~#shape\",[\"^ \",\"~:y\",644.0000188344361,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",644.0000188344361]],[\"^K\",[\"^ \",\"~:x\",665.9999165534973,\"~:y\",644.0000188344361]],[\"^K\",[\"^ \",\"~:x\",665.9999165534973,\"~:y\",660.0000188344361]],[\"^K\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",660.0000188344361]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:strokes\",[],\"~:x\",623.9999165534973,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",644.0000188344361,\"^D\",42,\"~:height\",16,\"~:x1\",623.9999165534973,\"~:y1\",644.0000188344361,\"~:x2\",665.9999165534973,\"~:y2\",660.0000188344361]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2888\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2888": "[\"~#shape\",[\"^ \",\"~:y\",645.0000188344363,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",645.0000188344363]],[\"^S\",[\"^ \",\"~:x\",663.9999165534973,\"~:y\",645.0000188344363]],[\"^S\",[\"^ \",\"~:x\",663.9999165534973,\"~:y\",660.0000188344359]],[\"^S\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",660.0000188344363]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2888\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:position-data\",[[\"^ \",\"~:y\",659.3400268554688,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.94000244140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",626.0299682617188,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:strokes\",[],\"~:x\",625.9999165534973,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",645.0000188344363,\"^Q\",38,\"^11\",15,\"~:x1\",625.9999165534973,\"~:y1\",645.0000188344363,\"~:x2\",663.9999165534973,\"~:y2\",660.0000188344363]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff45a": "[\"~#shape\",[\"^ \",\"~:y\",429.00000357564727,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",429.00000357564727]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",429.00000357564727]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",444.0000035756468]],[\"^S\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",444.00000357564727]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff45a\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:position-data\",[[\"^ \",\"~:y\",443.3399963378906,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",710.030029296875,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.079986572265625,\"^L\",\"Label\"]],\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:strokes\",[],\"~:x\",709.9999775886536,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",429.00000357564727,\"^Q\",38,\"^11\",15,\"~:x1\",709.9999775886536,\"~:y1\",429.00000357564727,\"~:x2\",747.9999775886536,\"~:y2\",444.00000357564727]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff459": "[\"~#shape\",[\"^ \",\"~:y\",428.00000357564704,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",428.00000357564704]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",428.00000357564704]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",444.00000357564704]],[\"^K\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",444.00000357564704]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:strokes\",[],\"~:x\",707.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",428.00000357564704,\"^D\",42,\"~:height\",16,\"~:x1\",707.9999775886536,\"~:y1\",428.00000357564704,\"~:x2\",749.9999775886536,\"~:y2\",444.00000357564704]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff45a\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff458": "[\"~#shape\",[\"^ \",\"~:y\",420.00000357564704,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",420.00000357564704]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",420.00000357564704]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",452.00000357564704]],[\"^K\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",452.00000357564704]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:strokes\",[],\"~:x\",695.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",420.00000357564704,\"^E\",66,\"~:height\",32,\"~:x1\",695.9999775886536,\"~:y1\",420.00000357564704,\"~:x2\",761.9999775886536,\"~:y2\",452.00000357564704]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff459\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cf857f": "[\"~#shape\",[\"^ \",\"~:y\",395.99997913999186,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 1\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",395.99997913999186]],[\"^J\",[\"^ \",\"~:x\",865.0000386238098,\"~:y\",395.99997913999186]],[\"^J\",[\"^ \",\"~:x\",865.0000386238098,\"~:y\",475.9999669761459]],[\"^J\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",475.9999669761459]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",593.0000386238098,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",395.99997913999186,\"^D\",272,\"~:height\",79.99998783615405,\"~:x1\",593.0000386238098,\"~:y1\",395.99997913999186,\"~:x2\",865.0000386238098,\"~:y2\",475.9999669761459]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.99998783615405,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cf8584\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d50980078e": "[\"~#shape\",[\"^ \",\"~:y\",720.0000478045426,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 4\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",720.0000478045426]],[\"^J\",[\"^ \",\"~:x\",864.9998555183411,\"~:y\",720.0000478045426]],[\"^J\",[\"^ \",\"~:x\",864.9998555183411,\"~:y\",800.0000356406968]],[\"^J\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",800.0000356406968]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column-reverse\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9998555183411,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",720.0000478045426,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9998555183411,\"~:y1\",720.0000478045426,\"~:x2\",864.9998555183411,\"~:y2\",800.0000356406968]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d50980078f\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d50980078f": "[\"~#shape\",[\"^ \",\"~:y\",719.9999806874634,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",719.9999806874634]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",719.9999806874634]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",799.9999806874634]],[\"^;\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",799.9999806874634]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:strokes\",[],\"~:x\",604.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",719.9999806874634,\"^7\",80,\"~:height\",80,\"~:x1\",604.9999775886536,\"~:y1\",719.9999806874634,\"~:x2\",684.9999775886536,\"~:y2\",799.9999806874634]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800790\",\"~u94eaebe4-addd-80d1-8007-79d509800791\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508a9dc2f": "[\"~#shape\",[\"^ \",\"~:y\",612.000024916359,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 3\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",612.000024916359]],[\"^J\",[\"^ \",\"~:x\",864.9999165534973,\"~:y\",612.000024916359]],[\"^J\",[\"^ \",\"~:x\",864.9999165534973,\"~:y\",692.0000127525132]],[\"^J\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",692.0000127525132]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9999165534973,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",612.000024916359,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9999165534973,\"~:y1\",612.000024916359,\"~:x2\",864.9999165534973,\"~:y2\",692.0000127525132]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800790": "[\"~#shape\",[\"^ \",\"~:y\",720.0000417226197,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",720.0000417226197]],[\"^>\",[\"^ \",\"~:x\",684.9998555183411,\"~:y\",720.0000417226197]],[\"^>\",[\"^ \",\"~:x\",684.9998555183411,\"~:y\",800.0000417226197]],[\"^>\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",800.0000417226197]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800790\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:strokes\",[],\"~:x\",604.9998555183411,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",720.0000417226197,\"^9\",80,\"~:height\",80,\"~:x1\",604.9998555183411,\"~:y1\",720.0000417226197,\"~:x2\",684.9998555183411,\"~:y2\",800.0000417226197]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508a9dc30": "[\"~#shape\",[\"^ \",\"~:y\",612.0000188344361,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",612.0000188344361]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",612.0000188344361]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",692.0000188344361]],[\"^;\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",692.0000188344361]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:strokes\",[],\"~:x\",604.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",612.0000188344361,\"^7\",80,\"~:height\",80,\"~:x1\",604.9999775886536,\"~:y1\",612.0000188344361,\"~:x2\",684.9999775886536,\"~:y2\",692.0000188344361]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2885\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800791": "[\"~#shape\",[\"^ \",\"~:y\",744.0000417226197,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",744.0000417226197]],[\"^K\",[\"^ \",\"~:x\",677.9998555183411,\"~:y\",744.0000417226197]],[\"^K\",[\"^ \",\"~:x\",677.9998555183411,\"~:y\",776.0000417226197]],[\"^K\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",776.0000417226197]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:strokes\",[],\"~:x\",611.9998555183411,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",744.0000417226197,\"^E\",66,\"~:height\",32,\"~:x1\",611.9998555183411,\"~:y1\",744.0000417226197,\"~:x2\",677.9998555183411,\"~:y2\",776.0000417226197]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800792\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800792": "[\"~#shape\",[\"^ \",\"~:y\",752.0000417226197,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",752.0000417226197]],[\"^K\",[\"^ \",\"~:x\",665.9998555183411,\"~:y\",752.0000417226197]],[\"^K\",[\"^ \",\"~:x\",665.9998555183411,\"~:y\",768.0000417226197]],[\"^K\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",768.0000417226197]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:strokes\",[],\"~:x\",623.9998555183411,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",752.0000417226197,\"^D\",42,\"~:height\",16,\"~:x1\",623.9998555183411,\"~:y1\",752.0000417226197,\"~:x2\",665.9998555183411,\"~:y2\",768.0000417226197]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800793\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800793": "[\"~#shape\",[\"^ \",\"~:y\",753.0000417226199,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",753.0000417226199]],[\"^S\",[\"^ \",\"~:x\",663.9998555183411,\"~:y\",753.0000417226199]],[\"^S\",[\"^ \",\"~:x\",663.9998555183411,\"~:y\",768.0000417226195]],[\"^S\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",768.0000417226199]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800793\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:position-data\",[[\"^ \",\"~:y\",767.340087890625,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",626.0299072265625,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:strokes\",[],\"~:x\",625.9998555183411,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",753.0000417226199,\"^Q\",38,\"^11\",15,\"~:x1\",625.9998555183411,\"~:y1\",753.0000417226199,\"~:x2\",663.9998555183411,\"~:y2\",768.0000417226199]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cf8584": "[\"~#shape\",[\"^ \",\"~:y\",396.00000357564704,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",396.00000357564704]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",476.00000357564704]],[\"^;\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",476.00000357564704]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704,\"^7\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",396.00000357564704,\"~:x2\",768.9999775886536,\"~:y2\",476.00000357564704]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff457\",\"~u77c71dba-32ee-804c-8007-736561cff458\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d6859": "[\"~#shape\",[\"^ \",\"~:y\",504.00000202817546,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 2\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",504.00000202817546]],[\"^J\",[\"^ \",\"~:x\",864.9999775886536,\"~:y\",504.00000202817546]],[\"^J\",[\"^ \",\"~:x\",864.9999775886536,\"~:y\",583.9999898643296]],[\"^J\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",583.9999898643296]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:row-reverse\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",504.00000202817546,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9999775886536,\"~:y1\",504.00000202817546,\"~:x2\",864.9999775886536,\"~:y2\",583.9999898643296]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685a\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685a": "[\"~#shape\",[\"^ \",\"~:y\",503.9999959462525,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",503.9999959462525]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",583.9999959462525]],[\"^;\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",583.9999959462525]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525,\"^7\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",503.9999959462525,\"~:x2\",768.9999775886536,\"~:y2\",583.9999959462525]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685b\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685b": "[\"~#shape\",[\"^ \",\"~:y\",503.9999959462525,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",503.9999959462525]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",583.9999959462525]],[\"^>\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",583.9999959462525]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685b\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525,\"^9\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",503.9999959462525,\"~:x2\",768.9999775886536,\"~:y2\",583.9999959462525]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685c": "[\"~#shape\",[\"^ \",\"~:y\",527.9999959462525,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",527.9999959462525]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",527.9999959462525]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",559.9999959462525]],[\"^K\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",559.9999959462525]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:strokes\",[],\"~:x\",695.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",527.9999959462525,\"^E\",66,\"~:height\",32,\"~:x1\",695.9999775886536,\"~:y1\",527.9999959462525,\"~:x2\",761.9999775886536,\"~:y2\",559.9999959462525]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685d\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685d": "[\"~#shape\",[\"^ \",\"~:y\",535.9999959462525,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",535.9999959462525]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",535.9999959462525]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",551.9999959462525]],[\"^K\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",551.9999959462525]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:strokes\",[],\"~:x\",707.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",535.9999959462525,\"^D\",42,\"~:height\",16,\"~:x1\",707.9999775886536,\"~:y1\",535.9999959462525,\"~:x2\",749.9999775886536,\"~:y2\",551.9999959462525]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685e\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685e": "[\"~#shape\",[\"^ \",\"~:y\",536.9999959462527,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",536.9999959462527]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",536.9999959462527]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",551.9999959462523]],[\"^S\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",551.9999959462527]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685e\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:position-data\",[[\"^ \",\"~:y\",551.3400268554688,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",710.030029296875,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:strokes\",[],\"~:x\",709.9999775886536,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",536.9999959462527,\"^Q\",38,\"^11\",15,\"~:x1\",709.9999775886536,\"~:y1\",536.9999959462527,\"~:x2\",747.9999775886536,\"~:y2\",551.9999959462527]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
"~:options": {
|
||||||
|
"~:components-v2": true,
|
||||||
|
"~:base-font-size": "16px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,10 +58,10 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
|
|
||||||
async waitForTextSpan(nth = 0) {
|
async waitForTextSpan(nth = 0) {
|
||||||
if (!nth) {
|
if (!nth) {
|
||||||
return this.page.waitForSelector('[data-itype="inline"]');
|
return this.page.waitForSelector('[data-itype="span"]');
|
||||||
}
|
}
|
||||||
return this.page.waitForSelector(
|
return this.page.waitForSelector(
|
||||||
`[data-itype="inline"]:nth-child(${nth})`,
|
`[data-itype="span"]:nth-child(${nth})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,22 @@ test("Renders a file with shadows applied to any kind of shape", async ({
|
|||||||
await expect(workspace.canvas).toHaveScreenshot();
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Renders a file with flex layouts and different directions", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page);
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("render-wasm/get-file-flex-layouts.json");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace({
|
||||||
|
id: "31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
pageId: "02e9633d-4ce7-80da-8007-736558496fa8",
|
||||||
|
});
|
||||||
|
await workspace.waitForFirstRenderWithoutUI();
|
||||||
|
|
||||||
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
|
});
|
||||||
|
|
||||||
test("Renders a file with a closed path shape with multiple segments using strokes and shadow", async ({
|
test("Renders a file with a closed path shape with multiple segments using strokes and shadow", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -759,87 +759,4 @@ test.describe("Tokens: Apply token", () => {
|
|||||||
});
|
});
|
||||||
await expect(StrokeWidthPillSmall).toBeVisible();
|
await expect(StrokeWidthPillSmall).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User applies margin token to a shape", async ({ page }) => {
|
|
||||||
const workspace = new WorkspacePage(page, {
|
|
||||||
textEditor: true,
|
|
||||||
});
|
|
||||||
// Set up
|
|
||||||
await workspace.mockConfigFlags(["enable-feature-token-input"]);
|
|
||||||
await workspace.setupEmptyFile();
|
|
||||||
await workspace.mockGetFile("workspace/get-file-layout-stroke-token-json");
|
|
||||||
await workspace.goToWorkspace();
|
|
||||||
|
|
||||||
// Select shape apply stroke
|
|
||||||
await workspace.layers
|
|
||||||
.getByTestId("layer-row")
|
|
||||||
.nth(1)
|
|
||||||
.getByRole("button", { name: "Toggle layer" })
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await workspace.layers.getByTestId("layer-row").nth(2).click();
|
|
||||||
|
|
||||||
const rightSidebar = page.getByTestId("right-sidebar");
|
|
||||||
await expect(rightSidebar).toBeVisible();
|
|
||||||
await rightSidebar.getByTestId("add-stroke").click();
|
|
||||||
|
|
||||||
// Apply margin token from token panel
|
|
||||||
const tokensTab = page.getByRole("tab", { name: "Tokens" });
|
|
||||||
await expect(tokensTab).toBeVisible();
|
|
||||||
await tokensTab.click();
|
|
||||||
await page.getByRole("button", { name: "Dimensions 4" }).click();
|
|
||||||
await page.getByRole("button", { name: "dim", exact: true }).click();
|
|
||||||
const tokensSidebar = workspace.tokensSidebar;
|
|
||||||
await expect(
|
|
||||||
tokensSidebar.getByRole("button", { name: "dim.md" }),
|
|
||||||
).toBeVisible();
|
|
||||||
await tokensSidebar
|
|
||||||
.getByRole("button", { name: "dim.md" })
|
|
||||||
.click({ button: "right" });
|
|
||||||
await page
|
|
||||||
.getByTestId("tokens-context-menu-for-token")
|
|
||||||
.getByText("Spacing")
|
|
||||||
.hover();
|
|
||||||
await page
|
|
||||||
.getByTestId("tokens-context-menu-for-token")
|
|
||||||
.getByText("Horizontal")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Check if token pill is visible on right sidebar
|
|
||||||
const layoutItemSectionSidebar = rightSidebar.getByRole("region", {
|
|
||||||
name: "layout item menu",
|
|
||||||
});
|
|
||||||
await expect(layoutItemSectionSidebar).toBeVisible();
|
|
||||||
const marginPillMd = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "dim.md",
|
|
||||||
});
|
|
||||||
await expect(marginPillMd).toBeVisible();
|
|
||||||
|
|
||||||
await marginPillMd.click();
|
|
||||||
const dimensionTokenOptionXl = page.getByRole("option", { name: "dim.xl" });
|
|
||||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
|
||||||
await dimensionTokenOptionXl.click();
|
|
||||||
|
|
||||||
const marginPillXL = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "dim.xl",
|
|
||||||
});
|
|
||||||
await expect(marginPillXL).toBeVisible();
|
|
||||||
|
|
||||||
// Detach token from right sidebar and apply another from dropdown
|
|
||||||
const detachButton = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "Detach token",
|
|
||||||
});
|
|
||||||
await detachButton.click();
|
|
||||||
await expect(marginPillXL).not.toBeVisible();
|
|
||||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText('Horizontal marginOpen token');
|
|
||||||
await expect(horizontalMarginInput).toBeVisible();
|
|
||||||
|
|
||||||
const tokenDropdown = horizontalMarginInput.getByRole('button', { name: 'Open token list' });
|
|
||||||
await tokenDropdown.click();
|
|
||||||
|
|
||||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
|
||||||
await dimensionTokenOptionXl.click();
|
|
||||||
|
|
||||||
await expect(marginPillXL).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,88 +12,118 @@ test.beforeEach(async ({ page }) => {
|
|||||||
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json");
|
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Tokens: Remapping Feature", () => {
|
const createToken = async (page, type, name, textFieldName, value) => {
|
||||||
test.describe("Box Shadow Token Remapping", () => {
|
|
||||||
test("User renames box shadow token with alias references", async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
tokensUpdateCreateModal,
|
|
||||||
tokensSidebar,
|
|
||||||
tokenContextMenuForToken,
|
|
||||||
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
// Create base shadow token
|
const { tokensUpdateCreateModal } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base token
|
||||||
await tokensTabPanel
|
await tokensTabPanel
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
.getByRole("button", { name: `Add Token: ${type}` })
|
||||||
.click();
|
.click();
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
await nameField.fill("base-shadow");
|
await nameField.fill(name);
|
||||||
|
|
||||||
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Color",
|
name: textFieldName,
|
||||||
});
|
});
|
||||||
await colorField.fill("#000000");
|
await colorField.fill(value);
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
// Create derived shadow token that references base-shadow
|
const renameToken = async (page, oldName, newName) => {
|
||||||
await tokensTabPanel
|
const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } =
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Name",
|
|
||||||
});
|
|
||||||
await nameField.fill("derived-shadow");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{base-shadow}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base-shadow token
|
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
const baseToken = tokensSidebar.getByRole("button", {
|
||||||
name: "base-shadow",
|
name: oldName,
|
||||||
});
|
});
|
||||||
await baseToken.click({ button: "right" });
|
await baseToken.click({ button: "right" });
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("foundation-shadow");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
|
await nameField.fill(newName);
|
||||||
|
|
||||||
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCompositeDerivedToken = async (page, type, name, reference) => {
|
||||||
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
|
const { tokensUpdateCreateModal } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
await tokensTabPanel
|
||||||
|
.getByRole("button", { name: `Add Token: ${type}` })
|
||||||
|
.click();
|
||||||
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
|
const nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
|
name: "Name",
|
||||||
|
});
|
||||||
|
await nameField.fill(name);
|
||||||
|
|
||||||
|
const referenceToggle = tokensUpdateCreateModal.getByTestId("reference-opt");
|
||||||
|
await referenceToggle.click();
|
||||||
|
|
||||||
|
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
|
name: "Reference",
|
||||||
|
});
|
||||||
|
await referenceField.fill(reference);
|
||||||
|
|
||||||
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
|
name: "Save",
|
||||||
|
});
|
||||||
|
await submitButton.click();
|
||||||
|
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
test.describe("Remapping Tokens", () => {
|
||||||
|
test.describe("Box Shadow Token Remapping", () => {
|
||||||
|
test("User renames box shadow token with alias references", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base shadow token
|
||||||
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
|
|
||||||
|
// Create derived shadow token that references base-shadow
|
||||||
|
await createCompositeDerivedToken(
|
||||||
|
page,
|
||||||
|
"Shadow",
|
||||||
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename base-shadow token
|
||||||
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
await expect(remappingModal).toContainText("1");
|
await expect(remappingModal).toContainText("base-shadow");
|
||||||
|
await expect(remappingModal).toContainText("foundation-shadow");
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -116,51 +146,16 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
workspacePage,
|
workspacePage,
|
||||||
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base shadow token
|
// Create base shadow token
|
||||||
await tokensTabPanel
|
await createToken(page, "Shadow", "primary-shadow", "Color", "#000000");
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("primary-shadow");
|
|
||||||
|
|
||||||
let colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Color",
|
|
||||||
});
|
|
||||||
await colorField.fill("#000000");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived shadow token that references base
|
// Create derived shadow token that references base
|
||||||
await tokensTabPanel
|
await createCompositeDerivedToken(
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
page,
|
||||||
.click();
|
"Shadow",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"card-shadow",
|
||||||
|
"{primary-shadow}",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
);
|
||||||
await nameField.fill("card-shadow");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{primary-shadow}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Apply the referenced token to a shape
|
// Apply the referenced token to a shape
|
||||||
await page.getByRole("tab", { name: "Layers" }).click();
|
await page.getByRole("tab", { name: "Layers" }).click();
|
||||||
@@ -183,16 +178,16 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
await nameField.fill("main-shadow");
|
await nameField.fill("main-shadow");
|
||||||
|
|
||||||
// Update the color value
|
// Update the color value
|
||||||
colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Color",
|
name: "Color",
|
||||||
});
|
});
|
||||||
await colorField.fill("#FF0000");
|
await colorField.fill("#FF0000");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -202,7 +197,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -259,73 +254,25 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
// Create base typography token
|
// Create base typography token
|
||||||
await tokensTabPanel
|
await createToken(page, "Typography", "base-text", "Font size", "16");
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("base-text");
|
|
||||||
|
|
||||||
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Font size",
|
|
||||||
});
|
|
||||||
await fontSizeField.fill("16");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived typography token
|
// Create derived typography token
|
||||||
await tokensTabPanel
|
await createCompositeDerivedToken(
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
page,
|
||||||
.click();
|
"Typography",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"body-text",
|
||||||
|
"{base-text}",
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
);
|
||||||
name: "Name",
|
|
||||||
});
|
|
||||||
await nameField.fill("body-text");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{base-text}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base token
|
// Rename base token
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
await renameToken(page, "base-text", "default-text");
|
||||||
name: "base-text",
|
|
||||||
});
|
|
||||||
await baseToken.click({ button: "right" });
|
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("default-text");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -351,24 +298,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
// Create base typography token
|
// Create base typography token
|
||||||
await tokensTabPanel
|
await createToken(page, "Typography", "body-style", "Font size", "16");
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("body-style");
|
|
||||||
|
|
||||||
let fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Font size",
|
|
||||||
});
|
|
||||||
await fontSizeField.fill("16");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived typography token
|
// Create derived typography token
|
||||||
await tokensTabPanel
|
await tokensTabPanel
|
||||||
@@ -376,7 +306,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
.click();
|
.click();
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
let nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
});
|
});
|
||||||
await nameField.fill("paragraph-style");
|
await nameField.fill("paragraph-style");
|
||||||
@@ -390,7 +320,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
});
|
});
|
||||||
await referenceField.fill("{body-style}");
|
await referenceField.fill("{body-style}");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -421,7 +351,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await nameField.fill("text-base");
|
await nameField.fill("text-base");
|
||||||
|
|
||||||
// Update the font size value
|
// Update the font size value
|
||||||
fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Font size",
|
name: "Font size",
|
||||||
});
|
});
|
||||||
await fontSizeField.fill("18");
|
await fontSizeField.fill("18");
|
||||||
@@ -436,7 +366,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -471,72 +401,29 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
test("User renames border radius token with alias references", async ({
|
test("User renames border radius token with alias references", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { tokensSidebar } = await setupTokensFile(page);
|
||||||
tokensUpdateCreateModal,
|
|
||||||
tokensSidebar,
|
|
||||||
tokenContextMenuForToken,
|
|
||||||
} = await setupTokensFile(page);
|
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base border radius token
|
// Create base border radius token
|
||||||
await tokensTabPanel
|
await createToken(page, "Border Radius", "base-radius", "Value", "4");
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("base-radius");
|
|
||||||
|
|
||||||
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField.fill("4");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived border radius token
|
// Create derived border radius token
|
||||||
await tokensTabPanel
|
await createToken(
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
page,
|
||||||
.click();
|
"Border Radius",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"card-radius",
|
||||||
|
"Value",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
"{base-radius}",
|
||||||
await nameField.fill("card-radius");
|
);
|
||||||
|
|
||||||
const valueField2 = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField2.fill("{base-radius}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base token
|
// Rename base token
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
await renameToken(page, "base-radius", "primary-radius");
|
||||||
name: "base-radius",
|
|
||||||
});
|
|
||||||
await baseToken.click({ button: "right" });
|
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("primary-radius");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -558,43 +445,17 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
tokenContextMenuForToken,
|
tokenContextMenuForToken,
|
||||||
} = await setupTokensFile(page);
|
} = await setupTokensFile(page);
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base border radius token
|
// Create base border radius token
|
||||||
await tokensTabPanel
|
await createToken(page, "Border Radius", "radius-sm", "Value", "4");
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("radius-sm");
|
|
||||||
|
|
||||||
let valueField = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField.fill("4");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived border radius token
|
// Create derived border radius token
|
||||||
await tokensTabPanel
|
await createToken(
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
page,
|
||||||
.click();
|
"Border Radius",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"button-radius",
|
||||||
|
"Value",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
"{radius-sm}",
|
||||||
await nameField.fill("button-radius");
|
);
|
||||||
|
|
||||||
const valueField2 = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField2.fill("{radius-sm}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename and update value of base token
|
// Rename and update value of base token
|
||||||
const radiusToken = tokensSidebar.getByRole("button", {
|
const radiusToken = tokensSidebar.getByRole("button", {
|
||||||
@@ -604,14 +465,14 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
await nameField.fill("radius-base");
|
await nameField.fill("radius-base");
|
||||||
|
|
||||||
// Update the value
|
// Update the value
|
||||||
valueField = tokensUpdateCreateModal.getByLabel("Value");
|
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
||||||
await valueField.fill("8");
|
await valueField.fill("8");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -621,7 +482,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -648,4 +509,82 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(currentValue).toHaveValue("{radius-base}");
|
await expect(currentValue).toHaveValue("{radius-base}");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("Cancel remap", () => {
|
||||||
|
test("Only rename - breaks reference", async ({ page }) => {
|
||||||
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base shadow token
|
||||||
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
|
|
||||||
|
// Create derived shadow token that references base-shadow
|
||||||
|
await createCompositeDerivedToken(
|
||||||
|
page,
|
||||||
|
"Shadow",
|
||||||
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename base-shadow token
|
||||||
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
|
|
||||||
|
// Check for remapping modal
|
||||||
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
|
const cancelButton = remappingModal.getByRole("button", {
|
||||||
|
name: "don't remap",
|
||||||
|
});
|
||||||
|
await cancelButton.click();
|
||||||
|
|
||||||
|
// Verify token was renamed
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", {
|
||||||
|
name: "foundation-shadow",
|
||||||
|
}),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.locator('[aria-label="Missing reference"]'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Cancel process - no changes applied", async ({ page }) => {
|
||||||
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base shadow token
|
||||||
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
|
|
||||||
|
// Create derived shadow token that references base-shadow
|
||||||
|
await createCompositeDerivedToken(
|
||||||
|
page,
|
||||||
|
"Shadow",
|
||||||
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename base-shadow token
|
||||||
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
|
|
||||||
|
// Check for remapping modal
|
||||||
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
|
const closeButton = remappingModal.getByRole("button", {
|
||||||
|
name: "close",
|
||||||
|
});
|
||||||
|
await closeButton.click();
|
||||||
|
|
||||||
|
// Verify original token name still exists
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", { name: "base-shadow" }),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", { name: "derived-shadow" }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -216,4 +216,32 @@ test.describe("Tokens: Sets Tab", () => {
|
|||||||
await expect(tokenSetItems.nth(1)).toHaveAttribute("aria-checked", "false");
|
await expect(tokenSetItems.nth(1)).toHaveAttribute("aria-checked", "false");
|
||||||
await expect(tokenSetItems.nth(2)).toHaveAttribute("aria-checked", "true");
|
await expect(tokenSetItems.nth(2)).toHaveAttribute("aria-checked", "true");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Display active set and verify if is enabled", async ({ page }) => {
|
||||||
|
const { tokenThemesSetsSidebar, tokensSidebar, tokenSetItems } =
|
||||||
|
await setupTokensFile(page);
|
||||||
|
|
||||||
|
// Create set
|
||||||
|
await tokenThemesSetsSidebar
|
||||||
|
.getByRole("button", { name: "Add set" })
|
||||||
|
.click();
|
||||||
|
await changeSetInput(tokenThemesSetsSidebar, "Inactive set");
|
||||||
|
await tokenThemesSetsSidebar
|
||||||
|
.getByRole("button", { name: "Inactive set" })
|
||||||
|
.click();
|
||||||
|
let activeSetTitle = await tokensSidebar.getByTestId(
|
||||||
|
"active-token-set-title",
|
||||||
|
);
|
||||||
|
await expect(activeSetTitle).toHaveText("TOKENS - Inactive set");
|
||||||
|
const inactiveSetInfo = await tokensSidebar.getByTitle(
|
||||||
|
"This set is not active.",
|
||||||
|
);
|
||||||
|
await expect(inactiveSetInfo).toBeVisible();
|
||||||
|
|
||||||
|
// Switch active set
|
||||||
|
|
||||||
|
await tokenThemesSetsSidebar.getByRole("button", { name: "theme" }).click();
|
||||||
|
await expect(activeSetTitle).toHaveText("TOKENS - theme");
|
||||||
|
await expect(inactiveSetInfo).not.toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
226
frontend/pnpm-lock.yaml
generated
226
frontend/pnpm-lock.yaml
generated
@@ -99,7 +99,7 @@ importers:
|
|||||||
version: 1.15.4
|
version: 1.15.4
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^27.4.0
|
specifier: ^27.4.0
|
||||||
version: 27.4.0
|
version: 27.4.0(canvas@3.2.1)
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -210,7 +210,7 @@ importers:
|
|||||||
version: 7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
version: 7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.0.18
|
specifier: ^4.0.18
|
||||||
version: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
version: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
wait-on:
|
wait-on:
|
||||||
specifier: ^9.0.3
|
specifier: ^9.0.3
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
@@ -265,12 +265,15 @@ importers:
|
|||||||
'@vitest/ui':
|
'@vitest/ui':
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.1(vitest@1.6.1)
|
version: 1.6.1(vitest@1.6.1)
|
||||||
|
canvas:
|
||||||
|
specifier: ^3.2.1
|
||||||
|
version: 3.2.1
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.27.2
|
specifier: ^0.27.2
|
||||||
version: 0.27.2
|
version: 0.27.2
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^27.4.0
|
specifier: ^27.4.0
|
||||||
version: 27.4.0
|
version: 27.4.0(canvas@3.2.1)
|
||||||
playwright:
|
playwright:
|
||||||
specifier: ^1.45.1
|
specifier: ^1.45.1
|
||||||
version: 1.57.0
|
version: 1.57.0
|
||||||
@@ -282,7 +285,7 @@ importers:
|
|||||||
version: 5.4.21(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)
|
version: 5.4.21(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
version: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1751,6 +1754,9 @@ packages:
|
|||||||
bintrees@1.0.2:
|
bintrees@1.0.2:
|
||||||
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||||
|
|
||||||
body-parser@2.2.1:
|
body-parser@2.2.1:
|
||||||
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -1779,6 +1785,9 @@ packages:
|
|||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||||
|
|
||||||
@@ -1809,6 +1818,10 @@ packages:
|
|||||||
caniuse-lite@1.0.30001762:
|
caniuse-lite@1.0.30001762:
|
||||||
resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
|
resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
|
||||||
|
|
||||||
|
canvas@3.2.1:
|
||||||
|
resolution: {integrity: sha512-ej1sPFR5+0YWtaVp6S1N1FVz69TQCqmrkGeRvQxZeAB1nAIcjNTHVwrZtYtWFFBmQsF40/uDLehsW5KuYC99mg==}
|
||||||
|
engines: {node: ^18.12.0 || >= 20.9.0}
|
||||||
|
|
||||||
chai@4.5.0:
|
chai@4.5.0:
|
||||||
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1851,6 +1864,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
|
|
||||||
|
chownr@1.1.4:
|
||||||
|
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||||
|
|
||||||
chownr@2.0.0:
|
chownr@2.0.0:
|
||||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2092,6 +2108,10 @@ packages:
|
|||||||
decimal.js@10.6.0:
|
decimal.js@10.6.0:
|
||||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
deep-eql@4.1.4:
|
deep-eql@4.1.4:
|
||||||
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
|
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2100,6 +2120,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
deep-extend@0.6.0:
|
||||||
|
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||||
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
||||||
deepmerge@4.3.1:
|
deepmerge@4.3.1:
|
||||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2144,6 +2168,10 @@ packages:
|
|||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
detect-libc@2.1.2:
|
||||||
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
dettle@1.0.5:
|
dettle@1.0.5:
|
||||||
resolution: {integrity: sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==}
|
resolution: {integrity: sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==}
|
||||||
|
|
||||||
@@ -2232,6 +2260,9 @@ packages:
|
|||||||
encoding@0.1.13:
|
encoding@0.1.13:
|
||||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
entities@2.2.0:
|
entities@2.2.0:
|
||||||
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
|
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
|
||||||
|
|
||||||
@@ -2329,6 +2360,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||||
engines: {node: '>=16.17'}
|
engines: {node: '>=16.17'}
|
||||||
|
|
||||||
|
expand-template@2.0.3:
|
||||||
|
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
expect-type@1.3.0:
|
expect-type@1.3.0:
|
||||||
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
@@ -2421,6 +2456,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
fs-constants@1.0.0:
|
||||||
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
|
|
||||||
fs-extra@10.1.0:
|
fs-extra@10.1.0:
|
||||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2493,6 +2531,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-eFmhDi2xQ+2reMRY2AbJ2oa10uFOl1oyGbAKdCZiNOk94NJHi7aN0OBELSC9v35ZAPQdr+uRBi93/Gu4SlBdrA==}
|
resolution: {integrity: sha512-eFmhDi2xQ+2reMRY2AbJ2oa10uFOl1oyGbAKdCZiNOk94NJHi7aN0OBELSC9v35ZAPQdr+uRBi93/Gu4SlBdrA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
github-from-package@0.0.0:
|
||||||
|
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -3042,6 +3083,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
mimic-response@3.1.0:
|
||||||
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
min-indent@1.0.1:
|
min-indent@1.0.1:
|
||||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -3080,6 +3125,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3:
|
||||||
|
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||||
|
|
||||||
mkdirp@1.0.4:
|
mkdirp@1.0.4:
|
||||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -3112,6 +3160,9 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0:
|
||||||
|
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||||
|
|
||||||
negotiator@0.6.4:
|
negotiator@0.6.4:
|
||||||
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -3123,6 +3174,10 @@ packages:
|
|||||||
nice-try@1.0.5:
|
nice-try@1.0.5:
|
||||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||||
|
|
||||||
|
node-abi@3.87.0:
|
||||||
|
resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
@@ -3415,6 +3470,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
prettier@3.5.3:
|
prettier@3.5.3:
|
||||||
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
|
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -3472,6 +3532,9 @@ packages:
|
|||||||
pstree.remy@1.1.8:
|
pstree.remy@1.1.8:
|
||||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||||
|
|
||||||
|
pump@3.0.3:
|
||||||
|
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
||||||
|
|
||||||
punycode@1.4.1:
|
punycode@1.4.1:
|
||||||
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
||||||
|
|
||||||
@@ -3497,6 +3560,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0:
|
react-docgen-typescript@2.4.0:
|
||||||
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3884,6 +3951,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
simple-concat@1.0.1:
|
||||||
|
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||||
|
|
||||||
simple-update-notifier@2.0.0:
|
simple-update-notifier@2.0.0:
|
||||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4016,6 +4089,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
|
resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1:
|
||||||
|
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
strip-literal@2.1.1:
|
strip-literal@2.1.1:
|
||||||
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
|
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
|
||||||
|
|
||||||
@@ -4069,6 +4146,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
|
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
tar-fs@2.1.4:
|
||||||
|
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tar@6.2.1:
|
tar@6.2.1:
|
||||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4195,6 +4279,9 @@ packages:
|
|||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||||
|
|
||||||
type-detect@4.1.0:
|
type-detect@4.1.0:
|
||||||
resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
|
resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -5411,7 +5498,7 @@ snapshots:
|
|||||||
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
'@vitest/runner': 4.0.18
|
'@vitest/runner': 4.0.18
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- react
|
- react
|
||||||
- react-dom
|
- react-dom
|
||||||
@@ -5583,7 +5670,7 @@ snapshots:
|
|||||||
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
||||||
playwright: 1.58.0
|
playwright: 1.58.0
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- msw
|
- msw
|
||||||
@@ -5595,7 +5682,7 @@ snapshots:
|
|||||||
'@vitest/utils': 1.6.1
|
'@vitest/utils': 1.6.1
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
playwright: 1.57.0
|
playwright: 1.57.0
|
||||||
|
|
||||||
@@ -5608,7 +5695,7 @@ snapshots:
|
|||||||
pngjs: 7.0.0
|
pngjs: 7.0.0
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
ws: 8.19.0
|
ws: 8.19.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -5631,7 +5718,7 @@ snapshots:
|
|||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
strip-literal: 2.1.1
|
strip-literal: 2.1.1
|
||||||
test-exclude: 6.0.0
|
test-exclude: 6.0.0
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -5647,7 +5734,7 @@ snapshots:
|
|||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
|
|
||||||
@@ -5740,7 +5827,7 @@ snapshots:
|
|||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
|
|
||||||
'@vitest/utils@1.6.1':
|
'@vitest/utils@1.6.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5908,6 +5995,12 @@ snapshots:
|
|||||||
|
|
||||||
bintrees@1.0.2: {}
|
bintrees@1.0.2: {}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
buffer: 5.7.1
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
body-parser@2.2.1:
|
body-parser@2.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
bytes: 3.1.2
|
bytes: 3.1.2
|
||||||
@@ -5949,6 +6042,11 @@ snapshots:
|
|||||||
|
|
||||||
buffer-from@1.1.2: {}
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
base64-js: 1.5.1
|
||||||
@@ -5981,6 +6079,11 @@ snapshots:
|
|||||||
|
|
||||||
caniuse-lite@1.0.30001762: {}
|
caniuse-lite@1.0.30001762: {}
|
||||||
|
|
||||||
|
canvas@3.2.1:
|
||||||
|
dependencies:
|
||||||
|
node-addon-api: 7.1.1
|
||||||
|
prebuild-install: 7.1.3
|
||||||
|
|
||||||
chai@4.5.0:
|
chai@4.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
assertion-error: 1.1.0
|
assertion-error: 1.1.0
|
||||||
@@ -6038,6 +6141,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
|
|
||||||
|
chownr@1.1.4: {}
|
||||||
|
|
||||||
chownr@2.0.0: {}
|
chownr@2.0.0: {}
|
||||||
|
|
||||||
ci-info@3.9.0: {}
|
ci-info@3.9.0: {}
|
||||||
@@ -6273,12 +6378,18 @@ snapshots:
|
|||||||
|
|
||||||
decimal.js@10.6.0: {}
|
decimal.js@10.6.0: {}
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 3.1.0
|
||||||
|
|
||||||
deep-eql@4.1.4:
|
deep-eql@4.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
type-detect: 4.1.0
|
type-detect: 4.1.0
|
||||||
|
|
||||||
deep-eql@5.0.2: {}
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
|
deep-extend@0.6.0: {}
|
||||||
|
|
||||||
deepmerge@4.3.1: {}
|
deepmerge@4.3.1: {}
|
||||||
|
|
||||||
default-browser-id@5.0.1: {}
|
default-browser-id@5.0.1: {}
|
||||||
@@ -6313,6 +6424,8 @@ snapshots:
|
|||||||
detect-libc@1.0.3:
|
detect-libc@1.0.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
dettle@1.0.5: {}
|
dettle@1.0.5: {}
|
||||||
|
|
||||||
diff-sequences@29.6.3: {}
|
diff-sequences@29.6.3: {}
|
||||||
@@ -6407,6 +6520,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
iconv-lite: 0.6.3
|
iconv-lite: 0.6.3
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
entities@2.2.0: {}
|
entities@2.2.0: {}
|
||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
@@ -6588,6 +6705,8 @@ snapshots:
|
|||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
strip-final-newline: 3.0.0
|
strip-final-newline: 3.0.0
|
||||||
|
|
||||||
|
expand-template@2.0.3: {}
|
||||||
|
|
||||||
expect-type@1.3.0: {}
|
expect-type@1.3.0: {}
|
||||||
|
|
||||||
expr-eval-fork@2.0.2: {}
|
expr-eval-fork@2.0.2: {}
|
||||||
@@ -6711,6 +6830,8 @@ snapshots:
|
|||||||
|
|
||||||
fresh@2.0.0: {}
|
fresh@2.0.0: {}
|
||||||
|
|
||||||
|
fs-constants@1.0.0: {}
|
||||||
|
|
||||||
fs-extra@10.1.0:
|
fs-extra@10.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@@ -6789,6 +6910,8 @@ snapshots:
|
|||||||
readable-stream: 4.7.0
|
readable-stream: 4.7.0
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
github-from-package@0.0.0: {}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -7163,7 +7286,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
jsdom@27.4.0:
|
jsdom@27.4.0(canvas@3.2.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@acemir/cssom': 0.9.30
|
'@acemir/cssom': 0.9.30
|
||||||
'@asamuzakjp/dom-selector': 6.7.6
|
'@asamuzakjp/dom-selector': 6.7.6
|
||||||
@@ -7185,6 +7308,8 @@ snapshots:
|
|||||||
whatwg-url: 15.1.0
|
whatwg-url: 15.1.0
|
||||||
ws: 8.18.3
|
ws: 8.18.3
|
||||||
xml-name-validator: 5.0.0
|
xml-name-validator: 5.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
canvas: 3.2.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@exodus/crypto'
|
- '@exodus/crypto'
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -7342,6 +7467,8 @@ snapshots:
|
|||||||
|
|
||||||
mimic-fn@4.0.0: {}
|
mimic-fn@4.0.0: {}
|
||||||
|
|
||||||
|
mimic-response@3.1.0: {}
|
||||||
|
|
||||||
min-indent@1.0.1: {}
|
min-indent@1.0.1: {}
|
||||||
|
|
||||||
minimatch@10.1.1:
|
minimatch@10.1.1:
|
||||||
@@ -7375,6 +7502,8 @@ snapshots:
|
|||||||
minipass: 3.3.6
|
minipass: 3.3.6
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3: {}
|
||||||
|
|
||||||
mkdirp@1.0.4: {}
|
mkdirp@1.0.4: {}
|
||||||
|
|
||||||
mkdirp@3.0.1: {}
|
mkdirp@3.0.1: {}
|
||||||
@@ -7396,14 +7525,19 @@ snapshots:
|
|||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0: {}
|
||||||
|
|
||||||
negotiator@0.6.4: {}
|
negotiator@0.6.4: {}
|
||||||
|
|
||||||
negotiator@1.0.0: {}
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
nice-try@1.0.5: {}
|
nice-try@1.0.5: {}
|
||||||
|
|
||||||
node-addon-api@7.1.1:
|
node-abi@3.87.0:
|
||||||
optional: true
|
dependencies:
|
||||||
|
semver: 7.7.3
|
||||||
|
|
||||||
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
node-fetch@2.7.0(encoding@0.1.13):
|
node-fetch@2.7.0(encoding@0.1.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7704,6 +7838,21 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
expand-template: 2.0.3
|
||||||
|
github-from-package: 0.0.0
|
||||||
|
minimist: 1.2.8
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
napi-build-utils: 2.0.0
|
||||||
|
node-abi: 3.87.0
|
||||||
|
pump: 3.0.3
|
||||||
|
rc: 1.2.8
|
||||||
|
simple-get: 4.0.1
|
||||||
|
tar-fs: 2.1.4
|
||||||
|
tunnel-agent: 0.6.0
|
||||||
|
|
||||||
prettier@3.5.3: {}
|
prettier@3.5.3: {}
|
||||||
|
|
||||||
prettier@3.7.4: {}
|
prettier@3.7.4: {}
|
||||||
@@ -7755,6 +7904,11 @@ snapshots:
|
|||||||
|
|
||||||
pstree.remy@1.1.8: {}
|
pstree.remy@1.1.8: {}
|
||||||
|
|
||||||
|
pump@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
end-of-stream: 1.4.5
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
punycode@1.4.1: {}
|
punycode@1.4.1: {}
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
@@ -7776,6 +7930,13 @@ snapshots:
|
|||||||
iconv-lite: 0.7.1
|
iconv-lite: 0.7.1
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
dependencies:
|
||||||
|
deep-extend: 0.6.0
|
||||||
|
ini: 1.3.8
|
||||||
|
minimist: 1.2.8
|
||||||
|
strip-json-comments: 2.0.1
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@@ -8249,6 +8410,14 @@ snapshots:
|
|||||||
|
|
||||||
signal-exit@4.1.0: {}
|
signal-exit@4.1.0: {}
|
||||||
|
|
||||||
|
simple-concat@1.0.1: {}
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
decompress-response: 6.0.0
|
||||||
|
once: 1.4.0
|
||||||
|
simple-concat: 1.0.1
|
||||||
|
|
||||||
simple-update-notifier@2.0.0:
|
simple-update-notifier@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.7.3
|
semver: 7.7.3
|
||||||
@@ -8404,6 +8573,8 @@ snapshots:
|
|||||||
|
|
||||||
strip-indent@4.1.1: {}
|
strip-indent@4.1.1: {}
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1: {}
|
||||||
|
|
||||||
strip-literal@2.1.1:
|
strip-literal@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 9.0.1
|
js-tokens: 9.0.1
|
||||||
@@ -8487,6 +8658,21 @@ snapshots:
|
|||||||
|
|
||||||
sync-message-port@1.1.3: {}
|
sync-message-port@1.1.3: {}
|
||||||
|
|
||||||
|
tar-fs@2.1.4:
|
||||||
|
dependencies:
|
||||||
|
chownr: 1.1.4
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
pump: 3.0.3
|
||||||
|
tar-stream: 2.2.0
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
bl: 4.1.0
|
||||||
|
end-of-stream: 1.4.5
|
||||||
|
fs-constants: 1.0.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
tar@6.2.1:
|
tar@6.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr: 2.0.0
|
chownr: 2.0.0
|
||||||
@@ -8587,6 +8773,10 @@ snapshots:
|
|||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
type-detect@4.1.0: {}
|
type-detect@4.1.0: {}
|
||||||
|
|
||||||
type-is@2.0.1:
|
type-is@2.0.1:
|
||||||
@@ -8757,7 +8947,7 @@ snapshots:
|
|||||||
sass-embedded: 1.97.1
|
sass-embedded: 1.97.1
|
||||||
yaml: 2.8.2
|
yaml: 2.8.2
|
||||||
|
|
||||||
vitest@1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1):
|
vitest@1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 1.6.1
|
'@vitest/expect': 1.6.1
|
||||||
'@vitest/runner': 1.6.1
|
'@vitest/runner': 1.6.1
|
||||||
@@ -8783,7 +8973,7 @@ snapshots:
|
|||||||
'@types/node': 25.0.3
|
'@types/node': 25.0.3
|
||||||
'@vitest/browser': 1.6.1(playwright@1.57.0)(vitest@1.6.1)
|
'@vitest/browser': 1.6.1(playwright@1.57.0)(vitest@1.6.1)
|
||||||
'@vitest/ui': 1.6.1(vitest@1.6.1)
|
'@vitest/ui': 1.6.1(vitest@1.6.1)
|
||||||
jsdom: 27.4.0
|
jsdom: 27.4.0(canvas@3.2.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- less
|
- less
|
||||||
- lightningcss
|
- lightningcss
|
||||||
@@ -8794,7 +8984,7 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- terser
|
- terser
|
||||||
|
|
||||||
vitest@4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2):
|
vitest@4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.0.18
|
'@vitest/expect': 4.0.18
|
||||||
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
||||||
@@ -8819,7 +9009,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 25.0.3
|
'@types/node': 25.0.3
|
||||||
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
jsdom: 27.4.0
|
jsdom: 27.4.0(canvas@3.2.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- jiti
|
- jiti
|
||||||
- less
|
- less
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.common.types.project :refer [valid-project?]]
|
[app.common.types.project :refer [valid-project?]]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.constants :as mconst]
|
[app.main.constants :as mconst]
|
||||||
[app.main.data.common :as dcm]
|
[app.main.data.common :as dcm]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
@@ -683,12 +684,25 @@
|
|||||||
(rx/of (dcm/change-team-role params)
|
(rx/of (dcm/change-team-role params)
|
||||||
(modal/hide)))))
|
(modal/hide)))))
|
||||||
|
|
||||||
|
(defn handle-change-team-org
|
||||||
|
[{:keys [team-id organization-id organization-name]}]
|
||||||
|
(ptk/reify ::handle-change-team-org
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(d/update-in-when state [:teams team-id] assoc
|
||||||
|
:organization-id organization-id
|
||||||
|
:organization-name organization-name)
|
||||||
|
state))))
|
||||||
|
|
||||||
|
|
||||||
(defn- process-message
|
(defn- process-message
|
||||||
[{:keys [type] :as msg}]
|
[{:keys [type] :as msg}]
|
||||||
(case type
|
(case type
|
||||||
:notification (dcm/handle-notification msg)
|
:notification (dcm/handle-notification msg)
|
||||||
:team-role-change (handle-change-team-role msg)
|
:team-role-change (handle-change-team-role msg)
|
||||||
:team-membership-change (dcm/team-membership-change msg)
|
:team-membership-change (dcm/team-membership-change msg)
|
||||||
|
:team-org-change (handle-change-team-org msg)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -214,8 +214,8 @@
|
|||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [change-fn
|
(let [change-fn
|
||||||
(fn [shape attrs]
|
(fn [node attrs]
|
||||||
(update shape :fills types.fills/prepend attrs))
|
(update node :fills types.fills/prepend attrs))
|
||||||
undo-id
|
undo-id
|
||||||
(js/Symbol)]
|
(js/Symbol)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
|||||||
@@ -539,11 +539,10 @@
|
|||||||
value shape-ids
|
value shape-ids
|
||||||
#{:stroke-width}
|
#{:stroke-width}
|
||||||
page-id))
|
page-id))
|
||||||
|
(some attributes #{:max-width :max-height})
|
||||||
(some attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w})
|
|
||||||
(conj #(update-layout-sizing-limits
|
(conj #(update-layout-sizing-limits
|
||||||
value shape-ids
|
value shape-ids
|
||||||
(set (filter attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w}))
|
(set (filter attributes #{:max-width :max-height}))
|
||||||
page-id))))
|
page-id))))
|
||||||
|
|
||||||
(defn apply-dimensions-token
|
(defn apply-dimensions-token
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
(or (> (:width srect) width)
|
(or (> (:width srect) width)
|
||||||
(> (:height srect) height))
|
(> (:height srect) height))
|
||||||
(let [srect (gal/adjust-to-viewport size srect {:padding 40})
|
(let [srect (gal/adjust-to-viewport size srect {:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width size) (:width srect))]
|
zoom (/ (:width size) (:width srect))]
|
||||||
|
|
||||||
(-> local
|
(-> local
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
state
|
state
|
||||||
(update state :workspace-local
|
(update state :workspace-local
|
||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport vport srect {:padding 160})
|
(let [srect (gal/adjust-to-viewport vport srect {:padding 160 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport) (:width srect))]
|
zoom (/ (:width vport) (:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
(assoc :zoom zoom)
|
(assoc :zoom zoom)
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
(gsh/shapes->rect))]
|
(gsh/shapes->rect))]
|
||||||
(update state :workspace-local
|
(update state :workspace-local
|
||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport vport srect {:padding 40})
|
(let [srect (gal/adjust-to-viewport vport srect {:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport) (:width srect))]
|
zoom (/ (:width vport) (:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
(assoc :zoom zoom)
|
(assoc :zoom zoom)
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport
|
(let [srect (gal/adjust-to-viewport
|
||||||
vport srect
|
vport srect
|
||||||
{:padding 40})
|
{:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport)
|
zoom (/ (:width vport)
|
||||||
(:width srect))]
|
(:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
;; Will contain the latest error report assigned
|
;; Will contain the latest error report assigned
|
||||||
(def last-report nil)
|
(def last-report nil)
|
||||||
|
|
||||||
|
;; Will contain last uncaught exception
|
||||||
|
(def last-exception nil)
|
||||||
|
|
||||||
(defn- print-data!
|
(defn- print-data!
|
||||||
[data]
|
[data]
|
||||||
(-> data
|
(-> data
|
||||||
@@ -338,7 +341,6 @@
|
|||||||
(print-data! werror)
|
(print-data! werror)
|
||||||
(print-explain! werror))))))))
|
(print-explain! werror))))))))
|
||||||
|
|
||||||
|
|
||||||
(defonce uncaught-error-handler
|
(defonce uncaught-error-handler
|
||||||
(letfn [(is-ignorable-exception? [cause]
|
(letfn [(is-ignorable-exception? [cause]
|
||||||
(let [message (ex-message cause)]
|
(let [message (ex-message cause)]
|
||||||
@@ -349,10 +351,31 @@
|
|||||||
|
|
||||||
(on-unhandled-error [event]
|
(on-unhandled-error [event]
|
||||||
(.preventDefault ^js event)
|
(.preventDefault ^js event)
|
||||||
(when-let [error (unchecked-get event "error")]
|
(when-let [cause (unchecked-get event "error")]
|
||||||
(when-not (is-ignorable-exception? error)
|
(set! last-exception cause)
|
||||||
(on-error error))))]
|
(when-not (is-ignorable-exception? cause)
|
||||||
|
(ex/print-throwable cause :prefix "uncaught exception")
|
||||||
|
(st/async-emit!
|
||||||
|
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
|
||||||
|
:type :toast
|
||||||
|
:level :error
|
||||||
|
:timeout 3000})))))
|
||||||
|
|
||||||
|
|
||||||
|
(on-unhandled-rejection [event]
|
||||||
|
(.preventDefault ^js event)
|
||||||
|
(when-let [cause (unchecked-get event "reason")]
|
||||||
|
(set! last-exception cause)
|
||||||
|
(ex/print-throwable cause :prefix "uncaught rejection")
|
||||||
|
(st/async-emit!
|
||||||
|
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
|
||||||
|
:type :toast
|
||||||
|
:level :error
|
||||||
|
:timeout 3000}))))]
|
||||||
|
|
||||||
(.addEventListener glob/window "error" on-unhandled-error)
|
(.addEventListener glob/window "error" on-unhandled-error)
|
||||||
|
(.addEventListener glob/window "unhandledrejection" on-unhandled-rejection)
|
||||||
(fn []
|
(fn []
|
||||||
(.removeEventListener glob/window "error" on-unhandled-error))))
|
(.removeEventListener glob/window "error" on-unhandled-error)
|
||||||
|
(.removeEventListener glob/window "unhandledrejection" on-unhandled-rejection))))
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
[app.main.ui.dashboard.team-form]
|
[app.main.ui.dashboard.team-form]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
|
[app.main.ui.nitrate.nitrate-form]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.dom.dnd :as dnd]
|
[app.util.dom.dnd :as dnd]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
@@ -280,8 +281,8 @@
|
|||||||
|
|
||||||
(mf/defc teams-selector-dropdown*
|
(mf/defc teams-selector-dropdown*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
[{:keys [team profile teams] :rest props}]
|
[{:keys [team profile teams show-default-team allow-create-teams allow-create-org] :rest props}]
|
||||||
(let [on-create-click
|
(let [on-create-team-click
|
||||||
(mf/use-fn #(st/emit! (modal/show :team-form {})))
|
(mf/use-fn #(st/emit! (modal/show :team-form {})))
|
||||||
|
|
||||||
on-team-click
|
on-team-click
|
||||||
@@ -290,10 +291,19 @@
|
|||||||
(let [team-id (-> (dom/get-current-target event)
|
(let [team-id (-> (dom/get-current-target event)
|
||||||
(dom/get-data "value")
|
(dom/get-data "value")
|
||||||
(uuid/parse))]
|
(uuid/parse))]
|
||||||
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))]
|
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))
|
||||||
|
|
||||||
|
on-create-org-click
|
||||||
|
(mf/use-fn
|
||||||
|
(fn []
|
||||||
|
(if (:nitrate-licence profile)
|
||||||
|
;; TODO update when org creation route is ready
|
||||||
|
(dom/open-new-window "/control-center/org/create")
|
||||||
|
(st/emit! (modal/show :nitrate-form {})))))]
|
||||||
|
|
||||||
[:> dropdown-menu* props
|
[:> dropdown-menu* props
|
||||||
|
|
||||||
|
(when show-default-team
|
||||||
[:> dropdown-menu-item* {:on-click on-team-click
|
[:> dropdown-menu-item* {:on-click on-team-click
|
||||||
:data-value (:default-team-id profile)
|
:data-value (:default-team-id profile)
|
||||||
:class (stl/css :team-dropdown-item)}
|
:class (stl/css :team-dropdown-item)}
|
||||||
@@ -301,7 +311,7 @@
|
|||||||
|
|
||||||
[:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")]
|
[:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")]
|
||||||
(when (= (:default-team-id profile) (:id team))
|
(when (= (:default-team-id profile) (:id team))
|
||||||
tick-icon)]
|
tick-icon)])
|
||||||
|
|
||||||
(for [team-item (remove :is-default (vals teams))]
|
(for [team-item (remove :is-default (vals teams))]
|
||||||
[:> dropdown-menu-item* {:on-click on-team-click
|
[:> dropdown-menu-item* {:on-click on-team-click
|
||||||
@@ -322,11 +332,19 @@
|
|||||||
(when (= (:id team-item) (:id team))
|
(when (= (:id team-item) (:id team))
|
||||||
tick-icon)])
|
tick-icon)])
|
||||||
|
|
||||||
|
(when allow-create-teams
|
||||||
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
||||||
[:> dropdown-menu-item* {:on-click on-create-click
|
[:> dropdown-menu-item* {:on-click on-create-team-click
|
||||||
:class (stl/css :team-dropdown-item :action)}
|
:class (stl/css :team-dropdown-item :action)}
|
||||||
[:span {:class (stl/css :icon-wrapper)} add-icon]
|
[:span {:class (stl/css :icon-wrapper)} add-icon]
|
||||||
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]]))
|
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]])
|
||||||
|
|
||||||
|
(when allow-create-org
|
||||||
|
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
||||||
|
[:> dropdown-menu-item* {:on-click on-create-org-click
|
||||||
|
:class (stl/css :team-dropdown-item :action)}
|
||||||
|
[:span {:class (stl/css :icon-wrapper)} add-icon]
|
||||||
|
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-org")]])]))
|
||||||
|
|
||||||
(mf/defc team-options-dropdown*
|
(mf/defc team-options-dropdown*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
@@ -476,9 +494,80 @@
|
|||||||
:data-testid "delete-team"}
|
:data-testid "delete-team"}
|
||||||
(tr "dashboard.delete-team")])]))
|
(tr "dashboard.delete-team")])]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc sidebar-org-switch*
|
||||||
|
[{:keys [team profile]}]
|
||||||
|
(let [teams (->> (mf/deref refs/teams)
|
||||||
|
vals
|
||||||
|
(group-by :organization-id)
|
||||||
|
(map (fn [[_group entries]] (first entries)))
|
||||||
|
vec
|
||||||
|
(d/index-by :id))
|
||||||
|
|
||||||
|
teams (update-vals teams
|
||||||
|
(fn [t]
|
||||||
|
(assoc t :name (str "ORG: " (:organization-name t)))))
|
||||||
|
|
||||||
|
team (assoc team :name (str "ORG: " (:organization-name team)))
|
||||||
|
|
||||||
|
show-teams-menu*
|
||||||
|
(mf/use-state false)
|
||||||
|
|
||||||
|
show-teams-menu?
|
||||||
|
(deref show-teams-menu*)
|
||||||
|
|
||||||
|
on-show-teams-click
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [event]
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(swap! show-teams-menu* not)))
|
||||||
|
|
||||||
|
on-show-teams-keydown
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [event]
|
||||||
|
(when (or (kbd/space? event)
|
||||||
|
(kbd/enter? event))
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(some-> (dom/get-current-target event)
|
||||||
|
(dom/click!)))))
|
||||||
|
close-teams-menu
|
||||||
|
(mf/use-fn #(reset! show-teams-menu* false))]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :sidebar-team-switch)}
|
||||||
|
[:div {:class (stl/css :switch-content)}
|
||||||
|
[:button {:class (stl/css :current-team)
|
||||||
|
:on-click on-show-teams-click
|
||||||
|
:on-key-down on-show-teams-keydown}
|
||||||
|
|
||||||
|
[:div {:class (stl/css :team-name)}
|
||||||
|
[:img {:src (cf/resolve-team-photo-url team)
|
||||||
|
:class (stl/css :team-picture)
|
||||||
|
:alt (:name team)}]
|
||||||
|
[:span {:class (stl/css :team-text) :title (:name team)} (:name team)]]
|
||||||
|
|
||||||
|
arrow-icon]]
|
||||||
|
|
||||||
|
;; Teams Dropdown
|
||||||
|
|
||||||
|
[:> teams-selector-dropdown* {:show show-teams-menu?
|
||||||
|
:on-close close-teams-menu
|
||||||
|
:id "organizations-list"
|
||||||
|
:class (stl/css :dropdown :teams-dropdown)
|
||||||
|
:team team
|
||||||
|
:profile profile
|
||||||
|
:teams teams
|
||||||
|
:show-default-team false
|
||||||
|
:allow-create-teams false
|
||||||
|
:allow-create-org true}]]))
|
||||||
|
|
||||||
(mf/defc sidebar-team-switch*
|
(mf/defc sidebar-team-switch*
|
||||||
[{:keys [team profile]}]
|
[{:keys [team profile]}]
|
||||||
(let [teams (mf/deref refs/teams)
|
(let [nitrate? (contains? cf/flags :nitrate)
|
||||||
|
org-id (when nitrate? (:organization-id team))
|
||||||
|
teams (cond->> (mf/deref refs/teams)
|
||||||
|
nitrate?
|
||||||
|
(filter #(= (-> % val :organization-id) org-id)))
|
||||||
|
|
||||||
subscription
|
subscription
|
||||||
(get team :subscription)
|
(get team :subscription)
|
||||||
@@ -586,7 +675,10 @@
|
|||||||
:class (stl/css :dropdown :teams-dropdown)
|
:class (stl/css :dropdown :teams-dropdown)
|
||||||
:team team
|
:team team
|
||||||
:profile profile
|
:profile profile
|
||||||
:teams teams}]
|
:teams teams
|
||||||
|
:show-default-team true
|
||||||
|
:allow-create-teams true
|
||||||
|
:allow-create-org false}]
|
||||||
|
|
||||||
[:> team-options-dropdown* {:show show-team-options-menu?
|
[:> team-options-dropdown* {:show show-team-options-menu?
|
||||||
:on-close close-team-options-menu
|
:on-close close-team-options-menu
|
||||||
@@ -703,6 +795,8 @@
|
|||||||
[:*
|
[:*
|
||||||
[:div {:class (stl/css-case :sidebar-content true)
|
[:div {:class (stl/css-case :sidebar-content true)
|
||||||
:ref container}
|
:ref container}
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
[:> sidebar-org-switch* {:team team :profile profile}])
|
||||||
[:> sidebar-team-switch* {:team team :profile profile}]
|
[:> sidebar-team-switch* {:team team :profile profile}]
|
||||||
|
|
||||||
[:> sidebar-search* {:search-term search-term
|
[:> sidebar-search* {:search-term search-term
|
||||||
|
|||||||
@@ -189,7 +189,6 @@
|
|||||||
:float
|
:float
|
||||||
:string
|
:string
|
||||||
[:= :multiple]]]]
|
[:= :multiple]]]]
|
||||||
[:text-icon {:optional true} :string]
|
|
||||||
[:default {:optional true} [:maybe :string]]
|
[:default {:optional true} [:maybe :string]]
|
||||||
[:placeholder {:optional true} :string]
|
[:placeholder {:optional true} :string]
|
||||||
[:icon {:optional true} [:maybe schema:icon]]
|
[:icon {:optional true} [:maybe schema:icon]]
|
||||||
@@ -217,8 +216,7 @@
|
|||||||
is-selected-on-focus nillable
|
is-selected-on-focus nillable
|
||||||
tokens applied-token empty-to-end
|
tokens applied-token empty-to-end
|
||||||
on-change on-blur on-focus on-detach
|
on-change on-blur on-focus on-detach
|
||||||
property align ref name
|
property align ref name]
|
||||||
text-icon]
|
|
||||||
:rest props}]
|
:rest props}]
|
||||||
|
|
||||||
(let [;; NOTE: we use mfu/bean here for transparently handle
|
(let [;; NOTE: we use mfu/bean here for transparently handle
|
||||||
@@ -639,23 +637,14 @@
|
|||||||
:on-change store-raw-value
|
:on-change store-raw-value
|
||||||
:variant "comfortable"
|
:variant "comfortable"
|
||||||
:disabled disabled
|
:disabled disabled
|
||||||
:slot-start (when (or icon text-icon)
|
:slot-start (when icon
|
||||||
(mf/html
|
(mf/html [:> tooltip*
|
||||||
[:> tooltip*
|
|
||||||
{:content property
|
{:content property
|
||||||
:id property}
|
:id property}
|
||||||
(cond
|
[:> icon* {:icon-id icon
|
||||||
icon
|
|
||||||
[:> icon*
|
|
||||||
{:icon-id icon
|
|
||||||
:size "s"
|
:size "s"
|
||||||
:aria-labelledby property
|
:aria-labelledby property
|
||||||
:class (stl/css :icon)}]
|
:class (stl/css :icon)}]]))
|
||||||
|
|
||||||
text-icon
|
|
||||||
[:div {:class (stl/css :text-icon)
|
|
||||||
:aria-labelledby property}
|
|
||||||
text-icon])]))
|
|
||||||
:slot-end (when-not disabled
|
:slot-end (when-not disabled
|
||||||
(when (some? tokens)
|
(when (some? tokens)
|
||||||
(mf/html [:> icon-button* {:variant "ghost"
|
(mf/html [:> icon-button* {:variant "ghost"
|
||||||
@@ -687,23 +676,14 @@
|
|||||||
:disabled disabled
|
:disabled disabled
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:class inner-class
|
:class inner-class
|
||||||
:slot-start (when (or icon text-icon)
|
:slot-start (when icon
|
||||||
(mf/html
|
(mf/html [:> tooltip*
|
||||||
[:> tooltip*
|
|
||||||
{:content property
|
{:content property
|
||||||
:id property}
|
:id property}
|
||||||
(cond
|
[:> icon* {:icon-id icon
|
||||||
icon
|
|
||||||
[:> icon*
|
|
||||||
{:icon-id icon
|
|
||||||
:size "s"
|
:size "s"
|
||||||
:aria-labelledby property
|
:aria-labelledby property
|
||||||
:class (stl/css :icon)}]
|
:class (stl/css :icon)}]]))
|
||||||
|
|
||||||
text-icon
|
|
||||||
[:div {:class (stl/css :text-icon)
|
|
||||||
:aria-labelledby property}
|
|
||||||
text-icon])]))
|
|
||||||
:token-wrapper-ref token-wrapper-ref
|
:token-wrapper-ref token-wrapper-ref
|
||||||
:token-detach-btn-ref token-detach-btn-ref
|
:token-detach-btn-ref token-detach-btn-ref
|
||||||
:detach-token detach-token})))]
|
:detach-token detach-token})))]
|
||||||
@@ -738,7 +718,6 @@
|
|||||||
(mf/with-effect [dropdown-options]
|
(mf/with-effect [dropdown-options]
|
||||||
(mf/set-ref-val! options-ref dropdown-options))
|
(mf/set-ref-val! options-ref dropdown-options))
|
||||||
|
|
||||||
(if (some? icon)
|
|
||||||
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
||||||
:ref wrapper-ref}
|
:ref wrapper-ref}
|
||||||
|
|
||||||
@@ -756,23 +735,4 @@
|
|||||||
:focused focused-id
|
:focused focused-id
|
||||||
:align align
|
:align align
|
||||||
:empty-to-end empty-to-end
|
:empty-to-end empty-to-end
|
||||||
:ref set-option-ref}]))]
|
:ref set-option-ref}]))]))
|
||||||
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
|
||||||
:aria-labelledby property
|
|
||||||
:ref wrapper-ref}
|
|
||||||
|
|
||||||
(if (and (some? token-applied)
|
|
||||||
(not= :multiple token-applied))
|
|
||||||
[:> token-field* token-props]
|
|
||||||
[:> input-field* input-props])
|
|
||||||
|
|
||||||
(when ^boolean is-open
|
|
||||||
(let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)]
|
|
||||||
[:> options-dropdown* {:on-click on-option-click
|
|
||||||
:id listbox-id
|
|
||||||
:options options
|
|
||||||
:selected selected-id
|
|
||||||
:focused focused-id
|
|
||||||
:align align
|
|
||||||
:empty-to-end empty-to-end
|
|
||||||
:ref set-option-ref}]))])))
|
|
||||||
|
|||||||
@@ -29,14 +29,7 @@
|
|||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
min-inline-size: var(--sp-l);
|
min-width: var(--sp-l);
|
||||||
}
|
|
||||||
|
|
||||||
.text-icon {
|
|
||||||
color: var(--color-foreground-secondary);
|
|
||||||
@include t.use-typography("code-font");
|
|
||||||
inline-size: fit-content;
|
|
||||||
min-inline-size: px2rem(40);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.invisible-button {
|
.invisible-button {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
--token-field-outline-color: none;
|
--token-field-outline-color: none;
|
||||||
--token-field-height: var(--sp-xxxl);
|
--token-field-height: var(--sp-xxxl);
|
||||||
--token-field-margin: unset;
|
--token-field-margin: unset;
|
||||||
display: inline-flex;
|
display: grid;
|
||||||
column-gap: var(--sp-xs);
|
column-gap: var(--sp-xs);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"React error boundary components"
|
"React error boundary components"
|
||||||
(:require
|
(:require
|
||||||
["react-error-boundary" :as reb]
|
["react-error-boundary" :as reb]
|
||||||
|
[app.common.exceptions :as ex]
|
||||||
[app.main.errors :as errors]
|
[app.main.errors :as errors]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[goog.functions :as gfn]
|
[goog.functions :as gfn]
|
||||||
@@ -34,7 +35,8 @@
|
|||||||
;; very small amount of time, so we debounce for 100ms for
|
;; very small amount of time, so we debounce for 100ms for
|
||||||
;; avoid duplicate and redundant reports
|
;; avoid duplicate and redundant reports
|
||||||
(gfn/debounce (fn [error info]
|
(gfn/debounce (fn [error info]
|
||||||
(js/console.log "Cause stack: \n" (.-stack error))
|
(set! errors/last-exception error)
|
||||||
|
(ex/print-throwable error)
|
||||||
(js/console.error
|
(js/console.error
|
||||||
"Component trace: \n"
|
"Component trace: \n"
|
||||||
(unchecked-get info "componentStack")
|
(unchecked-get info "componentStack")
|
||||||
|
|||||||
48
frontend/src/app/main/ui/nitrate/nitrate_form.cljs
Normal file
48
frontend/src/app/main/ui/nitrate/nitrate_form.cljs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.main.ui.nitrate.nitrate-form
|
||||||
|
(:require-macros [app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
;; FIXME: rename to `form` (remove the nitrate prefix from namespace,
|
||||||
|
;; because it is already under nitrate)
|
||||||
|
|
||||||
|
(mf/defc nitrate-form-modal*
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :nitrate-form}
|
||||||
|
[]
|
||||||
|
(let [on-click
|
||||||
|
(mf/use-fn
|
||||||
|
(fn []
|
||||||
|
(dom/open-new-window "/control-center/licenses/start")))]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
|
[:div {:class (stl/css :modal-container)}
|
||||||
|
[:div {:class (stl/css :nitrate-form)}
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-header)}
|
||||||
|
[:h2 {:class (stl/css :modal-title)}
|
||||||
|
"BUY NITRATE"]
|
||||||
|
|
||||||
|
[:button {:class (stl/css :modal-close-btn)
|
||||||
|
:on-click modal/hide!} deprecated-icon/close]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-content)}
|
||||||
|
"Nitrate is so cool! You should buy it!"]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-footer)}
|
||||||
|
[:div {:class (stl/css :action-buttons)}
|
||||||
|
[:> button* {:variant "primary"
|
||||||
|
:on-click on-click}
|
||||||
|
"BUY NOW!"]]]]]]))
|
||||||
|
|
||||||
|
|
||||||
52
frontend/src/app/main/ui/nitrate/nitrate_form.scss
Normal file
52
frontend/src/app/main/ui/nitrate/nitrate_form.scss
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@use "refactor/common-refactor.scss" as deprecated;
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
@extend .modal-overlay-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
@extend .modal-container-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
margin-bottom: deprecated.$s-24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
@include deprecated.uppercaseTitleTipography;
|
||||||
|
color: var(--modal-title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-close-btn {
|
||||||
|
@extend .modal-close-btn-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
margin-bottom: deprecated.$s-24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nitrate-form {
|
||||||
|
min-width: deprecated.$s-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
@extend .modal-action-btns;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
@extend .modal-cancel-btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accept-btn {
|
||||||
|
@extend .modal-accept-btn;
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
@extend .modal-danger-btn;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -437,7 +437,8 @@
|
|||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.flatten")
|
[:> menu-entry* {:title (tr "workspace.shape.menu.flatten")
|
||||||
:on-click do-transform-to-path}])
|
:on-click do-transform-to-path}])
|
||||||
|
|
||||||
(when (and (not disable-booleans)
|
(when (and (not has-frame?)
|
||||||
|
(not disable-booleans)
|
||||||
(or multiple? (and single? (or is-group? is-bool?))))
|
(or multiple? (and single? (or is-group? is-bool?))))
|
||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.path")}
|
[:> menu-entry* {:title (tr "workspace.shape.menu.path")}
|
||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.union")
|
[:> menu-entry* {:title (tr "workspace.shape.menu.union")
|
||||||
|
|||||||
@@ -119,7 +119,6 @@
|
|||||||
[:button {:class (stl/css-case
|
[:button {:class (stl/css-case
|
||||||
:toggle-content true
|
:toggle-content true
|
||||||
:inverse expanded?)
|
:inverse expanded?)
|
||||||
:aria-label "Toggle layer"
|
|
||||||
:on-click on-toggle-collapse}
|
:on-click on-toggle-collapse}
|
||||||
deprecated-icon/arrow])
|
deprecated-icon/arrow])
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,17 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.types.shape.radius :as ctsr]
|
[app.common.types.shape.radius :as ctsr]
|
||||||
|
[app.common.types.token :as tk]
|
||||||
[app.main.data.workspace.shapes :as dwsh]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]
|
||||||
@@ -44,6 +46,63 @@
|
|||||||
(identical? (get old-values :r4)
|
(identical? (get old-values :r4)
|
||||||
(get new-values :r4)))))
|
(get new-values :r4)))))
|
||||||
|
|
||||||
|
(mf/defc numeric-input-wrapper*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [values name applied-tokens align on-detach radius] :rest props}]
|
||||||
|
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
||||||
|
tokens (mf/with-memo [tokens name]
|
||||||
|
(delay
|
||||||
|
(-> (deref tokens)
|
||||||
|
(select-keys (get tk/tokens-by-input name))
|
||||||
|
(not-empty))))
|
||||||
|
on-detach-attr
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-detach name)
|
||||||
|
#(on-detach % name))
|
||||||
|
|
||||||
|
r1-value (get applied-tokens :r1)
|
||||||
|
all-token-equal? (and (seq applied-tokens) (all-equal? applied-tokens))
|
||||||
|
all-values-equal? (all-equal? values)
|
||||||
|
|
||||||
|
applied-token (cond
|
||||||
|
(not (seq applied-tokens))
|
||||||
|
nil
|
||||||
|
|
||||||
|
(and (= radius :all) (or (not all-values-equal?) (not all-token-equal?)))
|
||||||
|
:multiple
|
||||||
|
|
||||||
|
(and all-token-equal? all-values-equal? (= radius :all))
|
||||||
|
r1-value
|
||||||
|
|
||||||
|
:else
|
||||||
|
(get applied-tokens radius))
|
||||||
|
|
||||||
|
|
||||||
|
placeholder (if (= radius :all)
|
||||||
|
(cond
|
||||||
|
(or (not all-values-equal?)
|
||||||
|
(not all-token-equal?))
|
||||||
|
(tr "settings.multiple")
|
||||||
|
:else
|
||||||
|
"--")
|
||||||
|
|
||||||
|
(cond
|
||||||
|
(or (= :multiple (:applied-tokens values))
|
||||||
|
(= :multiple (get values name)))
|
||||||
|
(tr "settings.multiple")
|
||||||
|
:else
|
||||||
|
"--"))
|
||||||
|
|
||||||
|
|
||||||
|
props (mf/spread-props props
|
||||||
|
{:placeholder placeholder
|
||||||
|
:applied-token applied-token
|
||||||
|
:tokens (if (delay? tokens) @tokens tokens)
|
||||||
|
:align align
|
||||||
|
:on-detach on-detach-attr
|
||||||
|
:value values})]
|
||||||
|
[:> numeric-input* props]))
|
||||||
|
|
||||||
(mf/defc border-radius-menu*
|
(mf/defc border-radius-menu*
|
||||||
{::mf/wrap [#(mf/memo' % check-border-radius-menu-props)]}
|
{::mf/wrap [#(mf/memo' % check-border-radius-menu-props)]}
|
||||||
[{:keys [class ids values applied-tokens]}]
|
[{:keys [class ids values applied-tokens]}]
|
||||||
@@ -51,7 +110,6 @@
|
|||||||
(features/use-feature "tokens/numeric-input")
|
(features/use-feature "tokens/numeric-input")
|
||||||
|
|
||||||
all-values-equal? (all-equal? values)
|
all-values-equal? (all-equal? values)
|
||||||
all-token-equal? (and (seq applied-tokens) (all-equal? applied-tokens))
|
|
||||||
|
|
||||||
radius-expanded* (mf/use-state false)
|
radius-expanded* (mf/use-state false)
|
||||||
radius-expanded (deref radius-expanded*)
|
radius-expanded (deref radius-expanded*)
|
||||||
@@ -177,27 +235,14 @@
|
|||||||
:on-detach on-detach-all
|
:on-detach on-detach-all
|
||||||
:icon i/corner-radius
|
:icon i/corner-radius
|
||||||
:min 0
|
:min 0
|
||||||
:attr :border-radius
|
:name :border-radius
|
||||||
:nillable true
|
:nillable true
|
||||||
:property (tr "workspace.options.radius")
|
:property (tr "workspace.options.radius")
|
||||||
:class (stl/css :radius-wrapper)
|
:class (stl/css :radius-wrapper)
|
||||||
:applied-token (cond
|
:applied-tokens applied-tokens
|
||||||
(not (seq applied-tokens))
|
:radius :all
|
||||||
nil
|
|
||||||
|
|
||||||
(or (not all-values-equal?) (not all-token-equal?))
|
|
||||||
:multiple
|
|
||||||
|
|
||||||
:else
|
|
||||||
(get applied-tokens :r1))
|
|
||||||
:align :right
|
:align :right
|
||||||
:placeholder (cond
|
:values (if all-values-equal?
|
||||||
(or (not all-values-equal?)
|
|
||||||
(not all-token-equal?))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
:else
|
|
||||||
"--")
|
|
||||||
:value (if all-values-equal?
|
|
||||||
(if (nil? (:r1 values))
|
(if (nil? (:r1 values))
|
||||||
0
|
0
|
||||||
(:r1 values))
|
(:r1 values))
|
||||||
@@ -231,76 +276,56 @@
|
|||||||
{:on-change on-radius-r1-change
|
{:on-change on-radius-r1-change
|
||||||
:on-detach on-detach-r1
|
:on-detach on-detach-r1
|
||||||
:min 0
|
:min 0
|
||||||
:attr :border-radius
|
:name :border-radius
|
||||||
:property (tr "workspace.options.radius-top-left")
|
:property (tr "workspace.options.radius-top-left")
|
||||||
:applied-token (get applied-tokens :r1)
|
:applied-tokens applied-tokens
|
||||||
|
:radius :r1
|
||||||
:align :right
|
:align :right
|
||||||
:placeholder (cond
|
|
||||||
(or (= :multiple (get applied-tokens :r1))
|
|
||||||
(= :multiple (get values :r1)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
:else
|
|
||||||
"--")
|
|
||||||
:class (stl/css :radius-wrapper :dropdown-offset)
|
:class (stl/css :radius-wrapper :dropdown-offset)
|
||||||
:inner-class (stl/css :no-icon-input)
|
:inner-class (stl/css :no-icon-input)
|
||||||
:value (:r1 values)}]
|
:values (:r1 values)}]
|
||||||
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:on-change on-radius-r2-change
|
{:on-change on-radius-r2-change
|
||||||
:on-detach on-detach-r2
|
:on-detach on-detach-r2
|
||||||
:min 0
|
:min 0
|
||||||
:attr :border-radius
|
:name :border-radius
|
||||||
:nillable true
|
:nillable true
|
||||||
:property (tr "workspace.options.radius-top-right")
|
:property (tr "workspace.options.radius-top-right")
|
||||||
:applied-token (get applied-tokens :r2)
|
:applied-tokens applied-tokens
|
||||||
:align :right
|
:align :right
|
||||||
:class (stl/css :radius-wrapper)
|
:class (stl/css :radius-wrapper)
|
||||||
:inner-class (stl/css :no-icon-input)
|
:inner-class (stl/css :no-icon-input)
|
||||||
:placeholder (cond
|
:radius :r2
|
||||||
(or (= :multiple (get applied-tokens :r2))
|
:values (:r2 values)}]
|
||||||
(= :multiple (get values :r2)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
:else
|
|
||||||
"--")
|
|
||||||
:value (:r2 values)}]
|
|
||||||
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:on-change on-radius-r4-change
|
{:on-change on-radius-r4-change
|
||||||
:on-detach on-detach-r4
|
:on-detach on-detach-r4
|
||||||
:min 0
|
:min 0
|
||||||
:attr :border-radius
|
:name :border-radius
|
||||||
:nillable true
|
:nillable true
|
||||||
:property (tr "workspace.options.radius-bottom-left")
|
:property (tr "workspace.options.radius-bottom-left")
|
||||||
:applied-token (get applied-tokens :r4)
|
:applied-tokens applied-tokens
|
||||||
:class (stl/css :radius-wrapper :dropdown-offset)
|
:class (stl/css :radius-wrapper :dropdown-offset)
|
||||||
:inner-class (stl/css :no-icon-input)
|
:inner-class (stl/css :no-icon-input)
|
||||||
:placeholder (cond
|
:radius :r4
|
||||||
(or (= :multiple (get applied-tokens :r4))
|
|
||||||
(= :multiple (get values :r4)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
:else
|
|
||||||
"--")
|
|
||||||
:align :right
|
:align :right
|
||||||
:value (:r4 values)}]
|
:values (:r4 values)}]
|
||||||
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:on-change on-radius-r3-change
|
{:on-change on-radius-r3-change
|
||||||
:on-detach on-detach-r3
|
:on-detach on-detach-r3
|
||||||
:min 0
|
:min 0
|
||||||
:attr :border-radius
|
:name :border-radius
|
||||||
:nillable true
|
:nillable true
|
||||||
:property (tr "workspace.options.radius-bottom-right")
|
:property (tr "workspace.options.radius-bottom-right")
|
||||||
:applied-token (get applied-tokens :r3)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (cond
|
:radius :r3
|
||||||
(or (= :multiple (get applied-tokens :r3))
|
|
||||||
(= :multiple (get values :r3)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
:else
|
|
||||||
"--")
|
|
||||||
:align :right
|
:align :right
|
||||||
:class (stl/css :radius-wrapper)
|
:class (stl/css :radius-wrapper)
|
||||||
:inner-class (stl/css :no-icon-input)
|
:inner-class (stl/css :no-icon-input)
|
||||||
:value (:r3 values)}]]
|
:values (:r3 values)}]]
|
||||||
|
|
||||||
[:div {:class (stl/css :radius-4)}
|
[:div {:class (stl/css :radius-4)}
|
||||||
[:div {:class (stl/css :small-input)}
|
[:div {:class (stl/css :small-input)}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
(ns app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens
|
|
||||||
(:require
|
|
||||||
[app.common.types.token :as tk]
|
|
||||||
[app.main.ui.context :as muc]
|
|
||||||
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
|
||||||
[rumext.v2 :as mf]))
|
|
||||||
|
|
||||||
(mf/defc numeric-input-wrapper*
|
|
||||||
[{:keys [value attr applied-token align on-detach placeholder input-type] :rest props}]
|
|
||||||
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
|
||||||
|
|
||||||
tokens (mf/with-memo [tokens input-type]
|
|
||||||
(delay
|
|
||||||
(-> (deref tokens)
|
|
||||||
(select-keys (get tk/tokens-by-input (or input-type attr)))
|
|
||||||
(not-empty))))
|
|
||||||
|
|
||||||
on-detach-attr
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps on-detach attr)
|
|
||||||
#(on-detach % attr))
|
|
||||||
|
|
||||||
props (mf/spread-props props
|
|
||||||
{:placeholder (or placeholder
|
|
||||||
(if (= :multiple value)
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--"))
|
|
||||||
:applied-token applied-token
|
|
||||||
:tokens (if (delay? tokens) @tokens tokens)
|
|
||||||
:align align
|
|
||||||
:on-detach on-detach-attr
|
|
||||||
:name attr
|
|
||||||
:value value})]
|
|
||||||
[:> numeric-input* props]))
|
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.types.token :as tk]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.shapes :as dwsh]
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
@@ -16,9 +17,10 @@
|
|||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.render-wasm.api :as wasm.api]
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@@ -60,6 +62,36 @@
|
|||||||
(identical? (get old-values :hidden)
|
(identical? (get old-values :hidden)
|
||||||
(get new-values :hidden)))))
|
(get new-values :hidden)))))
|
||||||
|
|
||||||
|
(mf/defc numeric-input-wrapper*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [values name applied-tokens align on-detach] :rest props}]
|
||||||
|
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
||||||
|
tokens (mf/with-memo [tokens name]
|
||||||
|
(delay
|
||||||
|
(-> (deref tokens)
|
||||||
|
(select-keys (get tk/tokens-by-input name))
|
||||||
|
(not-empty))))
|
||||||
|
|
||||||
|
on-detach-attr (mf/use-fn
|
||||||
|
(mf/deps on-detach name)
|
||||||
|
#(on-detach % name))
|
||||||
|
|
||||||
|
applied-token (get applied-tokens name)
|
||||||
|
opacity-value (or (get values name) 1)
|
||||||
|
|
||||||
|
props (mf/spread-props props
|
||||||
|
{:placeholder (if (or (= :multiple (:applied-tokens values))
|
||||||
|
(= :multiple opacity-value))
|
||||||
|
(tr "settings.multiple")
|
||||||
|
"--")
|
||||||
|
:applied-token applied-token
|
||||||
|
:tokens (if (delay? tokens) @tokens tokens)
|
||||||
|
:align align
|
||||||
|
:on-detach on-detach-attr
|
||||||
|
:name name
|
||||||
|
:value (* 100 opacity-value)})]
|
||||||
|
[:> numeric-input* props]))
|
||||||
|
|
||||||
(mf/defc layer-menu*
|
(mf/defc layer-menu*
|
||||||
{::mf/wrap [#(mf/memo' % check-layer-menu-props)]}
|
{::mf/wrap [#(mf/memo' % check-layer-menu-props)]}
|
||||||
[{:keys [ids values applied-tokens]}]
|
[{:keys [ids values applied-tokens]}]
|
||||||
@@ -218,24 +250,22 @@
|
|||||||
:on-pointer-enter-option handle-blend-mode-enter
|
:on-pointer-enter-option handle-blend-mode-enter
|
||||||
:on-pointer-leave-option handle-blend-mode-leave}]]
|
:on-pointer-leave-option handle-blend-mode-leave}]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(if token-numeric-inputs
|
(if token-numeric-inputs
|
||||||
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:on-change on-opacity-change
|
{:on-change on-opacity-change
|
||||||
:on-detach on-detach-token
|
:on-detach on-detach-token
|
||||||
:icon i/percentage
|
:icon i/percentage
|
||||||
:min 0
|
:min 0
|
||||||
:max 100
|
:max 100
|
||||||
:attr :opacity
|
:name :opacity
|
||||||
:property (tr "workspace.options.opacity")
|
:property (tr "workspace.options.opacity")
|
||||||
:applied-token (get applied-tokens :opacity)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (if (or (= :multiple (get applied-tokens :opacity))
|
|
||||||
(= :multiple (or (get values name) 1)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:align :right
|
:align :right
|
||||||
:class (stl/css :numeric-input-wrapper)
|
:class (stl/css :numeric-input-wrapper)
|
||||||
:value (* 100
|
:values values}]
|
||||||
(or (get values name) 1))}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :input)
|
[:div {:class (stl/css :input)
|
||||||
:title (tr "workspace.options.opacity")}
|
:title (tr "workspace.options.opacity")}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.common.types.token :as tk]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.event :as-alias ev]
|
[app.main.data.event :as-alias ev]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
@@ -24,14 +25,15 @@
|
|||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.formats :as fmt]
|
[app.main.ui.formats :as fmt]
|
||||||
[app.main.ui.hooks :as h]
|
[app.main.ui.hooks :as h]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
@@ -46,6 +48,44 @@
|
|||||||
:column i/column
|
:column i/column
|
||||||
:column-reverse i/column-reverse))
|
:column-reverse i/column-reverse))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc numeric-input-wrapper*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [values name applied-tokens align on-detach] :rest props}]
|
||||||
|
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
||||||
|
input-type (cond
|
||||||
|
(some #{:p2 :p4} [name])
|
||||||
|
:horizontal-padding
|
||||||
|
|
||||||
|
(some #{:p1 :p3} [name])
|
||||||
|
:vertical-padding
|
||||||
|
:else
|
||||||
|
name)
|
||||||
|
|
||||||
|
tokens (mf/with-memo [tokens input-type]
|
||||||
|
(delay
|
||||||
|
(-> (deref tokens)
|
||||||
|
(select-keys (get tk/tokens-by-input input-type))
|
||||||
|
(not-empty))))
|
||||||
|
on-detach-attr
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-detach name)
|
||||||
|
#(on-detach % name))
|
||||||
|
|
||||||
|
props (mf/spread-props props
|
||||||
|
{:placeholder (if (or (= :multiple (:applied-tokens values))
|
||||||
|
(= :multiple (get values name))
|
||||||
|
(nil? (get values name)))
|
||||||
|
(tr "settings.multiple")
|
||||||
|
"--")
|
||||||
|
:class (stl/css :numeric-input-layout)
|
||||||
|
:applied-token (get applied-tokens name)
|
||||||
|
:tokens tokens
|
||||||
|
:align align
|
||||||
|
:on-detach on-detach-attr
|
||||||
|
:value (get values name)})]
|
||||||
|
[:> numeric-input* props]))
|
||||||
|
|
||||||
;; FLEX COMPONENTS
|
;; FLEX COMPONENTS
|
||||||
|
|
||||||
(def layout-container-flex-attrs
|
(def layout-container-flex-attrs
|
||||||
@@ -375,17 +415,11 @@
|
|||||||
:on-focus on-focus-p1
|
:on-focus on-focus-p1
|
||||||
:icon i/padding-top-bottom
|
:icon i/padding-top-bottom
|
||||||
:min 0
|
:min 0
|
||||||
:attr :p1
|
:name :p1
|
||||||
:input-type :vertical-padding
|
|
||||||
:property (tr "workspace.layout-grid.editor.padding.vertical")
|
:property (tr "workspace.layout-grid.editor.padding.vertical")
|
||||||
:nillable true
|
:nillable true
|
||||||
:placeholder (if (or (= :multiple applied-to-p1)
|
:applied-tokens {:p1 applied-to-p1}
|
||||||
(= :multiple p1)
|
:values {:p1 p1}}]
|
||||||
(nil? p1))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token applied-to-p1
|
|
||||||
:value p1}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-simple)
|
[:div {:class (stl/css :padding-simple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.vertical")}
|
:title (tr "workspace.layout-grid.editor.padding.vertical")}
|
||||||
@@ -410,18 +444,12 @@
|
|||||||
:on-focus on-focus-p2
|
:on-focus on-focus-p2
|
||||||
:icon i/padding-left-right
|
:icon i/padding-left-right
|
||||||
:min 0
|
:min 0
|
||||||
:attr :p2
|
:name :p2
|
||||||
:input-type :horizontal-padding
|
|
||||||
:align :right
|
:align :right
|
||||||
:property (tr "workspace.layout-grid.editor.padding.horizontal")
|
:property (tr "workspace.layout-grid.editor.padding.horizontal")
|
||||||
:nillable true
|
:nillable true
|
||||||
:applied-token applied-to-p2
|
:applied-tokens {:p2 applied-to-p2}
|
||||||
:placeholder (if (or (= :multiple applied-to-p2)
|
:values {:p2 p2}}]
|
||||||
(= :multiple p2)
|
|
||||||
(nil? p2))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:value p2}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-simple)
|
[:div {:class (stl/css :padding-simple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.horizontal")}
|
:title (tr "workspace.layout-grid.editor.padding.horizontal")}
|
||||||
@@ -448,11 +476,6 @@
|
|||||||
p3 (:p3 value)
|
p3 (:p3 value)
|
||||||
p4 (:p4 value)
|
p4 (:p4 value)
|
||||||
|
|
||||||
applied-to-p1 (:p1 applied-tokens)
|
|
||||||
applied-to-p2 (:p2 applied-tokens)
|
|
||||||
applied-to-p3 (:p3 applied-tokens)
|
|
||||||
applied-to-p4 (:p4 applied-tokens)
|
|
||||||
|
|
||||||
on-change'
|
on-change'
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-change ids)
|
(mf/deps on-change ids)
|
||||||
@@ -512,15 +535,10 @@
|
|||||||
:on-focus on-focus-p1
|
:on-focus on-focus-p1
|
||||||
:icon i/padding-top
|
:icon i/padding-top
|
||||||
:min 0
|
:min 0
|
||||||
:attr :p1
|
:name :p1
|
||||||
:input-type :vertical-padding
|
|
||||||
:property (tr "workspace.layout-grid.editor.padding.top")
|
:property (tr "workspace.layout-grid.editor.padding.top")
|
||||||
:placeholder (if (or (= :multiple applied-to-p1)
|
:applied-tokens applied-tokens
|
||||||
(= :multiple p1))
|
:values value}]
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token applied-to-p1
|
|
||||||
:value p1}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-multiple)
|
[:div {:class (stl/css :padding-multiple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.top")}
|
:title (tr "workspace.layout-grid.editor.padding.top")}
|
||||||
@@ -545,16 +563,11 @@
|
|||||||
:on-focus on-focus-p2
|
:on-focus on-focus-p2
|
||||||
:icon i/padding-right
|
:icon i/padding-right
|
||||||
:min 0
|
:min 0
|
||||||
:attr :p2
|
:name :p2
|
||||||
:input-type :horizontal-padding
|
|
||||||
:align :right
|
:align :right
|
||||||
:property (tr "workspace.layout-grid.editor.padding.right")
|
:property (tr "workspace.layout-grid.editor.padding.right")
|
||||||
:placeholder (if (or (= :multiple applied-to-p2)
|
:applied-tokens applied-tokens
|
||||||
(= :multiple p2))
|
:values value}]
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token applied-to-p2
|
|
||||||
:value p2}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-multiple)
|
[:div {:class (stl/css :padding-multiple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.right")}
|
:title (tr "workspace.layout-grid.editor.padding.right")}
|
||||||
@@ -579,15 +592,10 @@
|
|||||||
:on-focus on-focus-p3
|
:on-focus on-focus-p3
|
||||||
:icon i/padding-bottom
|
:icon i/padding-bottom
|
||||||
:min 0
|
:min 0
|
||||||
:attr :p3
|
:name :p3
|
||||||
:input-type :vertical-padding
|
|
||||||
:property (tr "workspace.layout-grid.editor.padding.bottom")
|
:property (tr "workspace.layout-grid.editor.padding.bottom")
|
||||||
:placeholder (if (or (= :multiple applied-to-p3)
|
:applied-tokens applied-tokens
|
||||||
(= :multiple p3))
|
:values value}]
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token applied-to-p3
|
|
||||||
:value p3}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-multiple)
|
[:div {:class (stl/css :padding-multiple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.bottom")}
|
:title (tr "workspace.layout-grid.editor.padding.bottom")}
|
||||||
@@ -613,15 +621,10 @@
|
|||||||
:icon i/padding-left
|
:icon i/padding-left
|
||||||
:min 0
|
:min 0
|
||||||
:align :right
|
:align :right
|
||||||
:attr :p4
|
:name :p4
|
||||||
:input-type :horizontal-padding
|
|
||||||
:property (tr "workspace.layout-grid.editor.padding.left")
|
:property (tr "workspace.layout-grid.editor.padding.left")
|
||||||
:placeholder (if (or (= :multiple applied-to-p4)
|
:applied-tokens applied-tokens
|
||||||
(= :multiple p4))
|
:values value}]
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token applied-to-p4
|
|
||||||
:value p4}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :padding-multiple)
|
[:div {:class (stl/css :padding-multiple)
|
||||||
:title (tr "workspace.layout-grid.editor.padding.left")}
|
:title (tr "workspace.layout-grid.editor.padding.left")}
|
||||||
@@ -754,16 +757,11 @@
|
|||||||
:icon i/gap-vertical
|
:icon i/gap-vertical
|
||||||
:nillable true
|
:nillable true
|
||||||
:min 0
|
:min 0
|
||||||
:attr :row-gap
|
:name :row-gap
|
||||||
|
:applied-tokens applied-tokens
|
||||||
:property "Row gap"
|
:property "Row gap"
|
||||||
:values {:row-gap (:row-gap value)}
|
:values {:row-gap (:row-gap value)}
|
||||||
:disabled row-gap-disabled?
|
:disabled row-gap-disabled?}]
|
||||||
:placeholder (if (or (= :multiple (:row-gap applied-tokens))
|
|
||||||
(= :multiple (:row-gap value)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token (:row-gap applied-tokens)
|
|
||||||
:value (:row-gap value)}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css-case
|
[:div {:class (stl/css-case
|
||||||
:row-gap true
|
:row-gap true
|
||||||
@@ -793,15 +791,11 @@
|
|||||||
:icon i/gap-horizontal
|
:icon i/gap-horizontal
|
||||||
:nillable true
|
:nillable true
|
||||||
:min 0
|
:min 0
|
||||||
:attr :column-gap
|
:name :column-gap
|
||||||
:align :right
|
:align :right
|
||||||
|
:applied-tokens applied-tokens
|
||||||
:property "Column gap"
|
:property "Column gap"
|
||||||
:placeholder (if (or (= :multiple (:column-gap applied-tokens))
|
:values {:column-gap (:column-gap value)}
|
||||||
(= :multiple (:column-gap value)))
|
|
||||||
(tr "settings.multiple")
|
|
||||||
"--")
|
|
||||||
:applied-token (:column-gap applied-tokens)
|
|
||||||
:value (:column-gap value)
|
|
||||||
:disabled col-gap-disabled?}]
|
:disabled col-gap-disabled?}]
|
||||||
|
|
||||||
[:div {:class (stl/css-case
|
[:div {:class (stl/css-case
|
||||||
|
|||||||
@@ -11,8 +11,6 @@
|
|||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
|
||||||
[app.main.features :as features]
|
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
@@ -21,7 +19,6 @@
|
|||||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
@@ -49,114 +46,40 @@
|
|||||||
(select-margins (= prop :m1) (= prop :m2) (= prop :m3) (= prop :m4)))
|
(select-margins (= prop :m1) (= prop :m2) (= prop :m3) (= prop :m4)))
|
||||||
|
|
||||||
(mf/defc margin-simple*
|
(mf/defc margin-simple*
|
||||||
[{:keys [value on-change on-blur applied-tokens ids]}]
|
[{:keys [value on-change on-blur]}]
|
||||||
(let [token-numeric-inputs
|
(let [m1 (:m1 value)
|
||||||
(features/use-feature "tokens/numeric-input")
|
|
||||||
|
|
||||||
m1 (:m1 value)
|
|
||||||
m2 (:m2 value)
|
m2 (:m2 value)
|
||||||
m3 (:m3 value)
|
m3 (:m3 value)
|
||||||
m4 (:m4 value)
|
m4 (:m4 value)
|
||||||
|
|
||||||
|
m1-placeholder (if (and (not= value :multiple) (not= m1 m3)) (tr "settings.multiple") "--")
|
||||||
|
m2-placeholder (if (and (not= value :multiple) (not= m2 m4)) (tr "settings.multiple") "--")
|
||||||
|
|
||||||
m1 (when (and (not= value :multiple) (= m1 m3)) m1)
|
m1 (when (and (not= value :multiple) (= m1 m3)) m1)
|
||||||
m2 (when (and (not= value :multiple) (= m2 m4)) m2)
|
m2 (when (and (not= value :multiple) (= m2 m4)) m2)
|
||||||
|
|
||||||
token-applied-m1 (:m1 applied-tokens)
|
|
||||||
token-applied-m2 (:m2 applied-tokens)
|
|
||||||
token-applied-m3 (:m3 applied-tokens)
|
|
||||||
token-applied-m4 (:m4 applied-tokens)
|
|
||||||
|
|
||||||
token-applied-m1 (if (and (not= applied-tokens :multiple) (= token-applied-m1 token-applied-m3)) token-applied-m1
|
|
||||||
:multiple)
|
|
||||||
|
|
||||||
token-applied-m2 (if (and (not= applied-tokens :multiple) (= token-applied-m2 token-applied-m4)) token-applied-m2
|
|
||||||
:multiple)
|
|
||||||
|
|
||||||
m1-placeholder (if (and (not= value :multiple)
|
|
||||||
(= m1 m3)
|
|
||||||
(= token-applied-m1 token-applied-m3))
|
|
||||||
"--"
|
|
||||||
(tr "settings.multiple"))
|
|
||||||
|
|
||||||
m2-placeholder (if (and (not= value :multiple)
|
|
||||||
(= m2 m4)
|
|
||||||
(= token-applied-m2 token-applied-m4))
|
|
||||||
"--"
|
|
||||||
(tr "settings.multiple"))
|
|
||||||
|
|
||||||
on-focus
|
on-focus
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [attr event]
|
(fn [event]
|
||||||
|
(let [attr (-> (dom/get-current-target event)
|
||||||
|
(dom/get-data "name")
|
||||||
|
(keyword))]
|
||||||
(case attr
|
(case attr
|
||||||
:m1 (select-margins true false true false)
|
:m1 (select-margins true false true false)
|
||||||
:m2 (select-margins false true false true))
|
:m2 (select-margins false true false true))
|
||||||
(dom/select-target event)))
|
|
||||||
|
|
||||||
on-detach-token
|
(dom/select-target event))))
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps ids)
|
|
||||||
(fn [token attr]
|
|
||||||
(st/emit! (dwta/unapply-token {:token (first token)
|
|
||||||
:attributes #{attr}
|
|
||||||
:shape-ids ids}))))
|
|
||||||
|
|
||||||
on-detach-horizontal
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps on-detach-token)
|
|
||||||
(fn [token]
|
|
||||||
(prn "token" token)
|
|
||||||
(run! #(on-detach-token token %) [:m2 :m4])))
|
|
||||||
|
|
||||||
on-detach-vertical
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps on-detach-token)
|
|
||||||
(fn [token]
|
|
||||||
(run! #(on-detach-token token %) [:m1 :m3])))
|
|
||||||
|
|
||||||
on-change'
|
on-change'
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-change ids)
|
(mf/deps on-change)
|
||||||
(fn [value attr]
|
(fn [value event]
|
||||||
(if (or (string? value) (int? value))
|
(let [attr (-> (dom/get-current-target event)
|
||||||
(on-change :simple attr value)
|
(dom/get-data "name")
|
||||||
(do
|
(keyword))]
|
||||||
(st/emit!
|
(on-change :simple attr value))))]
|
||||||
(dwta/toggle-token {:token (first value)
|
|
||||||
:attrs (if (= :m1 attr)
|
|
||||||
#{:m1 :m3}
|
|
||||||
#{:m2 :m4})
|
|
||||||
:shape-ids ids}))))))
|
|
||||||
|
|
||||||
on-focus-m1
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
|
|
||||||
|
|
||||||
on-focus-m2
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
|
|
||||||
|
|
||||||
on-m1-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m1))
|
|
||||||
|
|
||||||
on-m2-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m2))]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :margin-simple)}
|
[:div {:class (stl/css :margin-simple)}
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m1-change
|
|
||||||
:on-detach on-detach-vertical
|
|
||||||
:class (stl/css :vertical-margin-wrapper)
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m1
|
|
||||||
:placeholder m1-placeholder
|
|
||||||
:icon i/margin-top-bottom
|
|
||||||
:min 0
|
|
||||||
:attr :m1
|
|
||||||
:input-type :vertical-margin
|
|
||||||
:property "Vertical margin "
|
|
||||||
:nillable true
|
|
||||||
:applied-token token-applied-m1
|
|
||||||
:value m1}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :vertical-margin)
|
[:div {:class (stl/css :vertical-margin)
|
||||||
:title "Vertical margin"}
|
:title "Vertical margin"}
|
||||||
[:span {:class (stl/css :icon)}
|
[:span {:class (stl/css :icon)}
|
||||||
@@ -164,29 +87,11 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder m1-placeholder
|
:placeholder m1-placeholder
|
||||||
:data-name "m1"
|
:data-name "m1"
|
||||||
:on-focus on-focus-m1
|
:on-focus on-focus
|
||||||
:on-change on-m1-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m1}]])
|
:value m1}]]
|
||||||
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m2-change
|
|
||||||
:on-detach on-detach-horizontal
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m2
|
|
||||||
:placeholder m2-placeholder
|
|
||||||
:icon i/margin-left-right
|
|
||||||
:class (stl/css :horizontal-margin-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :m2
|
|
||||||
:align :right
|
|
||||||
:input-type :horizontal-margin
|
|
||||||
:property "Horizontal margin"
|
|
||||||
:nillable true
|
|
||||||
:applied-token token-applied-m2
|
|
||||||
:value m2}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :horizontal-margin)
|
[:div {:class (stl/css :horizontal-margin)
|
||||||
:title "Horizontal margin"}
|
:title "Horizontal margin"}
|
||||||
@@ -195,95 +100,38 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder m2-placeholder
|
:placeholder m2-placeholder
|
||||||
:data-name "m2"
|
:data-name "m2"
|
||||||
:on-focus on-focus-m2
|
:on-focus on-focus
|
||||||
:on-change on-m2-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m2}]])]))
|
:value m2}]]]))
|
||||||
|
|
||||||
(mf/defc margin-multiple*
|
(mf/defc margin-multiple*
|
||||||
[{:keys [value on-change on-blur applied-tokens ids]}]
|
[{:keys [value on-change on-blur]}]
|
||||||
(let [token-numeric-inputs
|
(let [m1 (:m1 value)
|
||||||
(features/use-feature "tokens/numeric-input")
|
|
||||||
|
|
||||||
m1 (:m1 value)
|
|
||||||
m2 (:m2 value)
|
m2 (:m2 value)
|
||||||
m3 (:m3 value)
|
m3 (:m3 value)
|
||||||
m4 (:m4 value)
|
m4 (:m4 value)
|
||||||
|
|
||||||
applied-token-to-m1 (:m1 applied-tokens)
|
|
||||||
applied-token-to-m2 (:m2 applied-tokens)
|
|
||||||
applied-token-to-m3 (:m3 applied-tokens)
|
|
||||||
applied-token-to-m4 (:m4 applied-tokens)
|
|
||||||
|
|
||||||
on-detach-token
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps ids)
|
|
||||||
(fn [token attr]
|
|
||||||
(st/emit! (dwta/unapply-token {:token (first token)
|
|
||||||
:attributes #{attr}
|
|
||||||
:shape-ids ids}))))
|
|
||||||
|
|
||||||
on-focus
|
on-focus
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [attr event]
|
(fn [event]
|
||||||
|
(let [attr (-> (dom/get-current-target event)
|
||||||
|
(dom/get-data "name")
|
||||||
|
(keyword))]
|
||||||
(select-margin attr)
|
(select-margin attr)
|
||||||
(dom/select-target event)))
|
(dom/select-target event))))
|
||||||
|
|
||||||
on-focus-m1
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
|
|
||||||
|
|
||||||
on-focus-m2
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
|
|
||||||
|
|
||||||
on-focus-m3
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m1))
|
|
||||||
|
|
||||||
on-focus-m4
|
|
||||||
(mf/use-fn (mf/deps on-focus) #(on-focus :m2))
|
|
||||||
|
|
||||||
on-change'
|
on-change'
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-change ids)
|
(mf/deps on-change)
|
||||||
(fn [value attr]
|
(fn [value event]
|
||||||
(if (or (string? value) (int? value))
|
(let [attr (-> (dom/get-current-target event)
|
||||||
(on-change :multiple attr value)
|
(dom/get-data "name")
|
||||||
(do
|
(keyword))]
|
||||||
(st/emit!
|
(on-change :multiple attr value))))]
|
||||||
(dwta/toggle-token {:token (first value)
|
|
||||||
:attrs #{attr}
|
|
||||||
:shape-ids ids}))))))
|
|
||||||
|
|
||||||
|
|
||||||
on-m1-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m1))
|
|
||||||
|
|
||||||
on-m2-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m2))
|
|
||||||
|
|
||||||
on-m3-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m3))
|
|
||||||
|
|
||||||
on-m4-change
|
|
||||||
(mf/use-fn (mf/deps on-change') #(on-change' % :m4))]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :margin-multiple)}
|
[:div {:class (stl/css :margin-multiple)}
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m1-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m1
|
|
||||||
:icon i/margin-top
|
|
||||||
:class (stl/css :top-margin-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :m1
|
|
||||||
:input-type :vertical-margin
|
|
||||||
:property "Top margin"
|
|
||||||
:nillable true
|
|
||||||
:applied-token applied-token-to-m1
|
|
||||||
:value m1}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :top-margin)
|
[:div {:class (stl/css :top-margin)
|
||||||
:title "Top margin"}
|
:title "Top margin"}
|
||||||
[:span {:class (stl/css :icon)}
|
[:span {:class (stl/css :icon)}
|
||||||
@@ -291,28 +139,11 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:data-name "m1"
|
:data-name "m1"
|
||||||
:on-focus on-focus-m1
|
:on-focus on-focus
|
||||||
:on-change on-m1-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m1}]])
|
:value m1}]]
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m2-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m2
|
|
||||||
:icon i/margin-right
|
|
||||||
:class (stl/css :right-margin-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :m2
|
|
||||||
:align :right
|
|
||||||
:input-type :horizontal-margin
|
|
||||||
:property "Right margin"
|
|
||||||
:nillable true
|
|
||||||
:applied-token applied-token-to-m2
|
|
||||||
:value m2}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :right-margin)
|
[:div {:class (stl/css :right-margin)
|
||||||
:title "Right margin"}
|
:title "Right margin"}
|
||||||
[:span {:class (stl/css :icon)}
|
[:span {:class (stl/css :icon)}
|
||||||
@@ -320,28 +151,11 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:data-name "m2"
|
:data-name "m2"
|
||||||
:on-focus on-focus-m2
|
:on-focus on-focus
|
||||||
:on-change on-m2-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m2}]])
|
:value m2}]]
|
||||||
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m3-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m3
|
|
||||||
:icon i/margin-bottom
|
|
||||||
:class (stl/css :bottom-margin-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :m3
|
|
||||||
:align :right
|
|
||||||
:input-type :vertical-margin
|
|
||||||
:property "Bottom margin"
|
|
||||||
:nillable true
|
|
||||||
:applied-token applied-token-to-m3
|
|
||||||
:value m3}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :bottom-margin)
|
[:div {:class (stl/css :bottom-margin)
|
||||||
:title "Bottom margin"}
|
:title "Bottom margin"}
|
||||||
@@ -350,27 +164,11 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:data-name "m3"
|
:data-name "m3"
|
||||||
:on-focus on-focus-m3
|
:on-focus on-focus
|
||||||
:on-change on-m3-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m3}]])
|
:value m3}]]
|
||||||
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-m4-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:on-blur on-blur
|
|
||||||
:on-focus on-focus-m4
|
|
||||||
:icon i/margin-left
|
|
||||||
:class (stl/css :left-margin-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :m4
|
|
||||||
:property "Left margin"
|
|
||||||
:input-type :horizontal-margin
|
|
||||||
:nillable true
|
|
||||||
:applied-token applied-token-to-m4
|
|
||||||
:value m4}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :left-margin)
|
[:div {:class (stl/css :left-margin)
|
||||||
:title "Left margin"}
|
:title "Left margin"}
|
||||||
@@ -379,15 +177,16 @@
|
|||||||
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
[:> deprecated-input/numeric-input* {:class (stl/css :numeric-input)
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
:data-name "m4"
|
:data-name "m4"
|
||||||
:on-focus on-focus-m4
|
:on-focus on-focus
|
||||||
:on-change on-m4-change
|
:on-change on-change'
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:nillable true
|
:nillable true
|
||||||
:value m4}]])]))
|
:value m4}]]]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc margin-section*
|
(mf/defc margin-section*
|
||||||
{::mf/private true
|
{::mf/private true
|
||||||
::mf/expect-props #{:value :type :on-type-change :on-change :applied-tokens :ids}}
|
::mf/expect-props #{:value :type :on-type-change :on-change}}
|
||||||
[{:keys [type on-type-change] :as props}]
|
[{:keys [type on-type-change] :as props}]
|
||||||
(let [type (d/nilv type :simple)
|
(let [type (d/nilv type :simple)
|
||||||
on-blur (mf/use-fn #(select-margins false false false false))
|
on-blur (mf/use-fn #(select-margins false false false false))
|
||||||
@@ -493,191 +292,8 @@
|
|||||||
:label "Align self end"
|
:label "Align self end"
|
||||||
:value "end"}]}])
|
:value "end"}]}])
|
||||||
|
|
||||||
(mf/defc layout-size-constraints*
|
|
||||||
{::mf/private true
|
|
||||||
::mf/expect-props #{:value :applied-tokens :ids :v-sizing}}
|
|
||||||
[{:keys [values v-sizing ids applied-tokens] :as props}]
|
|
||||||
(let [token-numeric-inputs
|
|
||||||
(features/use-feature "tokens/numeric-input")
|
|
||||||
|
|
||||||
min-w (get values :layout-item-min-w)
|
|
||||||
|
|
||||||
max-w (get values :layout-item-max-w)
|
|
||||||
|
|
||||||
min-h (get values :layout-item-min-h)
|
|
||||||
|
|
||||||
max-h (get values :layout-item-max-h)
|
|
||||||
|
|
||||||
applied-token-to-min-w (get applied-tokens :layout-item-min-w)
|
|
||||||
|
|
||||||
applied-token-to-max-w (get applied-tokens :layout-item-max-w)
|
|
||||||
|
|
||||||
applied-token-to-min-h (get applied-tokens :layout-item-min-h)
|
|
||||||
|
|
||||||
applied-token-to-max-h (get applied-tokens :layout-item-max-h)
|
|
||||||
|
|
||||||
on-detach-token
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps ids)
|
|
||||||
(fn [token attr]
|
|
||||||
(st/emit! (dwta/unapply-token {:token (first token)
|
|
||||||
:attributes #{attr}
|
|
||||||
:shape-ids ids}))))
|
|
||||||
|
|
||||||
on-size-change
|
|
||||||
(mf/use-fn
|
|
||||||
(mf/deps ids)
|
|
||||||
(fn [value attr]
|
|
||||||
(if (or (string? value) (int? value))
|
|
||||||
(st/emit! (dwsl/update-layout-child ids {attr value}))
|
|
||||||
(do
|
|
||||||
(st/emit!
|
|
||||||
(dwta/toggle-token {:token (first value)
|
|
||||||
:attrs #{attr}
|
|
||||||
:shape-ids ids}))))))
|
|
||||||
|
|
||||||
on-layout-item-min-w-change
|
|
||||||
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-w))
|
|
||||||
|
|
||||||
on-layout-item-max-w-change
|
|
||||||
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-w))
|
|
||||||
|
|
||||||
on-layout-item-min-h-change
|
|
||||||
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-min-h))
|
|
||||||
|
|
||||||
on-layout-item-max-h-change
|
|
||||||
(mf/use-fn (mf/deps on-size-change) #(on-size-change % :layout-item-max-h))]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :advanced-options)}
|
|
||||||
(when (= (:layout-item-h-sizing values) :fill)
|
|
||||||
[:div {:class (stl/css :horizontal-fill)}
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-layout-item-min-w-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:class (stl/css :min-w-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :layout-item-min-w
|
|
||||||
:property (tr "workspace.options.layout-item.layout-item-min-w")
|
|
||||||
:text-icon "MIN W"
|
|
||||||
:input-type :min-width
|
|
||||||
:nillable true
|
|
||||||
:applied-token applied-token-to-min-w
|
|
||||||
:tooltip-class (stl/css :tooltip-wrapper)
|
|
||||||
:value min-w}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :layout-item-min-w)
|
|
||||||
:title (tr "workspace.options.layout-item.layout-item-min-w")}
|
|
||||||
|
|
||||||
[:span {:class (stl/css :icon-text)} "MIN W"]
|
|
||||||
[:> deprecated-input/numeric-input*
|
|
||||||
{:class (stl/css :numeric-input)
|
|
||||||
:no-validate true
|
|
||||||
:min 0
|
|
||||||
:data-wrap true
|
|
||||||
:placeholder "--"
|
|
||||||
:data-attr "layout-item-min-w"
|
|
||||||
:on-focus dom/select-target
|
|
||||||
:on-change on-layout-item-min-w-change
|
|
||||||
:value (get values :layout-item-min-w)
|
|
||||||
:nillable true}]])
|
|
||||||
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-layout-item-max-w-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:text-icon "MAX W"
|
|
||||||
:class (stl/css :max-w-wrapper)
|
|
||||||
:min 0
|
|
||||||
:input-type :max-width
|
|
||||||
:attr :layout-item-max-w
|
|
||||||
:property (tr "workspace.options.layout-item.layout-item-max-w")
|
|
||||||
:nillable true
|
|
||||||
:tooltip-class (stl/css :tooltip-wrapper)
|
|
||||||
:applied-token applied-token-to-max-w
|
|
||||||
:value max-w}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :layout-item-max-w)
|
|
||||||
:title (tr "workspace.options.layout-item.layout-item-max-w")}
|
|
||||||
[:span {:class (stl/css :icon-text)} "MAX W"]
|
|
||||||
[:> deprecated-input/numeric-input*
|
|
||||||
{:class (stl/css :numeric-input)
|
|
||||||
:no-validate true
|
|
||||||
:min 0
|
|
||||||
:data-wrap true
|
|
||||||
:placeholder "--"
|
|
||||||
:data-attr "layout-item-max-w"
|
|
||||||
:on-focus dom/select-target
|
|
||||||
:on-change on-layout-item-max-w-change
|
|
||||||
:value (get values :layout-item-max-w)
|
|
||||||
:nillable true}]])])
|
|
||||||
|
|
||||||
(when (= v-sizing :fill)
|
|
||||||
[:div {:class (stl/css :vertical-fill)}
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-layout-item-min-h-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:text-icon "MIN H"
|
|
||||||
:input-type :max-height
|
|
||||||
:class (stl/css :min-h-wrapper)
|
|
||||||
:min 0
|
|
||||||
:attr :layout-item-min-h
|
|
||||||
:property (tr "workspace.options.layout-item.layout-item-min-h")
|
|
||||||
:nillable true
|
|
||||||
:tooltip-class (stl/css :tooltip-wrapper)
|
|
||||||
|
|
||||||
:applied-token applied-token-to-min-h
|
|
||||||
:value min-h}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :layout-item-min-h)
|
|
||||||
:title (tr "workspace.options.layout-item.layout-item-min-h")}
|
|
||||||
[:span {:class (stl/css :icon-text)} "MIN H"]
|
|
||||||
[:> deprecated-input/numeric-input*
|
|
||||||
{:class (stl/css :numeric-input)
|
|
||||||
:no-validate true
|
|
||||||
:min 0
|
|
||||||
:data-wrap true
|
|
||||||
:placeholder "--"
|
|
||||||
:data-attr "layout-item-min-h"
|
|
||||||
:on-focus dom/select-target
|
|
||||||
:on-change on-layout-item-min-h-change
|
|
||||||
:value (get values :layout-item-min-h)
|
|
||||||
:nillable true}]])
|
|
||||||
|
|
||||||
(if token-numeric-inputs
|
|
||||||
[:> numeric-input-wrapper*
|
|
||||||
{:on-change on-layout-item-max-h-change
|
|
||||||
:on-detach on-detach-token
|
|
||||||
:class (stl/css :max-h-wrapper)
|
|
||||||
:min 0
|
|
||||||
:text-icon "MAX H"
|
|
||||||
:input-type :max-height
|
|
||||||
:attr :layout-item-max-h
|
|
||||||
:property (tr "workspace.options.layout-item.layout-item-max-h")
|
|
||||||
:nillable true
|
|
||||||
:tooltip-class (stl/css :tooltip-wrapper)
|
|
||||||
:applied-token applied-token-to-max-h
|
|
||||||
:value max-h}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :layout-item-max-h)
|
|
||||||
:title (tr "workspace.options.layout-item.layout-item-max-h")}
|
|
||||||
|
|
||||||
[:span {:class (stl/css :icon-text)} "MAX H"]
|
|
||||||
[:> deprecated-input/numeric-input*
|
|
||||||
{:class (stl/css :numeric-input)
|
|
||||||
:no-validate true
|
|
||||||
:min 0
|
|
||||||
:data-wrap true
|
|
||||||
:placeholder "--"
|
|
||||||
:data-attr "layout-item-max-h"
|
|
||||||
:on-focus dom/select-target
|
|
||||||
:on-change on-layout-item-max-h-change
|
|
||||||
:value (get values :layout-item-max-h)
|
|
||||||
:nillable true}]])])]))
|
|
||||||
|
|
||||||
(mf/defc layout-item-menu
|
(mf/defc layout-item-menu
|
||||||
{::mf/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent? :is-grid-layout? :is-flex-layout? :applied-tokens}
|
{::mf/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent? :is-grid-layout? :is-flex-layout?}
|
||||||
::mf/props :obj}
|
::mf/props :obj}
|
||||||
[{:keys [ids values
|
[{:keys [ids values
|
||||||
^boolean is-layout-child?
|
^boolean is-layout-child?
|
||||||
@@ -685,8 +301,7 @@
|
|||||||
^boolean is-grid-parent?
|
^boolean is-grid-parent?
|
||||||
^boolean is-flex-parent?
|
^boolean is-flex-parent?
|
||||||
^boolean is-flex-layout?
|
^boolean is-flex-layout?
|
||||||
^boolean is-grid-layout?
|
^boolean is-grid-layout?]}]
|
||||||
applied-tokens]}]
|
|
||||||
|
|
||||||
(let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
(let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
selection-parents (mf/deref selection-parents*)
|
selection-parents (mf/deref selection-parents*)
|
||||||
@@ -782,7 +397,16 @@
|
|||||||
(fn [value]
|
(fn [value]
|
||||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing (keyword value)}))))
|
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing (keyword value)}))))
|
||||||
|
|
||||||
;; Position
|
;; Size and position
|
||||||
|
on-size-change
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps ids)
|
||||||
|
(fn [value event]
|
||||||
|
(let [attr (-> (dom/get-current-target event)
|
||||||
|
(dom/get-data "attr")
|
||||||
|
(keyword))]
|
||||||
|
(st/emit! (dwsl/update-layout-child ids {attr value})))))
|
||||||
|
|
||||||
on-change-position
|
on-change-position
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
@@ -799,8 +423,7 @@
|
|||||||
(fn [value]
|
(fn [value]
|
||||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-z-index value}))))]
|
(st/emit! (dwsl/update-layout-child ids {:layout-item-z-index value}))))]
|
||||||
|
|
||||||
[:section {:class (stl/css :element-set)
|
[:div {:class (stl/css :element-set)}
|
||||||
:aria-label "layout item menu"}
|
|
||||||
[:div {:class (stl/css :element-title)}
|
[:div {:class (stl/css :element-title)}
|
||||||
[:> title-bar* {:collapsable has-content?
|
[:> title-bar* {:collapsable has-content?
|
||||||
:collapsed (not open?)
|
:collapsed (not open?)
|
||||||
@@ -860,13 +483,74 @@
|
|||||||
[:> margin-section* {:value (:layout-item-margin values)
|
[:> margin-section* {:value (:layout-item-margin values)
|
||||||
:type (:layout-item-margin-type values)
|
:type (:layout-item-margin-type values)
|
||||||
:on-type-change on-margin-type-change
|
:on-type-change on-margin-type-change
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:ids ids
|
|
||||||
:on-change on-margin-change}])
|
:on-change on-margin-change}])
|
||||||
|
|
||||||
(when (or (= h-sizing :fill)
|
(when (or (= h-sizing :fill)
|
||||||
(= v-sizing :fill))
|
(= v-sizing :fill))
|
||||||
[:> layout-size-constraints* {:ids ids
|
[:div {:class (stl/css :advanced-options)}
|
||||||
:values values
|
(when (= (:layout-item-h-sizing values) :fill)
|
||||||
:applied-tokens applied-tokens
|
[:div {:class (stl/css :horizontal-fill)}
|
||||||
:v-sizing v-sizing}])])]))
|
[:div {:class (stl/css :layout-item-min-w)
|
||||||
|
:title (tr "workspace.options.layout-item.layout-item-min-w")}
|
||||||
|
|
||||||
|
[:span {:class (stl/css :icon-text)} "MIN W"]
|
||||||
|
[:> deprecated-input/numeric-input*
|
||||||
|
{:class (stl/css :numeric-input)
|
||||||
|
:no-validate true
|
||||||
|
:min 0
|
||||||
|
:data-wrap true
|
||||||
|
:placeholder "--"
|
||||||
|
:data-attr "layout-item-min-w"
|
||||||
|
:on-focus dom/select-target
|
||||||
|
:on-change on-size-change
|
||||||
|
:value (get values :layout-item-min-w)
|
||||||
|
:nillable true}]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :layout-item-max-w)
|
||||||
|
:title (tr "workspace.options.layout-item.layout-item-max-w")}
|
||||||
|
[:span {:class (stl/css :icon-text)} "MAX W"]
|
||||||
|
[:> deprecated-input/numeric-input*
|
||||||
|
{:class (stl/css :numeric-input)
|
||||||
|
:no-validate true
|
||||||
|
:min 0
|
||||||
|
:data-wrap true
|
||||||
|
:placeholder "--"
|
||||||
|
:data-attr "layout-item-max-w"
|
||||||
|
:on-focus dom/select-target
|
||||||
|
:on-change on-size-change
|
||||||
|
:value (get values :layout-item-max-w)
|
||||||
|
:nillable true}]]])
|
||||||
|
|
||||||
|
(when (= v-sizing :fill)
|
||||||
|
[:div {:class (stl/css :vertical-fill)}
|
||||||
|
[:div {:class (stl/css :layout-item-min-h)
|
||||||
|
:title (tr "workspace.options.layout-item.layout-item-min-h")}
|
||||||
|
|
||||||
|
[:span {:class (stl/css :icon-text)} "MIN H"]
|
||||||
|
[:> deprecated-input/numeric-input*
|
||||||
|
{:class (stl/css :numeric-input)
|
||||||
|
:no-validate true
|
||||||
|
:min 0
|
||||||
|
:data-wrap true
|
||||||
|
:placeholder "--"
|
||||||
|
:data-attr "layout-item-min-h"
|
||||||
|
:on-focus dom/select-target
|
||||||
|
:on-change on-size-change
|
||||||
|
:value (get values :layout-item-min-h)
|
||||||
|
:nillable true}]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :layout-item-max-h)
|
||||||
|
:title (tr "workspace.options.layout-item.layout-item-max-h")}
|
||||||
|
|
||||||
|
[:span {:class (stl/css :icon-text)} "MAX H"]
|
||||||
|
[:> deprecated-input/numeric-input*
|
||||||
|
{:class (stl/css :numeric-input)
|
||||||
|
:no-validate true
|
||||||
|
:min 0
|
||||||
|
:data-wrap true
|
||||||
|
:placeholder "--"
|
||||||
|
:data-attr "layout-item-max-h"
|
||||||
|
:on-focus dom/select-target
|
||||||
|
:on-change on-size-change
|
||||||
|
:value (get values :layout-item-max-h)
|
||||||
|
:nillable true}]]])])])]))
|
||||||
|
|||||||
@@ -6,21 +6,17 @@
|
|||||||
|
|
||||||
@use "refactor/common-refactor.scss" as deprecated;
|
@use "refactor/common-refactor.scss" as deprecated;
|
||||||
@use "../../../sidebar/common/sidebar.scss" as sidebar;
|
@use "../../../sidebar/common/sidebar.scss" as sidebar;
|
||||||
@use "ds/_borders.scss" as *;
|
|
||||||
@use "ds/_sizes.scss" as *;
|
|
||||||
@use "ds/typography.scss" as *;
|
|
||||||
@use "ds/_utils.scss" as *;
|
|
||||||
|
|
||||||
.element-set {
|
.element-set {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-spacing-layout-element {
|
.title-spacing-layout-element {
|
||||||
margin: 0 0 var(--sp-xs) 0;
|
margin: 0 0 deprecated.$s-4 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-spacing-empty {
|
.title-spacing-empty {
|
||||||
padding-inline-start: var(--sp-xxs);
|
padding-left: deprecated.$s-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-element-menu {
|
.flex-element-menu {
|
||||||
@@ -39,8 +35,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.z-index-wrapper {
|
.z-index-wrapper {
|
||||||
@include use-typography("body-small");
|
|
||||||
@extend .input-element;
|
@extend .input-element;
|
||||||
|
@include deprecated.bodySmallTypography;
|
||||||
grid-column: 6 / span 3;
|
grid-column: 6 / span 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.position-options {
|
.position-options {
|
||||||
inline-size: 100%;
|
width: 100%;
|
||||||
grid-column: 1 / span 5;
|
grid-column: 1 / span 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +75,7 @@
|
|||||||
.vertical-margin,
|
.vertical-margin,
|
||||||
.horizontal-margin {
|
.horizontal-margin {
|
||||||
@extend .input-element;
|
@extend .input-element;
|
||||||
@include use-typography("body-small");
|
@include deprecated.bodySmallTypography;
|
||||||
}
|
}
|
||||||
.vertical-margin {
|
.vertical-margin {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
@@ -89,16 +85,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical-margin-wrapper {
|
|
||||||
grid-column: 1;
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizontal-margin-wrapper {
|
|
||||||
grid-column: 2;
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
}
|
|
||||||
|
|
||||||
.margin-multiple {
|
.margin-multiple {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: subgrid;
|
grid-template-columns: subgrid;
|
||||||
@@ -110,33 +96,25 @@
|
|||||||
.left-margin,
|
.left-margin,
|
||||||
.right-margin {
|
.right-margin {
|
||||||
@extend .input-element;
|
@extend .input-element;
|
||||||
@include use-typography("body-small");
|
@include deprecated.bodySmallTypography;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-margin,
|
.top-margin {
|
||||||
.top-margin-wrapper {
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-margin,
|
.bottom-margin {
|
||||||
.bottom-margin-wrapper {
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-margin,
|
.left-margin {
|
||||||
.left-margin-wrapper {
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-margin,
|
.right-margin {
|
||||||
.right-margin-wrapper {
|
|
||||||
--dropdown-width: var(--7-columns-dropdown-width);
|
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
}
|
}
|
||||||
@@ -162,18 +140,14 @@
|
|||||||
.layout-item-max-w,
|
.layout-item-max-w,
|
||||||
.layout-item-max-h {
|
.layout-item-max-h {
|
||||||
@extend .input-element;
|
@extend .input-element;
|
||||||
@include use-typography("body-small");
|
@include deprecated.bodySmallTypography;
|
||||||
.icon-text {
|
.icon-text {
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
inline-size: px2rem(80);
|
width: deprecated.$s-80;
|
||||||
padding-block-start: var(--sp-xxs);
|
padding-top: deprecated.$s-2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.inputs-wrapper {
|
.inputs-wrapper {
|
||||||
grid-column: 1 / span 2;
|
grid-column: 1 / span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-wrapper {
|
|
||||||
inline-size: 100%;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.logic.shapes :as cls]
|
[app.common.logic.shapes :as cls]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.common.types.token :as tk]
|
||||||
[app.main.constants :refer [size-presets]]
|
[app.main.constants :refer [size-presets]]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.interactions :as dwi]
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
@@ -25,12 +26,13 @@
|
|||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.border-radius :refer [border-radius-menu*]]
|
[app.main.ui.workspace.sidebar.options.menus.border-radius :refer [border-radius-menu*]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
@@ -89,6 +91,32 @@
|
|||||||
shape)]
|
shape)]
|
||||||
(select-keys shape measure-attrs)))
|
(select-keys shape measure-attrs)))
|
||||||
|
|
||||||
|
(mf/defc numeric-input-wrapper*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [values name applied-tokens align on-detach] :rest props}]
|
||||||
|
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
||||||
|
tokens (mf/with-memo [tokens name]
|
||||||
|
(delay
|
||||||
|
(-> (deref tokens)
|
||||||
|
(select-keys (get tk/tokens-by-input name))
|
||||||
|
(not-empty))))
|
||||||
|
on-detach-attr
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps on-detach name)
|
||||||
|
#(on-detach % name))
|
||||||
|
|
||||||
|
props (mf/spread-props props
|
||||||
|
{:placeholder (if (or (= :multiple (:applied-tokens values))
|
||||||
|
(= :multiple (get values name)))
|
||||||
|
(tr "settings.multiple") "--")
|
||||||
|
:class (stl/css :numeric-input-measures)
|
||||||
|
:applied-token (get applied-tokens name)
|
||||||
|
:tokens (if (delay? tokens) @tokens tokens)
|
||||||
|
:align align
|
||||||
|
:on-detach on-detach-attr
|
||||||
|
:value (get values name)})]
|
||||||
|
[:> numeric-input* props]))
|
||||||
|
|
||||||
(def ^:private xf:map-type (map :type))
|
(def ^:private xf:map-type (map :type))
|
||||||
(def ^:private xf:mapcat-type-to-options (mapcat type->options))
|
(def ^:private xf:mapcat-type-to-options (mapcat type->options))
|
||||||
|
|
||||||
@@ -416,13 +444,10 @@
|
|||||||
:on-detach on-detach-token
|
:on-detach on-detach-token
|
||||||
:icon i/character-w
|
:icon i/character-w
|
||||||
:min 0.01
|
:min 0.01
|
||||||
:attr :width
|
:name :width
|
||||||
:property (tr "workspace.options.width")
|
:property (tr "workspace.options.width")
|
||||||
:applied-token (get applied-tokens :width)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (if (or (= :multiple (get applied-tokens :width))
|
:values values}]
|
||||||
(= :multiple (get values :width)))
|
|
||||||
(tr "settings.multiple") "--")
|
|
||||||
:value (get values :width)}]
|
|
||||||
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:disabled disabled-height-sizing?
|
{:disabled disabled-height-sizing?
|
||||||
@@ -430,11 +455,11 @@
|
|||||||
:on-detach on-detach-token
|
:on-detach on-detach-token
|
||||||
:min 0.01
|
:min 0.01
|
||||||
:icon i/character-h
|
:icon i/character-h
|
||||||
:attr :height
|
:name :height
|
||||||
:align :right
|
:align :right
|
||||||
:property (tr "workspace.options.height")
|
:property (tr "workspace.options.height")
|
||||||
:applied-token (get applied-tokens :height)
|
:applied-tokens applied-tokens
|
||||||
:value (get values :height)}]]
|
:values values}]]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:div {:class (stl/css-case :width true
|
[:div {:class (stl/css-case :width true
|
||||||
@@ -478,26 +503,20 @@
|
|||||||
:on-change on-pos-x-change
|
:on-change on-pos-x-change
|
||||||
:on-detach on-detach-token
|
:on-detach on-detach-token
|
||||||
:icon i/character-x
|
:icon i/character-x
|
||||||
:attr :x
|
:name :x
|
||||||
:property (tr "workspace.options.x")
|
:property (tr "workspace.options.x")
|
||||||
:applied-token (get applied-tokens :x)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (if (or (= :multiple (get applied-tokens :x))
|
:values values}]
|
||||||
(= :multiple (get values :x)))
|
|
||||||
(tr "settings.multiple") "--")
|
|
||||||
:value (get values :x)}]
|
|
||||||
[:> numeric-input-wrapper*
|
[:> numeric-input-wrapper*
|
||||||
{:disabled disabled-position?
|
{:disabled disabled-position?
|
||||||
:on-change on-pos-y-change
|
:on-change on-pos-y-change
|
||||||
:on-detach on-detach-token
|
:on-detach on-detach-token
|
||||||
:icon i/character-y
|
:icon i/character-y
|
||||||
:attr :y
|
:name :y
|
||||||
:align :right
|
:align :right
|
||||||
:property (tr "workspace.options.y")
|
:property (tr "workspace.options.y")
|
||||||
:applied-token (get applied-tokens :y)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (if (or (= :multiple (get applied-tokens :y))
|
:values values}]]
|
||||||
(= :multiple (get values :y)))
|
|
||||||
(tr "settings.multiple") "--")
|
|
||||||
:value (get values :y)}]]
|
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:div {:class (stl/css-case :x-position true
|
[:div {:class (stl/css-case :x-position true
|
||||||
@@ -532,13 +551,10 @@
|
|||||||
:icon i/rotation
|
:icon i/rotation
|
||||||
:min -359
|
:min -359
|
||||||
:max 359
|
:max 359
|
||||||
:attr :rotation
|
:name :rotation
|
||||||
:property (tr "workspace.options.rotation")
|
:property (tr "workspace.options.rotation")
|
||||||
:applied-token (get applied-tokens :rotation)
|
:applied-tokens applied-tokens
|
||||||
:placeholder (if (or (= :multiple (get applied-tokens :rotation))
|
:values values}]
|
||||||
(= :multiple (get values :rotation)))
|
|
||||||
(tr "settings.multiple") "--")
|
|
||||||
:value (get values :rotation)}]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :rotation)
|
[:div {:class (stl/css :rotation)
|
||||||
:title (tr "workspace.options.rotation")}
|
:title (tr "workspace.options.rotation")}
|
||||||
|
|||||||
@@ -346,17 +346,19 @@
|
|||||||
{:value (:id variant)
|
{:value (:id variant)
|
||||||
:key (pr-str variant)
|
:key (pr-str variant)
|
||||||
:label (:name variant)})))
|
:label (:name variant)})))
|
||||||
variant-options (if (= font-variant-id :multiple)
|
variant-options (if (or (= font-variant-id :multiple) (= font-variant-id "mixed"))
|
||||||
(conj basic-variant-options
|
(conj basic-variant-options
|
||||||
{:value ""
|
{:value ""
|
||||||
:key :multiple-variants
|
:key :multiple-variants
|
||||||
:label "--"})
|
:label "--"})
|
||||||
basic-variant-options)]
|
basic-variant-options)
|
||||||
|
font-variant-value (attr->string font-variant-id)
|
||||||
|
font-variant-value (if (= font-variant-value "mixed") "" font-variant-value)]
|
||||||
|
|
||||||
;; TODO Add disabled mode
|
;; TODO Add disabled mode
|
||||||
[:& select
|
[:& select
|
||||||
{:class (stl/css :font-variant-select)
|
{:class (stl/css :font-variant-select)
|
||||||
:default-value (attr->string font-variant-id)
|
:default-value font-variant-value
|
||||||
:options variant-options
|
:options variant-options
|
||||||
:on-change on-font-variant-change
|
:on-change on-font-variant-change
|
||||||
:on-blur on-blur}])]]]))
|
:on-blur on-blur}])]]]))
|
||||||
|
|||||||
@@ -9,20 +9,50 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
|
[app.common.types.token :as tk]
|
||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||||
[app.main.ui.components.reorder-handler :refer [reorder-handler*]]
|
[app.main.ui.components.reorder-handler :refer [reorder-handler*]]
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.controls.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.hooks :as h]
|
[app.main.ui.hooks :as h]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.input-wrapper-tokens :refer [numeric-input-wrapper*]]
|
|
||||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]]
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc numeric-input-wrapper*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [values name applied-tokens align on-detach] :rest props}]
|
||||||
|
(let [tokens (mf/use-ctx muc/active-tokens-by-type)
|
||||||
|
tokens (mf/with-memo [tokens name]
|
||||||
|
(delay
|
||||||
|
(-> (deref tokens)
|
||||||
|
(select-keys (get tk/tokens-by-input name))
|
||||||
|
(not-empty))))
|
||||||
|
|
||||||
|
on-detach-attr (mf/use-fn
|
||||||
|
(mf/deps on-detach name)
|
||||||
|
#(on-detach % name))
|
||||||
|
|
||||||
|
applied-token (get applied-tokens name)
|
||||||
|
|
||||||
|
props (mf/spread-props props
|
||||||
|
{:placeholder (if (= :multiple values)
|
||||||
|
(tr "settings.multiple")
|
||||||
|
"--")
|
||||||
|
:applied-token applied-token
|
||||||
|
:tokens (if (delay? tokens) @tokens tokens)
|
||||||
|
:align align
|
||||||
|
:on-detach on-detach-attr
|
||||||
|
:name name
|
||||||
|
:value values})]
|
||||||
|
[:> numeric-input* props]))
|
||||||
|
|
||||||
(mf/defc stroke-row*
|
(mf/defc stroke-row*
|
||||||
[{:keys [index
|
[{:keys [index
|
||||||
stroke
|
stroke
|
||||||
@@ -220,11 +250,11 @@
|
|||||||
:min 0
|
:min 0
|
||||||
:on-focus on-focus
|
:on-focus on-focus
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:attr :stroke-width
|
:name :stroke-width
|
||||||
:class (stl/css :numeric-input-wrapper)
|
:class (stl/css :numeric-input-wrapper)
|
||||||
:property (tr "workspace.options.stroke-width")
|
:property (tr "workspace.options.stroke-width")
|
||||||
:applied-token (get applied-tokens :stroke-width)
|
:applied-tokens applied-tokens
|
||||||
:value stroke-width}]
|
:values stroke-width}]
|
||||||
|
|
||||||
[:div {:class (stl/css :stroke-width-input)
|
[:div {:class (stl/css :stroke-width-input)
|
||||||
:title (tr "workspace.options.stroke-width")}
|
:title (tr "workspace.options.stroke-width")}
|
||||||
|
|||||||
@@ -114,7 +114,6 @@
|
|||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -113,7 +113,6 @@
|
|||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -135,7 +135,6 @@
|
|||||||
:is-flex-layout? is-flex-layout?
|
:is-flex-layout? is-flex-layout?
|
||||||
:is-grid-layout? is-grid-layout?
|
:is-grid-layout? is-grid-layout?
|
||||||
:is-layout-child? is-layout-child?
|
:is-layout-child? is-layout-child?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:is-layout-container? is-layout-container?
|
:is-layout-container? is-layout-container?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,6 @@
|
|||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:values layout-item-values}])
|
:values layout-item-values}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -409,7 +409,7 @@
|
|||||||
[layout-container-ids layout-container-values layout-container-tokens]
|
[layout-container-ids layout-container-values layout-container-tokens]
|
||||||
(get-attrs shapes objects :layout-container)
|
(get-attrs shapes objects :layout-container)
|
||||||
|
|
||||||
[layout-item-ids layout-item-values layout-item-tokens]
|
[layout-item-ids layout-item-values {}]
|
||||||
(get-attrs shapes objects :layout-item)
|
(get-attrs shapes objects :layout-item)
|
||||||
|
|
||||||
components
|
components
|
||||||
@@ -471,7 +471,6 @@
|
|||||||
:is-layout-container? all-flex-layout-container?
|
:is-layout-container? all-flex-layout-container?
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens layout-item-tokens
|
|
||||||
:values layout-item-values}])
|
:values layout-item-values}])
|
||||||
|
|
||||||
(when-not (or (empty? constraint-ids) ^boolean is-layout-child?)
|
(when-not (or (empty? constraint-ids) ^boolean is-layout-child?)
|
||||||
|
|||||||
@@ -113,7 +113,6 @@
|
|||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -112,7 +112,6 @@
|
|||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,6 @@
|
|||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -154,7 +154,6 @@
|
|||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-flex-parent? is-flex-parent?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-grid-parent? is-grid-parent?
|
:is-grid-parent? is-grid-parent?
|
||||||
:applied-tokens applied-tokens
|
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
(when (or (not ^boolean is-layout-child?) ^boolean is-layout-child-absolute?)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
[{:keys [tokens-lib selected-token-set-id]}]
|
[{:keys [tokens-lib selected-token-set-id]}]
|
||||||
(let [selected-token-set
|
(let [selected-token-set
|
||||||
(mf/with-memo [tokens-lib]
|
(mf/with-memo [tokens-lib selected-token-set-id]
|
||||||
(when selected-token-set-id
|
(when selected-token-set-id
|
||||||
(some-> tokens-lib (ctob/get-set selected-token-set-id))))
|
(some-> tokens-lib (ctob/get-set selected-token-set-id))))
|
||||||
|
|
||||||
@@ -62,18 +62,20 @@
|
|||||||
[:div {:class (stl/css :sets-header-container)}
|
[:div {:class (stl/css :sets-header-container)}
|
||||||
[:> text* {:as "span"
|
[:> text* {:as "span"
|
||||||
:typography "headline-small"
|
:typography "headline-small"
|
||||||
:class (stl/css :sets-header)}
|
:class (stl/css :sets-header)
|
||||||
|
:data-testid "active-token-set-title"}
|
||||||
(tr "workspace.tokens.tokens-section-title" (ctob/get-name selected-token-set))]
|
(tr "workspace.tokens.tokens-section-title" (ctob/get-name selected-token-set))]
|
||||||
|
(when (and (some? selected-token-set-id)
|
||||||
|
(not (token-set-active? (ctob/get-name selected-token-set))))
|
||||||
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
|
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
|
||||||
;; NOTE: when no set in tokens-lib, the selected-token-set-id
|
;; NOTE: when no set in tokens-lib, the selected-token-set-id
|
||||||
;; will be `nil`, so for properly hide the inactive message we
|
;; will be `nil`, so for properly hide the inactive message we
|
||||||
;; check that at least `selected-token-set-id` has a value
|
;; check that at least `selected-token-set-id` has a value
|
||||||
(when (and (some? selected-token-set-id)
|
|
||||||
(not (token-set-active? (ctob/get-name selected-token-set))))
|
|
||||||
[:*
|
[:*
|
||||||
[:> icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}]
|
[:> icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}]
|
||||||
[:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)}
|
[:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)}
|
||||||
(tr "workspace.tokens.inactive-set")]])]]))
|
(tr "workspace.tokens.inactive-set")]]])]))
|
||||||
|
|
||||||
(mf/defc tokens-section*
|
(mf/defc tokens-section*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
@@ -158,7 +160,7 @@
|
|||||||
[:& token-context-menu]
|
[:& token-context-menu]
|
||||||
[:> token-node-context-menu* {:on-delete-node delete-node}]
|
[:> token-node-context-menu* {:on-delete-node delete-node}]
|
||||||
|
|
||||||
[:& selected-set-info* {:tokens-lib tokens-lib
|
[:> selected-set-info* {:tokens-lib tokens-lib
|
||||||
:selected-token-set-id selected-token-set-id}]
|
:selected-token-set-id selected-token-set-id}]
|
||||||
|
|
||||||
(for [type filled-group]
|
(for [type filled-group]
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
[app.main.ui.forms :as fc]
|
[app.main.ui.forms :as fc]
|
||||||
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
|
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
|
||||||
[app.main.ui.workspace.tokens.management.forms.validators :refer [default-validate-token]]
|
[app.main.ui.workspace.tokens.management.forms.validators :refer [default-validate-token]]
|
||||||
[app.main.ui.workspace.tokens.remapping-modal :as remapping-modal]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.forms :as fm]
|
[app.util.forms :as fm]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
@@ -182,9 +181,33 @@
|
|||||||
(when (or (k/enter? e) (k/space? e))
|
(when (or (k/enter? e) (k/space? e))
|
||||||
(on-cancel e))))
|
(on-cancel e))))
|
||||||
|
|
||||||
|
on-remap-token
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps token)
|
||||||
|
(fn [valid-token name old-name description]
|
||||||
|
(st/emit!
|
||||||
|
(dwtl/update-token (:id token)
|
||||||
|
{:name name
|
||||||
|
:value (:value valid-token)
|
||||||
|
:description description})
|
||||||
|
(remap/remap-tokens old-name name)
|
||||||
|
(dwtp/propagate-workspace-tokens)
|
||||||
|
(modal/hide!))))
|
||||||
|
|
||||||
|
on-rename-token
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps token)
|
||||||
|
(fn [valid-token name description]
|
||||||
|
(st/emit!
|
||||||
|
(dwtl/update-token (:id token)
|
||||||
|
{:name name
|
||||||
|
:value (:value valid-token)
|
||||||
|
:description description})
|
||||||
|
(modal/hide!))))
|
||||||
|
|
||||||
on-submit
|
on-submit
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps validate-token token tokens token-type value-subfield type active-tab)
|
(mf/deps validate-token token tokens token-type value-subfield type active-tab on-remap-token on-rename-token is-create)
|
||||||
(fn [form _event]
|
(fn [form _event]
|
||||||
(let [name (get-in @form [:clean-data :name])
|
(let [name (get-in @form [:clean-data :name])
|
||||||
path (str (d/name token-type) "." name)
|
path (str (d/name token-type) "." name)
|
||||||
@@ -202,22 +225,15 @@
|
|||||||
file-data (dh/lookup-file-data state)
|
file-data (dh/lookup-file-data state)
|
||||||
old-name (:name token)
|
old-name (:name token)
|
||||||
is-rename (and (= action "edit") (not= name old-name))
|
is-rename (and (= action "edit") (not= name old-name))
|
||||||
references-count (remap/count-token-references file-data old-name)]
|
references-count (remap/count-token-references file-data old-name)
|
||||||
|
on-remap #(on-remap-token valid-token name old-name description)
|
||||||
|
on-rename #(on-rename-token valid-token name description)]
|
||||||
(if (and is-rename (> references-count 0))
|
(if (and is-rename (> references-count 0))
|
||||||
(remapping-modal/show-remapping-modal
|
(st/emit! (modal/show :tokens/remapping-confirmation {:old-token-name old-name
|
||||||
{:old-token-name old-name
|
|
||||||
:new-token-name name
|
:new-token-name name
|
||||||
:references-count references-count
|
:references-count references-count
|
||||||
:on-confirm (fn []
|
:on-remap on-remap
|
||||||
(st/emit!
|
:on-rename on-rename}))
|
||||||
(dwtl/update-token (:id token)
|
|
||||||
{:name name
|
|
||||||
:value (:value valid-token)
|
|
||||||
:description description})
|
|
||||||
(remap/remap-tokens old-name name)
|
|
||||||
(dwtp/propagate-workspace-tokens)
|
|
||||||
(modal/hide!)))
|
|
||||||
:on-cancel #(modal/hide!)})
|
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(if is-create
|
(if is-create
|
||||||
(dwtl/create-token (ctob/make-token {:name name
|
(dwtl/create-token (ctob/make-token {:name name
|
||||||
|
|||||||
@@ -295,7 +295,8 @@
|
|||||||
errors?
|
errors?
|
||||||
[:> icon*
|
[:> icon*
|
||||||
{:icon-id i/broken-link
|
{:icon-id i/broken-link
|
||||||
:class (stl/css :token-pill-icon)}]
|
:class (stl/css :token-pill-icon)
|
||||||
|
:aria-label (tr "workspace.tokens.missing-reference")}]
|
||||||
|
|
||||||
color
|
color
|
||||||
[:> swatch* {:background color
|
[:> swatch* {:background color
|
||||||
|
|||||||
@@ -11,22 +11,15 @@
|
|||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||||
|
[app.main.ui.ds.foundations.typography :as t]
|
||||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.keyboard :as kbd]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn show-remapping-modal
|
|
||||||
"Show the token remapping confirmation modal"
|
|
||||||
[{:keys [old-token-name new-token-name references-count on-confirm on-cancel]}]
|
|
||||||
(let [props {:old-token-name old-token-name
|
|
||||||
:new-token-name new-token-name
|
|
||||||
:references-count references-count
|
|
||||||
:on-confirm on-confirm
|
|
||||||
:on-cancel on-cancel}]
|
|
||||||
(st/emit! (modal/show :tokens/remapping-confirmation props))))
|
|
||||||
|
|
||||||
(defn hide-remapping-modal
|
(defn hide-remapping-modal
|
||||||
"Hide the token remapping confirmation modal"
|
"Hide the token remapping confirmation modal"
|
||||||
[]
|
[]
|
||||||
@@ -34,73 +27,73 @@
|
|||||||
|
|
||||||
;; Remapping Modal Component
|
;; Remapping Modal Component
|
||||||
(mf/defc token-remapping-modal
|
(mf/defc token-remapping-modal
|
||||||
{::mf/wrap-props false
|
{::mf/register modal/components
|
||||||
::mf/register modal/components
|
|
||||||
::mf/register-as :tokens/remapping-confirmation}
|
::mf/register-as :tokens/remapping-confirmation}
|
||||||
[{:keys [old-token-name new-token-name references-count on-confirm on-cancel]}]
|
[{:keys [old-token-name new-token-name on-remap on-rename]}]
|
||||||
(let [remapping-in-progress* (mf/use-state false)
|
(let [remap-modal (get @st/state :remap-modal)
|
||||||
remapping-in-progress? (deref remapping-in-progress*)
|
|
||||||
|
|
||||||
;; Remap logic on confirm
|
;; Remap logic on confirm
|
||||||
on-confirm-remap
|
confirm-remap
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-confirm remapping-in-progress*)
|
(mf/deps on-remap remap-modal)
|
||||||
(fn [e]
|
(fn []
|
||||||
(dom/prevent-default e)
|
|
||||||
(dom/stop-propagation e)
|
|
||||||
(reset! remapping-in-progress* true)
|
|
||||||
;; Call shared remapping logic
|
;; Call shared remapping logic
|
||||||
(let [state @st/state
|
(let [old-token-name (:old-token-name remap-modal)
|
||||||
remap-modal (:remap-modal state)
|
|
||||||
old-token-name (:old-token-name remap-modal)
|
|
||||||
new-token-name (:new-token-name remap-modal)]
|
new-token-name (:new-token-name remap-modal)]
|
||||||
(st/emit! [:tokens/remap-tokens old-token-name new-token-name]))
|
(st/emit! [:tokens/remap-tokens old-token-name new-token-name]))
|
||||||
(when (fn? on-confirm)
|
(when (fn? on-remap)
|
||||||
(on-confirm))))
|
(on-remap))))
|
||||||
|
|
||||||
on-cancel-remap
|
rename-token
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-cancel)
|
(mf/deps on-rename)
|
||||||
(fn [e]
|
(fn []
|
||||||
(dom/prevent-default e)
|
(when (fn? on-rename)
|
||||||
(dom/stop-propagation e)
|
(on-rename))))
|
||||||
(modal/hide!)
|
|
||||||
(when (fn? on-cancel)
|
cancel-action
|
||||||
(on-cancel))))]
|
(mf/use-fn
|
||||||
|
(fn []
|
||||||
|
(hide-remapping-modal)))
|
||||||
|
|
||||||
|
;; Close modal on Escape key if not in progress
|
||||||
|
on-key-down
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps cancel-action)
|
||||||
|
(fn [event]
|
||||||
|
(when (kbd/enter? event)
|
||||||
|
(cancel-action))))]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :modal-overlay)
|
||||||
|
:on-key-down on-key-down
|
||||||
|
:role "alertdialog"
|
||||||
|
:aria-modal "true"
|
||||||
|
:aria-labelledby "modal-title"}
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-overlay)}
|
|
||||||
[:div {:class (stl/css :modal-dialog)
|
[:div {:class (stl/css :modal-dialog)
|
||||||
:data-testid "token-remapping-modal"}
|
:data-testid "token-remapping-modal"}
|
||||||
|
[:> icon-button* {:on-click cancel-action
|
||||||
|
:class (stl/css :close-btn)
|
||||||
|
:icon i/close
|
||||||
|
:variant "action"
|
||||||
|
:aria-label (tr "labels.close")}]
|
||||||
|
|
||||||
[:div {:class (stl/css :modal-header)}
|
[:div {:class (stl/css :modal-header)}
|
||||||
[:> heading* {:level 2
|
[:> heading* {:level 2
|
||||||
:typography "headline-medium"
|
:id "modal-title"
|
||||||
|
:typography "headline-large"
|
||||||
:class (stl/css :modal-title)}
|
:class (stl/css :modal-title)}
|
||||||
(tr "workspace.tokens.remap-token-references")]]
|
(tr "workspace.tokens.remap-token-references-title" old-token-name new-token-name)]]
|
||||||
[:div {:class (stl/css :modal-content)}
|
[:div {:class (stl/css :modal-content)}
|
||||||
[:> heading* {:level 3
|
[:> text* {:as "p" :typography t/body-medium} (tr "workspace.tokens.remap-warning-effects")]
|
||||||
:typography "title-medium"
|
[:> text* {:as "p" :typography t/body-medium} (tr "workspace.tokens.remap-warning-time")]]
|
||||||
:class (stl/css :modal-msg)}
|
|
||||||
(tr "workspace.tokens.renaming-token-from-to" old-token-name new-token-name)]
|
|
||||||
[:div {:class (stl/css :modal-scd-msg)}
|
|
||||||
(if (> references-count 0)
|
|
||||||
(tr "workspace.tokens.references-found" references-count)
|
|
||||||
(tr "workspace.tokens.no-references-found"))]
|
|
||||||
(when remapping-in-progress?
|
|
||||||
[:> context-notification*
|
|
||||||
{:level :info
|
|
||||||
:appearance :ghost}
|
|
||||||
(tr "workspace.tokens.remapping-in-progress")])]
|
|
||||||
[:div {:class (stl/css :modal-footer)}
|
[:div {:class (stl/css :modal-footer)}
|
||||||
[:div {:class (stl/css :action-buttons)}
|
[:div {:class (stl/css :action-buttons)}
|
||||||
[:> button* {:on-click on-cancel-remap
|
[:> button* {:on-click rename-token
|
||||||
:type "button"
|
:type "button"
|
||||||
:variant "secondary"
|
:variant "secondary"}
|
||||||
:disabled remapping-in-progress?}
|
(tr "workspace.tokens.not-remap")]
|
||||||
(tr "labels.cancel")]
|
[:> button* {:on-click confirm-remap
|
||||||
[:> button* {:on-click on-confirm-remap
|
|
||||||
:type "button"
|
:type "button"
|
||||||
:variant "primary"
|
:variant "primary"}
|
||||||
:disabled remapping-in-progress?}
|
(tr "workspace.tokens.remap")]]]]]))
|
||||||
(if (> references-count 0)
|
|
||||||
(tr "workspace.tokens.remap-and-rename")
|
|
||||||
(tr "workspace.tokens.rename-only"))]]]]]))
|
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
@use "refactor/common-refactor.scss" as deprecated;
|
@use "refactor/common-refactor.scss" as deprecated;
|
||||||
|
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
|
--modal-title-foreground-color: var(--color-foreground-primary);
|
||||||
|
--modal-text-foreground-color: var(--color-foreground-secondary);
|
||||||
|
|
||||||
@extend .modal-overlay-base;
|
@extend .modal-overlay-base;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -17,16 +20,22 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
inset-block-start: 0;
|
inset-block-start: 0;
|
||||||
height: 100%;
|
block-size: 100%;
|
||||||
width: 100%;
|
inline-size: 100%;
|
||||||
background-color: var(--overlay-color);
|
background-color: var(--overlay-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
inset-block-start: $sz-6;
|
||||||
|
inset-inline-end: $sz-6;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
@extend .modal-container-base;
|
@extend .modal-container-base;
|
||||||
width: 100%;
|
inline-size: 100%;
|
||||||
max-width: 32rem;
|
max-inline-size: 32rem;
|
||||||
max-height: unset;
|
max-block-size: unset;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -41,15 +50,12 @@
|
|||||||
.modal-title {
|
.modal-title {
|
||||||
@include t.use-typography("headline-medium");
|
@include t.use-typography("headline-medium");
|
||||||
color: var(--modal-title-foreground-color);
|
color: var(--modal-title-foreground-color);
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
@include t.use-typography("body-large");
|
@include t.use-typography("body-large");
|
||||||
margin-block-end: var(--sp-xxl);
|
color: var(--modal-text-foreground-color);
|
||||||
padding: var(--sp-xxl) 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--sp-l);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-footer {
|
.modal-footer {
|
||||||
|
|||||||
@@ -225,68 +225,49 @@
|
|||||||
get-expr)])
|
get-expr)])
|
||||||
|
|
||||||
(when set-expr
|
(when set-expr
|
||||||
(concat
|
[schema-sym schema-n
|
||||||
(when (and schema-n (not (list? schema-n)))
|
|
||||||
[coercer-sym `(sm/coercer ~schema-n)])
|
|
||||||
|
|
||||||
(when (and schema-n (list? schema-n))
|
coercer-sym `(if (and (some? ~schema-sym)
|
||||||
[schema-sym schema-n])
|
(not (fn? ~schema-sym)))
|
||||||
|
(sm/coercer ~schema-sym)
|
||||||
|
nil)
|
||||||
|
|
||||||
[decode-sym decode-expr]
|
decode-sym decode-expr
|
||||||
|
|
||||||
[(make-sym pname "set-fn")
|
(make-sym pname "set-fn")
|
||||||
(if this?
|
|
||||||
`(fn [~val-sym]
|
`(fn [~val-sym]
|
||||||
(let [~@(if (and schema-n (list? schema-n))
|
(let [~this-sym (~'js* "this")
|
||||||
[schema-sym `(~schema-sym ~val-sym)
|
|
||||||
coercer-sym `(sm/coercer ~schema-sym)]
|
|
||||||
[])
|
|
||||||
~this-sym (~'js* "this")
|
|
||||||
~fn-sym ~set-expr
|
~fn-sym ~set-expr
|
||||||
~val-sym ~(if schema-n
|
|
||||||
(if decode-expr
|
;; We only emit schema and coercer bindings if
|
||||||
|
;; schema-n is provided
|
||||||
|
~@(if (some? schema-n)
|
||||||
|
[schema-sym `(if (fn? ~schema-sym)
|
||||||
|
(~schema-sym ~val-sym)
|
||||||
|
~schema-sym)
|
||||||
|
|
||||||
|
coercer-sym `(if (nil? ~coercer-sym)
|
||||||
|
(sm/coercer ~schema-sym)
|
||||||
|
~coercer-sym)
|
||||||
|
val-sym (if (not= decode-expr 'app.common.json/->clj)
|
||||||
`(~decode-sym ~val-sym)
|
`(~decode-sym ~val-sym)
|
||||||
`(~decode-sym ~val-sym ~decode-options))
|
`(~decode-sym ~val-sym ~decode-options))
|
||||||
val-sym)
|
val-sym `(~coercer-sym ~val-sym)]
|
||||||
~val-sym ~(if schema-n
|
[])]
|
||||||
`(~coercer-sym ~val-sym)
|
|
||||||
val-sym)]
|
~(if this?
|
||||||
(.call ~fn-sym ~this-sym ~this-sym ~val-sym)))
|
`(.call ~fn-sym ~this-sym ~this-sym ~val-sym)
|
||||||
`(fn [~val-sym]
|
`(.call ~fn-sym ~this-sym ~val-sym))))])
|
||||||
(let [~@(if (and schema-n (list? schema-n))
|
|
||||||
[schema-sym `(~schema-sym ~val-sym)
|
|
||||||
coercer-sym `(sm/coercer ~schema-sym)]
|
|
||||||
[])
|
|
||||||
~this-sym (~'js* "this")
|
|
||||||
~fn-sym ~set-expr
|
|
||||||
~val-sym ~(if schema-n
|
|
||||||
(if decode-expr
|
|
||||||
`(~decode-sym ~val-sym)
|
|
||||||
`(~decode-sym ~val-sym ~decode-options))
|
|
||||||
val-sym)
|
|
||||||
~val-sym ~(if schema-n
|
|
||||||
`(~coercer-sym ~val-sym)
|
|
||||||
val-sym)]
|
|
||||||
(.call ~fn-sym ~this-sym ~val-sym))))]))
|
|
||||||
|
|
||||||
(when fn-expr
|
(when fn-expr
|
||||||
(concat
|
[schema-sym (or schema-n schema-1)
|
||||||
(cond
|
coercer-sym `(if (and (some? ~schema-sym)
|
||||||
(and schema-n (not (list? schema-n)))
|
(not (fn? ~schema-sym)))
|
||||||
[coercer-sym `(sm/coercer ~schema-n)]
|
(sm/coercer ~schema-sym)
|
||||||
|
nil)
|
||||||
|
decode-sym decode-expr
|
||||||
|
|
||||||
(and schema-n (list? schema-n))
|
(make-sym pname "get-fn")
|
||||||
[schema-sym schema-n]
|
|
||||||
|
|
||||||
(and schema-1 (not (list? schema-1)))
|
|
||||||
[coercer-sym `(sm/coercer ~schema-1)]
|
|
||||||
|
|
||||||
(and schema-1 (list? schema-1))
|
|
||||||
[schema-sym schema-1])
|
|
||||||
|
|
||||||
[decode-sym decode-expr]
|
|
||||||
|
|
||||||
[(make-sym pname "get-fn")
|
|
||||||
`(fn []
|
`(fn []
|
||||||
(let [~this-sym (~'js* "this")
|
(let [~this-sym (~'js* "this")
|
||||||
~fn-sym ~fn-expr
|
~fn-sym ~fn-expr
|
||||||
@@ -294,49 +275,33 @@
|
|||||||
`(.bind ~fn-sym ~this-sym ~this-sym)
|
`(.bind ~fn-sym ~this-sym ~this-sym)
|
||||||
`(.bind ~fn-sym ~this-sym))
|
`(.bind ~fn-sym ~this-sym))
|
||||||
|
|
||||||
~@(if schema-1
|
;; We only emit schema and coercer bindings if
|
||||||
[fn-sym `(fn* [~val-sym]
|
;; schema-n or schema-1 is provided
|
||||||
(let [~val-sym
|
~@(if (or schema-n schema-1)
|
||||||
~(if decode-expr
|
[fn-sym `(fn* [~@(if schema-1 [val-sym] [])]
|
||||||
|
(let [~@(if schema-n
|
||||||
|
[val-sym `(into-array (cljs.core/js-arguments))]
|
||||||
|
[])
|
||||||
|
~val-sym ~(if (not= decode-expr 'app.common.json/->clj)
|
||||||
`(~decode-sym ~val-sym)
|
`(~decode-sym ~val-sym)
|
||||||
`(~decode-sym ~val-sym ~decode-options))
|
`(~decode-sym ~val-sym ~decode-options))
|
||||||
|
|
||||||
~@(if (or (and schema-n (list? schema-n))
|
~schema-sym (if (fn? ~schema-sym)
|
||||||
(and schema-1 (list? schema-1)))
|
(~schema-sym ~val-sym)
|
||||||
[schema-sym `(~schema-sym ~val-sym)
|
~schema-sym)
|
||||||
coercer-sym `(sm/coercer ~schema-sym)]
|
|
||||||
[])
|
|
||||||
|
|
||||||
~val-sym
|
~coercer-sym (if (nil? ~coercer-sym)
|
||||||
(~coercer-sym ~val-sym)]
|
(sm/coercer ~schema-sym)
|
||||||
|
~coercer-sym)
|
||||||
|
|
||||||
(~fn-sym ~val-sym)))]
|
~val-sym (~coercer-sym ~val-sym)]
|
||||||
[])
|
~(if schema-1
|
||||||
~@(if schema-n
|
`(~fn-sym ~val-sym)
|
||||||
[fn-sym `(fn* []
|
`(apply ~fn-sym ~val-sym))))]
|
||||||
(let [~val-sym
|
|
||||||
(into-array (cljs.core/js-arguments))
|
|
||||||
|
|
||||||
~val-sym
|
|
||||||
~(if decode-expr
|
|
||||||
`(~decode-sym ~val-sym)
|
|
||||||
`(~decode-sym ~val-sym ~decode-options))
|
|
||||||
|
|
||||||
~@(if (or (and schema-n (list? schema-n))
|
|
||||||
(and schema-1 (list? schema-1)))
|
|
||||||
[schema-sym `(~schema-sym ~val-sym)
|
|
||||||
coercer-sym `(sm/coercer ~schema-sym)]
|
|
||||||
[])
|
|
||||||
|
|
||||||
~val-sym
|
|
||||||
(~coercer-sym ~val-sym)]
|
|
||||||
(apply ~fn-sym ~val-sym)))]
|
|
||||||
[])
|
|
||||||
~@(if wrap
|
|
||||||
[fn-sym `(~wrap-sym ~fn-sym)]
|
|
||||||
[])]
|
[])]
|
||||||
|
~(if wrap
|
||||||
~fn-sym))])))))))]
|
`(~wrap-sym ~fn-sym)
|
||||||
|
fn-sym)))]))))))]
|
||||||
|
|
||||||
`(let [~target-sym ~rsym
|
`(let [~target-sym ~rsym
|
||||||
~@bindings]
|
~@bindings]
|
||||||
|
|||||||
@@ -23,15 +23,15 @@
|
|||||||
[node]
|
[node]
|
||||||
(is-element node "br"))
|
(is-element node "br"))
|
||||||
|
|
||||||
(defn is-inline-child
|
(defn is-text-span-child
|
||||||
[node]
|
[node]
|
||||||
(or (is-line-break node)
|
(or (is-line-break node)
|
||||||
(is-text-node node)))
|
(is-text-node node)))
|
||||||
|
|
||||||
(defn get-inline-text
|
(defn get-text-span-text
|
||||||
[element]
|
[element]
|
||||||
(when-not (is-inline-child (.-firstChild element))
|
(when-not (is-text-span-child (.-firstChild element))
|
||||||
(throw (js/TypeError. "Invalid inline child")))
|
(throw (js/TypeError. "Invalid text span child")))
|
||||||
(if (is-line-break (.-firstChild element))
|
(if (is-line-break (.-firstChild element))
|
||||||
""
|
""
|
||||||
(.-textContent element)))
|
(.-textContent element)))
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
(assoc acc key (if (value-empty? value) (get defaults key) value))))
|
(assoc acc key (if (value-empty? value) (get defaults key) value))))
|
||||||
{} attrs)))
|
{} attrs)))
|
||||||
|
|
||||||
(defn get-inline-styles
|
(defn get-text-span-styles
|
||||||
[element]
|
[element]
|
||||||
(get-attrs-from-styles element txt/text-node-attrs (txt/get-default-text-attrs)))
|
(get-attrs-from-styles element txt/text-node-attrs (txt/get-default-text-attrs)))
|
||||||
|
|
||||||
@@ -66,18 +66,18 @@
|
|||||||
[element]
|
[element]
|
||||||
(get-attrs-from-styles element txt/root-attrs txt/default-root-attrs))
|
(get-attrs-from-styles element txt/root-attrs txt/default-root-attrs))
|
||||||
|
|
||||||
(defn create-inline
|
(defn create-text-span
|
||||||
[element]
|
[element]
|
||||||
(let [text (get-inline-text element)]
|
(let [text (get-text-span-text element)]
|
||||||
(d/merge {:text text
|
(d/merge {:text text
|
||||||
:key (.-id element)}
|
:key (.-id element)}
|
||||||
(get-inline-styles element))))
|
(get-text-span-styles element))))
|
||||||
|
|
||||||
(defn create-paragraph
|
(defn create-paragraph
|
||||||
[element]
|
[element]
|
||||||
(d/merge {:type "paragraph"
|
(d/merge {:type "paragraph"
|
||||||
:key (.-id element)
|
:key (.-id element)
|
||||||
:children (mapv create-inline (.-children element))}
|
:children (mapv create-text-span (.-children element))}
|
||||||
(get-paragraph-styles element)))
|
(get-paragraph-styles element)))
|
||||||
|
|
||||||
(defn create-root
|
(defn create-root
|
||||||
|
|||||||
@@ -92,7 +92,7 @@
|
|||||||
[root]
|
[root]
|
||||||
(get-styles-from-attrs root txt/root-attrs txt/default-text-attrs))
|
(get-styles-from-attrs root txt/root-attrs txt/default-text-attrs))
|
||||||
|
|
||||||
(defn get-inline-styles
|
(defn get-text-span-styles
|
||||||
[inline paragraph]
|
[inline paragraph]
|
||||||
(let [node (if (= "" (:text inline)) paragraph inline)
|
(let [node (if (= "" (:text inline)) paragraph inline)
|
||||||
styles (get-styles-from-attrs node txt/text-node-attrs txt/default-text-attrs)]
|
styles (get-styles-from-attrs node txt/text-node-attrs txt/default-text-attrs)]
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
(when text
|
(when text
|
||||||
(.replace text (js/RegExp "/" "g") "/\u200B")))
|
(.replace text (js/RegExp "/" "g") "/\u200B")))
|
||||||
|
|
||||||
(defn get-inline-children
|
(defn get-text-span-children
|
||||||
[inline paragraph]
|
[inline paragraph]
|
||||||
[(if (and (= "" (:text inline))
|
[(if (and (= "" (:text inline))
|
||||||
(= 1 (count (:children paragraph))))
|
(= 1 (count (:children paragraph))))
|
||||||
@@ -119,14 +119,14 @@
|
|||||||
[paragraph]
|
[paragraph]
|
||||||
(some #(not= "" (:text % "")) (:children paragraph)))
|
(some #(not= "" (:text % "")) (:children paragraph)))
|
||||||
|
|
||||||
(defn create-inline
|
(defn create-text-span
|
||||||
[inline paragraph]
|
[inline paragraph]
|
||||||
(create-element
|
(create-element
|
||||||
"span"
|
"span"
|
||||||
{:id (or (:key inline) (create-random-key))
|
{:id (or (:key inline) (create-random-key))
|
||||||
:data {:itype "inline"}
|
:data {:itype "span"}
|
||||||
:style (get-inline-styles inline paragraph)}
|
:style (get-text-span-styles inline paragraph)}
|
||||||
(get-inline-children inline paragraph)))
|
(get-text-span-children inline paragraph)))
|
||||||
|
|
||||||
(defn create-paragraph
|
(defn create-paragraph
|
||||||
[paragraph]
|
[paragraph]
|
||||||
@@ -135,7 +135,7 @@
|
|||||||
{:id (or (:key paragraph) (create-random-key))
|
{:id (or (:key paragraph) (create-random-key))
|
||||||
:data {:itype "paragraph"}
|
:data {:itype "paragraph"}
|
||||||
:style (get-paragraph-styles paragraph)}
|
:style (get-paragraph-styles paragraph)}
|
||||||
(mapv #(create-inline % paragraph) (:children paragraph))))
|
(mapv #(create-text-span % paragraph) (:children paragraph))))
|
||||||
|
|
||||||
(defn create-root
|
(defn create-root
|
||||||
[root]
|
[root]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user