From 90435db71ace434d245c615a87305821308fa14a Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani <32505158+KhudaDad414@users.noreply.github.com> Date: Tue, 11 Jul 2023 15:08:24 +0100 Subject: [PATCH] feat: add Visual Editor/Code Editor Switch component (#691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: add Visual Editor/Code Editor Switch component --------- Co-authored-by: Fran Méndez --- .changeset/perfect-sloths-repeat.md | 5 + apps/studio/.dockerignore => .dockerignore | 0 apps/studio/.eslintignore => .eslintignore | 3 +- .eslintrc.json | 176 ++++++++++++++++ apps/design-system/package.json | 1 + .../src/components/EditorSwitch.stories.tsx | 23 +++ .../src/components/OperationIcon.stories.tsx | 15 +- .../src/components/SlideOver.stories.tsx | 4 +- apps/studio/.eslintrc | 141 ------------- apps/studio/package.json | 1 + package-lock.json | 193 +++++++++++++----- package.json | 4 +- packages/ui/components/DropdownMenu.tsx | 6 +- packages/ui/components/EditorSwitch.tsx | 34 +++ packages/ui/components/Logo.tsx | 40 ++-- packages/ui/components/OperationIcon.tsx | 26 +-- packages/ui/components/ProtocolBadge.tsx | 114 +++++------ packages/ui/components/SlideOver.tsx | 10 +- packages/ui/components/Toolbar.tsx | 15 +- packages/ui/components/icons/AMQPIcon.tsx | 20 +- .../ui/components/icons/WebSocketIcon.tsx | 2 +- packages/ui/components/index.tsx | 21 +- packages/ui/package.json | 3 + packages/ui/tsup.config.ts | 2 +- turbo.json | 4 +- 25 files changed, 525 insertions(+), 338 deletions(-) create mode 100644 .changeset/perfect-sloths-repeat.md rename apps/studio/.dockerignore => .dockerignore (100%) rename apps/studio/.eslintignore => .eslintignore (74%) create mode 100644 .eslintrc.json create mode 100644 apps/design-system/src/components/EditorSwitch.stories.tsx delete mode 100644 apps/studio/.eslintrc create mode 100644 packages/ui/components/EditorSwitch.tsx diff --git a/.changeset/perfect-sloths-repeat.md b/.changeset/perfect-sloths-repeat.md new file mode 100644 index 000000000..4da9306c8 --- /dev/null +++ b/.changeset/perfect-sloths-repeat.md @@ -0,0 +1,5 @@ +--- +"@asyncapi/studio-ui": minor +--- + +Add EditorSwitch component. diff --git a/apps/studio/.dockerignore b/.dockerignore similarity index 100% rename from apps/studio/.dockerignore rename to .dockerignore diff --git a/apps/studio/.eslintignore b/.eslintignore similarity index 74% rename from apps/studio/.eslintignore rename to .eslintignore index 591f24c44..692fa078e 100644 --- a/apps/studio/.eslintignore +++ b/.eslintignore @@ -2,4 +2,5 @@ node_modules public docs lib -build \ No newline at end of file +build +dist \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..49436b5bd --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,176 @@ +{ + "env": { + "es6": true, + "browser": true, + "node": true + }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "sonarjs", "security", "react"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:sonarjs/recommended", + "plugin:react/recommended", + "plugin:security/recommended" + ], + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "strict": 0, + "no-underscore-dangle": 0, + "no-mixed-requires": 0, + "no-process-exit": 0, + "no-warning-comments": 0, + "curly": 0, + "no-multi-spaces": 0, + "no-alert": 0, + "consistent-return": 0, + "consistent-this": [0, "self"], + "func-style": 0, + "max-nested-callbacks": 0, + "camelcase": 0, + "no-debugger": 1, + "no-empty": 1, + "no-invalid-regexp": 1, + "no-unused-expressions": 0, + "no-native-reassign": 1, + "no-fallthrough": 1, + "sonarjs/cognitive-complexity": 1, + "eqeqeq": 2, + "no-undef": 2, + "no-dupe-keys": 2, + "no-empty-character-class": 2, + "no-self-compare": 2, + "valid-typeof": 2, + "no-unused-vars": [ + 2, + { + "args": "none" + } + ], + "handle-callback-err": 2, + "no-shadow-restricted-names": 2, + "no-new-require": 2, + "no-mixed-spaces-and-tabs": 2, + "block-scoped-var": 2, + "no-else-return": 2, + "no-throw-literal": 2, + "no-void": 2, + "radix": 2, + "wrap-iife": [2, "outside"], + "no-shadow": 0, + "no-path-concat": 2, + "valid-jsdoc": [ + 0, + { + "requireReturn": false, + "requireParamDescription": false, + "requireReturnDescription": false + } + ], + "no-spaced-func": 2, + "semi-spacing": 2, + "quotes": [2, "single"], + "key-spacing": [ + 2, + { + "beforeColon": false, + "afterColon": true + } + ], + "indent": [2, 2], + "no-lonely-if": 2, + "no-floating-decimal": 2, + "brace-style": [ + 2, + "1tbs", + { + "allowSingleLine": true + } + ], + "comma-style": [2, "last"], + "no-multiple-empty-lines": [ + 2, + { + "max": 1 + } + ], + "no-nested-ternary": 2, + "operator-assignment": [2, "always"], + "padded-blocks": [2, "never"], + "quote-props": [2, "as-needed"], + "keyword-spacing": [ + 2, + { + "before": true, + "after": true, + "overrides": {} + } + ], + "space-before-blocks": [2, "always"], + "array-bracket-spacing": [2, "never"], + "computed-property-spacing": [2, "never"], + "space-in-parens": [2, "never"], + "space-unary-ops": [ + 2, + { + "words": true, + "nonwords": false + } + ], + "wrap-regex": 2, + "linebreak-style": 0, + "arrow-spacing": [ + 2, + { + "before": true, + "after": true + } + ], + "no-class-assign": 2, + "no-const-assign": 2, + "no-dupe-class-members": 2, + "no-this-before-super": 2, + "no-var": 2, + "object-shorthand": [2, "always"], + "prefer-arrow-callback": 2, + "prefer-const": 2, + "prefer-spread": 2, + "prefer-template": 2, + "react/prop-types": "off", + "react/jsx-uses-react": "off", + "react/react-in-jsx-scope": "off", + "@typescript-eslint/no-empty-interface": "off", + "no-use-before-define": "off", + "@typescript-eslint/no-use-before-define": ["error"], + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-this-alias": "off", + "security/detect-object-injection": "off" + }, + "overrides": [ + { + "files": ["*.spec.ts", "*.spec.tsx", "*.test.ts", "*.test.tsx"], + "rules": { + "no-undef": "off", + "no-console": "off", + "prefer-arrow-callback": 0, + "sonarjs/no-duplicate-string": 0, + "security/detect-object-injection": 0 + } + } + ] +} diff --git a/apps/design-system/package.json b/apps/design-system/package.json index 0c85fef96..372b547ec 100644 --- a/apps/design-system/package.json +++ b/apps/design-system/package.json @@ -21,6 +21,7 @@ "test": "echo \"No tests\"", "eject": "react-scripts eject", "dev": "storybook dev -p 6006 --no-open", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", "build": "storybook build" }, "eslintConfig": { diff --git a/apps/design-system/src/components/EditorSwitch.stories.tsx b/apps/design-system/src/components/EditorSwitch.stories.tsx new file mode 100644 index 000000000..ce5e5cd6a --- /dev/null +++ b/apps/design-system/src/components/EditorSwitch.stories.tsx @@ -0,0 +1,23 @@ +import { StoryObj, Meta } from '@storybook/react' + +import { EditorSwitch } from '@asyncapi/studio-ui' + +const meta: Meta = { + component: EditorSwitch, +} + +export default meta +type Story = StoryObj +export const CodeEditor: Story = { + args: { + isCodeEditor: true, + onSwitchChange: (value) => console.log(`onSwitchChange() called.`), + }, +} + +export const VisualEditor: Story = { + args: { + isCodeEditor: false, + onSwitchChange: (value) => console.log(`onSwitchChange() called.`), + }, +} diff --git a/apps/design-system/src/components/OperationIcon.stories.tsx b/apps/design-system/src/components/OperationIcon.stories.tsx index 9bb9daa4b..33c842624 100644 --- a/apps/design-system/src/components/OperationIcon.stories.tsx +++ b/apps/design-system/src/components/OperationIcon.stories.tsx @@ -1,14 +1,15 @@ import { OperationIcon } from '@asyncapi/studio-ui'; -export default { - component: OperationIcon, - parameters: { - backgrounds: { - default: 'dark' - } - } +const meta = { + component: OperationIcon, + parameters: { + backgrounds: { + default: 'dark' + } + } }; +export default meta; export const WithReplyIcon = () => ; export const ReceiveIcon = () => ; export const SendIcon = () => ; diff --git a/apps/design-system/src/components/SlideOver.stories.tsx b/apps/design-system/src/components/SlideOver.stories.tsx index ee4520eb2..24b9e4634 100644 --- a/apps/design-system/src/components/SlideOver.stories.tsx +++ b/apps/design-system/src/components/SlideOver.stories.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { SlideOver } from "@asyncapi/studio-ui"; -export default { +const meta = { component: SlideOver, parameters: { layout: 'fullscreen', @@ -10,7 +10,7 @@ export default { } }, }; - +export default meta export const Example = () => { const [isOpen, setIsOpen] = useState(false); diff --git a/apps/studio/.eslintrc b/apps/studio/.eslintrc deleted file mode 100644 index 3ddfefcb2..000000000 --- a/apps/studio/.eslintrc +++ /dev/null @@ -1,141 +0,0 @@ -env: - es6: true - browser: true - node: true - -parser: "@typescript-eslint/parser" - -plugins: - - "@typescript-eslint" - - sonarjs - - security - - react - -extends: - - eslint:recommended - - plugin:@typescript-eslint/eslint-recommended - - plugin:@typescript-eslint/recommended - - plugin:sonarjs/recommended - - plugin:react/recommended - - plugin:security/recommended - -parserOptions: - ecmaVersion: 2018 - sourceType: module - ecmaFeatures: - jsx: true -settings: - react: - version: detect - -rules: - # Ignore Rules - strict: 0 - no-underscore-dangle: 0 - no-mixed-requires: 0 - no-process-exit: 0 - no-warning-comments: 0 - curly: 0 - no-multi-spaces: 0 - no-alert: 0 - consistent-return: 0 - consistent-this: [0, self] - func-style: 0 - max-nested-callbacks: 0 - camelcase: 0 - - # Warnings - no-debugger: 1 - no-empty: 1 - no-invalid-regexp: 1 - no-unused-expressions: 0 - no-native-reassign: 1 - no-fallthrough: 1 - sonarjs/cognitive-complexity: 1 - - # Errors - eqeqeq: 2 - no-undef: 2 - no-dupe-keys: 2 - no-empty-character-class: 2 - no-self-compare: 2 - valid-typeof: 2 - no-unused-vars: [2, { "args": "none" }] - handle-callback-err: 2 - no-shadow-restricted-names: 2 - no-new-require: 2 - no-mixed-spaces-and-tabs: 2 - block-scoped-var: 2 - no-else-return: 2 - no-throw-literal: 2 - no-void: 2 - radix: 2 - wrap-iife: [2, outside] - no-shadow: 0 - no-path-concat: 2 - valid-jsdoc: [0, {requireReturn: false, requireParamDescription: false, requireReturnDescription: false}] - - # stylistic errors - no-spaced-func: 2 - semi-spacing: 2 - quotes: [2, 'single'] - key-spacing: [2, { beforeColon: false, afterColon: true }] - indent: [2, 2] - no-lonely-if: 2 - no-floating-decimal: 2 - brace-style: [2, 1tbs, { allowSingleLine: true }] - comma-style: [2, last] - no-multiple-empty-lines: [2, {max: 1}] - no-nested-ternary: 2 - operator-assignment: [2, always] - padded-blocks: [2, never] - quote-props: [2, as-needed] - keyword-spacing: [2, {'before': true, 'after': true, 'overrides': {}}] - space-before-blocks: [2, always] - array-bracket-spacing: [2, never] - computed-property-spacing: [2, never] - space-in-parens: [2, never] - space-unary-ops: [2, {words: true, nonwords: false}] - wrap-regex: 2 - linebreak-style: 0 - semi: [2, always] - arrow-spacing: [2, {before: true, after: true}] - no-class-assign: 2 - no-const-assign: 2 - no-dupe-class-members: 2 - no-this-before-super: 2 - no-var: 2 - object-shorthand: [2, always] - prefer-arrow-callback: 2 - prefer-const: 2 - prefer-spread: 2 - prefer-template: 2 - - # React - react/prop-types: off - react/jsx-uses-react: off - react/react-in-jsx-scope: off - - # TypeScript - "@typescript-eslint/no-empty-interface": "off" - # disable JS rule - no-use-before-define: "off" - "@typescript-eslint/no-use-before-define": ["error"] - "@typescript-eslint/no-empty-function": "off" - "@typescript-eslint/ban-ts-comment": "off" - "@typescript-eslint/no-explicit-any": "off" - "@typescript-eslint/explicit-module-boundary-types": "off" - "@typescript-eslint/no-this-alias": "off" - -overrides: - - files: - - "*.spec.ts" - - "*.spec.tsx" - - "*.test.ts" - - "*.test.tsx" - rules: - no-undef: "off" - no-console: "off" - prefer-arrow-callback: 0 - sonarjs/no-duplicate-string: 0 - security/detect-object-injection: 0 \ No newline at end of file diff --git a/apps/studio/package.json b/apps/studio/package.json index 5f445da4c..36375e25e 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -50,6 +50,7 @@ "dev": "npm run start", "start": "craco start", "build": "npm run generate:template-parameters && craco build", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf build", "test": "npm run test:unit", "test:unit": "craco test --watchAll=false --detectOpenHandles", "eject": "react-scripts eject", diff --git a/package-lock.json b/package-lock.json index eab9c59ee..c95fa1399 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@changesets/cli": "^2.26.2" }, "devDependencies": { - "turbo": "^1.10.2" + "turbo": "^1.10.7" } }, "apps/design-system": { @@ -7785,6 +7785,35 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz", + "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -7972,6 +8001,23 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", @@ -14722,6 +14768,11 @@ "version": "5.0.4", "license": "MIT" }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.2", "dev": true, @@ -31990,27 +32041,27 @@ } }, "node_modules/turbo": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.10.3.tgz", - "integrity": "sha512-U4gKCWcKgLcCjQd4Pl8KJdfEKumpyWbzRu75A6FCj6Ctea1PIm58W6Ltw1QXKqHrl2pF9e1raAskf/h6dlrPCA==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.10.7.tgz", + "integrity": "sha512-xm0MPM28TWx1e6TNC3wokfE5eaDqlfi0G24kmeHupDUZt5Wd0OzHFENEHMPqEaNKJ0I+AMObL6nbSZonZBV2HA==", "dev": true, "hasInstallScript": true, "bin": { "turbo": "bin/turbo" }, "optionalDependencies": { - "turbo-darwin-64": "1.10.3", - "turbo-darwin-arm64": "1.10.3", - "turbo-linux-64": "1.10.3", - "turbo-linux-arm64": "1.10.3", - "turbo-windows-64": "1.10.3", - "turbo-windows-arm64": "1.10.3" + "turbo-darwin-64": "1.10.7", + "turbo-darwin-arm64": "1.10.7", + "turbo-linux-64": "1.10.7", + "turbo-linux-arm64": "1.10.7", + "turbo-windows-64": "1.10.7", + "turbo-windows-arm64": "1.10.7" } }, "node_modules/turbo-darwin-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.10.3.tgz", - "integrity": "sha512-IIB9IomJGyD3EdpSscm7Ip1xVWtYb7D0x7oH3vad3gjFcjHJzDz9xZ/iw/qItFEW+wGFcLSRPd+1BNnuLM8AsA==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.10.7.tgz", + "integrity": "sha512-N2MNuhwrl6g7vGuz4y3fFG2aR1oCs0UZ5HKl8KSTn/VC2y2YIuLGedQ3OVbo0TfEvygAlF3QGAAKKtOCmGPNKA==", "cpu": [ "x64" ], @@ -32021,9 +32072,9 @@ ] }, "node_modules/turbo-darwin-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.3.tgz", - "integrity": "sha512-SBNmOZU9YEB0eyNIxeeQ+Wi0Ufd+nprEVp41rgUSRXEIpXjsDjyBnKnF+sQQj3+FLb4yyi/yZQckB+55qXWEsw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.7.tgz", + "integrity": "sha512-WbJkvjU+6qkngp7K4EsswOriO3xrNQag7YEGRtfLoDdMTk4O4QTeU6sfg2dKfDsBpTidTvEDwgIYJhYVGzrz9Q==", "cpu": [ "arm64" ], @@ -32034,9 +32085,9 @@ ] }, "node_modules/turbo-linux-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.10.3.tgz", - "integrity": "sha512-kvAisGKE7xHJdyMxZLvg53zvHxjqPK1UVj4757PQqtx9dnjYHSc8epmivE6niPgDHon5YqImzArCjVZJYpIGHQ==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.10.7.tgz", + "integrity": "sha512-x1CF2CDP1pDz/J8/B2T0hnmmOQI2+y11JGIzNP0KtwxDM7rmeg3DDTtDM/9PwGqfPotN9iVGgMiMvBuMFbsLhg==", "cpu": [ "x64" ], @@ -32047,9 +32098,9 @@ ] }, "node_modules/turbo-linux-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.10.3.tgz", - "integrity": "sha512-Qgaqln0IYRgyL0SowJOi+PNxejv1I2xhzXOI+D+z4YHbgSx87ox1IsALYBlK8VRVYY8VCXl+PN12r1ioV09j7A==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.10.7.tgz", + "integrity": "sha512-JtnBmaBSYbs7peJPkXzXxsRGSGBmBEIb6/kC8RRmyvPAMyqF8wIex0pttsI+9plghREiGPtRWv/lfQEPRlXnNQ==", "cpu": [ "arm64" ], @@ -32060,9 +32111,9 @@ ] }, "node_modules/turbo-windows-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.10.3.tgz", - "integrity": "sha512-rbH9wManURNN8mBnN/ZdkpUuTvyVVEMiUwFUX4GVE5qmV15iHtZfDLUSGGCP2UFBazHcpNHG1OJzgc55GFFrUw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.10.7.tgz", + "integrity": "sha512-7A/4CByoHdolWS8dg3DPm99owfu1aY/W0V0+KxFd0o2JQMTQtoBgIMSvZesXaWM57z3OLsietFivDLQPuzE75w==", "cpu": [ "x64" ], @@ -32073,9 +32124,9 @@ ] }, "node_modules/turbo-windows-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.10.3.tgz", - "integrity": "sha512-ThlkqxhcGZX39CaTjsHqJnqVe+WImjX13pmjnpChz6q5HHbeRxaJSFzgrHIOt0sUUVx90W/WrNRyoIt/aafniw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.10.7.tgz", + "integrity": "sha512-D36K/3b6+hqm9IBAymnuVgyePktwQ+F0lSXr2B9JfAdFPBktSqGmp50JNC7pahxhnuCLj0Vdpe9RqfnJw5zATA==", "cpu": [ "arm64" ], @@ -34014,8 +34065,10 @@ "@headlessui/react": "^1.7.15", "@heroicons/react": "^2.0.18", "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.6", + "classnames": "^2.3.2", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -35263,9 +35316,11 @@ "@headlessui/react": "^1.7.15", "@heroicons/react": "^2.0.18", "@radix-ui/react-dropdown-menu": "^2.0.5", + "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.6", "@types/react": "^18.2.5", + "classnames": "^2.3.2", "eslint": "^7.32.0", "eslint-config-custom": "*", "postcss": "^8.4.20", @@ -39416,6 +39471,21 @@ "@radix-ui/react-compose-refs": "1.0.1" } }, + "@radix-ui/react-switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.0.3.tgz", + "integrity": "sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + } + }, "@radix-ui/react-toggle": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", @@ -39511,6 +39581,14 @@ "@babel/runtime": "^7.13.10" } }, + "@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "requires": { + "@babel/runtime": "^7.13.10" + } + }, "@radix-ui/react-use-rect": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", @@ -44151,6 +44229,11 @@ "classcat": { "version": "5.0.4" }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "clean-css": { "version": "5.3.2", "dev": true, @@ -55763,58 +55846,58 @@ } }, "turbo": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.10.3.tgz", - "integrity": "sha512-U4gKCWcKgLcCjQd4Pl8KJdfEKumpyWbzRu75A6FCj6Ctea1PIm58W6Ltw1QXKqHrl2pF9e1raAskf/h6dlrPCA==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-1.10.7.tgz", + "integrity": "sha512-xm0MPM28TWx1e6TNC3wokfE5eaDqlfi0G24kmeHupDUZt5Wd0OzHFENEHMPqEaNKJ0I+AMObL6nbSZonZBV2HA==", "dev": true, "requires": { - "turbo-darwin-64": "1.10.3", - "turbo-darwin-arm64": "1.10.3", - "turbo-linux-64": "1.10.3", - "turbo-linux-arm64": "1.10.3", - "turbo-windows-64": "1.10.3", - "turbo-windows-arm64": "1.10.3" + "turbo-darwin-64": "1.10.7", + "turbo-darwin-arm64": "1.10.7", + "turbo-linux-64": "1.10.7", + "turbo-linux-arm64": "1.10.7", + "turbo-windows-64": "1.10.7", + "turbo-windows-arm64": "1.10.7" } }, "turbo-darwin-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.10.3.tgz", - "integrity": "sha512-IIB9IomJGyD3EdpSscm7Ip1xVWtYb7D0x7oH3vad3gjFcjHJzDz9xZ/iw/qItFEW+wGFcLSRPd+1BNnuLM8AsA==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.10.7.tgz", + "integrity": "sha512-N2MNuhwrl6g7vGuz4y3fFG2aR1oCs0UZ5HKl8KSTn/VC2y2YIuLGedQ3OVbo0TfEvygAlF3QGAAKKtOCmGPNKA==", "dev": true, "optional": true }, "turbo-darwin-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.3.tgz", - "integrity": "sha512-SBNmOZU9YEB0eyNIxeeQ+Wi0Ufd+nprEVp41rgUSRXEIpXjsDjyBnKnF+sQQj3+FLb4yyi/yZQckB+55qXWEsw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.10.7.tgz", + "integrity": "sha512-WbJkvjU+6qkngp7K4EsswOriO3xrNQag7YEGRtfLoDdMTk4O4QTeU6sfg2dKfDsBpTidTvEDwgIYJhYVGzrz9Q==", "dev": true, "optional": true }, "turbo-linux-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.10.3.tgz", - "integrity": "sha512-kvAisGKE7xHJdyMxZLvg53zvHxjqPK1UVj4757PQqtx9dnjYHSc8epmivE6niPgDHon5YqImzArCjVZJYpIGHQ==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.10.7.tgz", + "integrity": "sha512-x1CF2CDP1pDz/J8/B2T0hnmmOQI2+y11JGIzNP0KtwxDM7rmeg3DDTtDM/9PwGqfPotN9iVGgMiMvBuMFbsLhg==", "dev": true, "optional": true }, "turbo-linux-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.10.3.tgz", - "integrity": "sha512-Qgaqln0IYRgyL0SowJOi+PNxejv1I2xhzXOI+D+z4YHbgSx87ox1IsALYBlK8VRVYY8VCXl+PN12r1ioV09j7A==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.10.7.tgz", + "integrity": "sha512-JtnBmaBSYbs7peJPkXzXxsRGSGBmBEIb6/kC8RRmyvPAMyqF8wIex0pttsI+9plghREiGPtRWv/lfQEPRlXnNQ==", "dev": true, "optional": true }, "turbo-windows-64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.10.3.tgz", - "integrity": "sha512-rbH9wManURNN8mBnN/ZdkpUuTvyVVEMiUwFUX4GVE5qmV15iHtZfDLUSGGCP2UFBazHcpNHG1OJzgc55GFFrUw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.10.7.tgz", + "integrity": "sha512-7A/4CByoHdolWS8dg3DPm99owfu1aY/W0V0+KxFd0o2JQMTQtoBgIMSvZesXaWM57z3OLsietFivDLQPuzE75w==", "dev": true, "optional": true }, "turbo-windows-arm64": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.10.3.tgz", - "integrity": "sha512-ThlkqxhcGZX39CaTjsHqJnqVe+WImjX13pmjnpChz6q5HHbeRxaJSFzgrHIOt0sUUVx90W/WrNRyoIt/aafniw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.10.7.tgz", + "integrity": "sha512-D36K/3b6+hqm9IBAymnuVgyePktwQ+F0lSXr2B9JfAdFPBktSqGmp50JNC7pahxhnuCLj0Vdpe9RqfnJw5zATA==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index 23257a450..cbcd991f3 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,11 @@ "url": "git://github.com/asyncapi/studio.git" }, "scripts": { + "lint": "eslint --ext .tsx,.ts .", "dev": "turbo run dev", "ds": "turbo run dev --no-cache --continue --filter=design-system...", "studio": "turbo run dev --filter=studio...", + "clean": "turbo run clean && rm -rf node_modules", "build": "turbo run build", "test": "turbo run test", "changeset": "changeset", @@ -17,7 +19,7 @@ "build:ds": "turbo run build --filter=design-system..." }, "devDependencies": { - "turbo": "^1.10.2" + "turbo": "^1.10.7" }, "workspaces": [ "apps/*", diff --git a/packages/ui/components/DropdownMenu.tsx b/packages/ui/components/DropdownMenu.tsx index 8cc0f334c..07c7e8cd1 100644 --- a/packages/ui/components/DropdownMenu.tsx +++ b/packages/ui/components/DropdownMenu.tsx @@ -4,7 +4,7 @@ import * as RadixDropdownMenu from '@radix-ui/react-dropdown-menu' interface DropdownMenuRegularItem { type?: 'regular' title: string - onSelect: () => {} + onSelect: () => void } interface DropdownMenuSeparatorItem { @@ -53,8 +53,8 @@ export const DropdownMenu: FunctionComponent = ({ { - items.map(item => ( - + items.map((item, index) => ( + )) } diff --git a/packages/ui/components/EditorSwitch.tsx b/packages/ui/components/EditorSwitch.tsx new file mode 100644 index 000000000..67af93ac7 --- /dev/null +++ b/packages/ui/components/EditorSwitch.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import * as Switch from '@radix-ui/react-switch' +import { EyeIcon, CodeBracketIcon } from '../components/icons' +import classNames from 'classnames' + +// Define styles as constants outside the component to avoid recreating these during each render +const transitionStyle = 'transition duration-200 ease-in-out' +const switchStyle = 'relative bg-gray-800 flex h-[35px] w-[62px] cursor-pointer rounded' +const switchThumbStyle = + 'pointer-events-none absolute h-[25px] inline-block w-[25px] transform rounded bg-primary transition duration-200 ease-in-out' +const switchIconsStyle = 'flex flex-grow justify-between content-center z-10 m-[4px]' + +export interface EditorSwitchProps { + isCodeEditor?: boolean + onSwitchChange?: (value: boolean) => void +} + +export const EditorSwitch: React.FC = ({ isCodeEditor = true, onSwitchChange }) => { + const visualIconStyle = classNames(transitionStyle, isCodeEditor ? 'stroke-gray-400' : 'stroke-white') + const codeIconStyle = classNames(transitionStyle, isCodeEditor ? 'stroke-white' : 'stroke-gray-400') + const thumbStyle = classNames(switchThumbStyle, isCodeEditor ? 'translate-x-7' : 'translate-x-0') + + return ( + +
+
+ + +
+
+
+ ) +} diff --git a/packages/ui/components/Logo.tsx b/packages/ui/components/Logo.tsx index 6363ee932..318591002 100644 --- a/packages/ui/components/Logo.tsx +++ b/packages/ui/components/Logo.tsx @@ -4,16 +4,6 @@ interface LogoProps { className: string } -export const Logo: FunctionComponent = ({ - className = '' -}) => { - return ( - - ) -} - function LogoImage({ className = '' }) { @@ -26,26 +16,36 @@ function LogoImage({ - - + + - - + + - - + + - - + + - - + + ) +} + +export const Logo: FunctionComponent = ({ + className = '' +}) => { + return ( + + ) } \ No newline at end of file diff --git a/packages/ui/components/OperationIcon.tsx b/packages/ui/components/OperationIcon.tsx index 8f54876c8..3fa866b8b 100644 --- a/packages/ui/components/OperationIcon.tsx +++ b/packages/ui/components/OperationIcon.tsx @@ -1,4 +1,4 @@ -import { FunctionComponent } from "react"; +import { FunctionComponent } from 'react'; import { ArrowUturnLeftIcon, ArrowDownLeftIcon, ArrowUpRightIcon } from '@heroicons/react/24/outline'; interface IconProps { @@ -7,25 +7,25 @@ interface IconProps { export const OperationIcon: FunctionComponent = ({ operation }) => { const bgColors = { - 'send': 'bg-blue-100', - 'receive': 'bg-green-100', - 'reply': 'bg-purple-100', + send: 'bg-blue-100', + receive: 'bg-green-100', + reply: 'bg-purple-100', }; const iconColors = { - 'send': 'text-blue-500', - 'receive': 'text-green-500', - 'reply': 'text-purple-500', + send: 'text-blue-500', + receive: 'text-green-500', + reply: 'text-purple-500', }; const IconComponent = (() => { switch (operation) { - case 'send': - return ArrowUpRightIcon; - case 'receive': - return ArrowDownLeftIcon; - case 'reply': - return ArrowUturnLeftIcon; + case 'send': + return ArrowUpRightIcon; + case 'receive': + return ArrowDownLeftIcon; + case 'reply': + return ArrowUturnLeftIcon; } })(); diff --git a/packages/ui/components/ProtocolBadge.tsx b/packages/ui/components/ProtocolBadge.tsx index fd1a34eb5..0272cb746 100644 --- a/packages/ui/components/ProtocolBadge.tsx +++ b/packages/ui/components/ProtocolBadge.tsx @@ -15,76 +15,76 @@ export const ProtocolBadge: FunctionComponent = ({ let content let ariaLabel - switch(protocol) { - case 'http': - ariaLabel = 'HTTP' - colorName = 'cyan' // bg-cyan-100 text-cyan-900 - content = HTTP + switch (protocol) { + case 'http': + ariaLabel = 'HTTP' + colorName = 'cyan' // bg-cyan-100 text-cyan-900 + content = HTTP break; - case 'kafka': - ariaLabel = 'Kafka' - colorName = 'indigo' // bg-indigo-100 text-indigo-900 - content = + case 'kafka': + ariaLabel = 'Kafka' + colorName = 'indigo' // bg-indigo-100 text-indigo-900 + content = break; - case 'websocket': - ariaLabel = 'WebSocket' - colorName = 'emerald' // bg-emerald-100 text-emerald-900 - content = + case 'websocket': + ariaLabel = 'WebSocket' + colorName = 'emerald' // bg-emerald-100 text-emerald-900 + content = break; - case 'amqp': - ariaLabel = 'AMQP' - colorName = 'blue' // bg-blue-100 text-blue-900 - content = + case 'amqp': + ariaLabel = 'AMQP' + colorName = 'blue' // bg-blue-100 text-blue-900 + content = break; - case 'mqtt': - ariaLabel = 'MQTT' - colorName = 'purple' // bg-purple-100 text-purple-900 - content = + case 'mqtt': + ariaLabel = 'MQTT' + colorName = 'purple' // bg-purple-100 text-purple-900 + content = break; - case 'googlepubsub': - ariaLabel = 'Google PubSub' - colorName = 'sky' // bg-sky-100 text-sky-900 - content = + case 'googlepubsub': + ariaLabel = 'Google PubSub' + colorName = 'sky' // bg-sky-100 text-sky-900 + content = break; - case 'ibmmq': - ariaLabel = 'IBM MQ' - colorName = 'sky' // bg-sky-100 text-sky-900 - content = + case 'ibmmq': + ariaLabel = 'IBM MQ' + colorName = 'sky' // bg-sky-100 text-sky-900 + content = break; - case 'nats': - ariaLabel = 'NATS' - colorName = 'green' // bg-green-100 text-green-900 - content = + case 'nats': + ariaLabel = 'NATS' + colorName = 'green' // bg-green-100 text-green-900 + content = break; - case 'pulsar': - ariaLabel = 'Pulsar' - colorName = 'cyan' // bg-cyan-100 text-cyan-900 - content = + case 'pulsar': + ariaLabel = 'Pulsar' + colorName = 'cyan' // bg-cyan-100 text-cyan-900 + content = break; - case 'redis': - ariaLabel = 'Redis' - colorName = 'red' // bg-red-100 text-red-900 - content = + case 'redis': + ariaLabel = 'Redis' + colorName = 'red' // bg-red-100 text-red-900 + content = break; - case 'sns': - ariaLabel = 'AWS SNS' - colorName = 'pink' // bg-pink-100 text-pink-900 - content = + case 'sns': + ariaLabel = 'AWS SNS' + colorName = 'pink' // bg-pink-100 text-pink-900 + content = break; - case 'sqs': - ariaLabel = 'AWS SQS' - colorName = 'yellow' // bg-yellow-100 text-yellow-900 - content = + case 'sqs': + ariaLabel = 'AWS SQS' + colorName = 'yellow' // bg-yellow-100 text-yellow-900 + content = break; - case 'solace': - ariaLabel = 'Soalce' - colorName = 'green' // bg-green-100 text-green-900 - content = + case 'solace': + ariaLabel = 'Soalce' + colorName = 'green' // bg-green-100 text-green-900 + content = break; - case 'stomp': - ariaLabel = 'STOMP' - colorName = 'orange' // bg-orange-100 text-orange-900 - content = + case 'stomp': + ariaLabel = 'STOMP' + colorName = 'orange' // bg-orange-100 text-orange-900 + content = break; } return ( diff --git a/packages/ui/components/SlideOver.tsx b/packages/ui/components/SlideOver.tsx index bbe49c05b..1f71856a4 100644 --- a/packages/ui/components/SlideOver.tsx +++ b/packages/ui/components/SlideOver.tsx @@ -1,3 +1,4 @@ +import React from 'react' import { XMarkIcon } from './icons'; interface SlideOverProps { @@ -7,7 +8,6 @@ interface SlideOverProps { } export const SlideOver: React.FC = ({ isOpen, onClose, children }) => { - if (!isOpen) return null; const handleKeyDown = (event: React.KeyboardEvent) => { @@ -18,10 +18,10 @@ export const SlideOver: React.FC = ({ isOpen, onClose, children return (
- - {children} + + {children}
); }; diff --git a/packages/ui/components/Toolbar.tsx b/packages/ui/components/Toolbar.tsx index 2b8eb39b9..3b41ab134 100644 --- a/packages/ui/components/Toolbar.tsx +++ b/packages/ui/components/Toolbar.tsx @@ -17,14 +17,14 @@ interface ToolbarToggleGroupSingleItem { type: 'toggleGroupSingle' value: string items: ToolbarToggleItem[] - onValueChange: (value: string) => {} + onValueChange: (value: string) => void } interface ToolbarToggleGroupMultipleItem { type: 'toggleGroupMultiple' value: string[] items: ToolbarToggleItem[] - onValueChange: (value: string[]) => {} + onValueChange: (value: string[]) => void } interface ToolbarSeparatorItem { @@ -88,12 +88,12 @@ const ToolbarItem: FunctionComponent = ({ item }) => { > { item.items.map((groupItem) => ( - + )) } ) - } else if (type === 'toggleGroupMultiple') { + } else if (type === 'toggleGroupMultiple') { return ( = ({ item }) => { > { item.items.map((groupItem) => ( - + )) } @@ -129,9 +129,8 @@ const ToolbarItem: FunctionComponent = ({ item }) => { /> ) - } else { - throw new Error(`Unsupported type of Toolbar item: ${type}`) - } + } + throw new Error(`Unsupported type of Toolbar item: ${type}`) } export const Toolbar: FunctionComponent = ({ diff --git a/packages/ui/components/icons/AMQPIcon.tsx b/packages/ui/components/icons/AMQPIcon.tsx index 55d2dac47..1f929b4ee 100644 --- a/packages/ui/components/icons/AMQPIcon.tsx +++ b/packages/ui/components/icons/AMQPIcon.tsx @@ -5,18 +5,18 @@ const AMQPIcon = (props: any) => ( fill="currentColor" {...props} > - - - + + + - - - - + + + + - - - + + + ) diff --git a/packages/ui/components/icons/WebSocketIcon.tsx b/packages/ui/components/icons/WebSocketIcon.tsx index 28c3a0bd5..e19b69fb5 100644 --- a/packages/ui/components/icons/WebSocketIcon.tsx +++ b/packages/ui/components/icons/WebSocketIcon.tsx @@ -5,7 +5,7 @@ const WebSocketIcon = (props: any) => ( fill="currentColor" {...props} > - + diff --git a/packages/ui/components/index.tsx b/packages/ui/components/index.tsx index a2586c5fa..a0b5ab612 100644 --- a/packages/ui/components/index.tsx +++ b/packages/ui/components/index.tsx @@ -1,13 +1,14 @@ // styles -import "./styles.css" +import './styles.css' // components -export * from "./DropdownMenu" -export * from "./Logo" -export * from "./Modal" -export * from "./OperationIcon" -export * from "./ProtocolBadge" -export * from "./Sidebar" -export * from "./SlideOver" -export * from "./Toolbar" -export { default as Tooltip } from "./Tooltip" \ No newline at end of file +export * from './EditorSwitch' +export * from './DropdownMenu' +export * from './Logo' +export * from './Modal' +export * from './OperationIcon' +export * from './ProtocolBadge' +export * from './Sidebar' +export * from './SlideOver' +export * from './Toolbar' +export { default as Tooltip } from './Tooltip' diff --git a/packages/ui/package.json b/packages/ui/package.json index c091679f3..47c3ed173 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -16,14 +16,17 @@ "scripts": { "build": "tsup", "dev": "tsup --watch", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", "check-types": "tsc --noEmit" }, "dependencies": { "@headlessui/react": "^1.7.15", "@heroicons/react": "^2.0.18", + "classnames": "^2.3.2", "@radix-ui/react-dropdown-menu": "^2.0.5", "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.6", + "@radix-ui/react-switch": "^1.0.3", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/ui/tsup.config.ts b/packages/ui/tsup.config.ts index eacd9369d..0a3c45207 100644 --- a/packages/ui/tsup.config.ts +++ b/packages/ui/tsup.config.ts @@ -9,4 +9,4 @@ export default defineConfig((options: Options) => ({ minify: true, external: ['react'], ...options, -})) \ No newline at end of file +})) diff --git a/turbo.json b/turbo.json index fe85f494b..94cbee02c 100644 --- a/turbo.json +++ b/turbo.json @@ -15,8 +15,6 @@ "dependsOn": ["build"] }, "test": {}, - "bump:version": { - "cache": false - } + "clean": {} } }