├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── Code.png ├── DesignletDataBinding.png ├── LowCodeModes.png ├── Settings.png ├── SimpleArchitecture.png └── Workflow.png ├── babel.config.js ├── documentation.md ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── index.js └── qux │ ├── Luisa.vue │ ├── ModelMixin.vue │ ├── actions │ ├── AbstractAction.js │ ├── ActionEngine.js │ ├── ContentFul.js │ └── Mail.js │ ├── core │ ├── Bus.js │ ├── CSSFactory.js │ ├── CSSOptimizer.js │ ├── CSSPosition.js │ ├── CSSWriter.js │ ├── ColorUtil.js │ ├── Config.js │ ├── Const.js │ ├── ExportUtil.js │ ├── FlexModelTransformer.js │ ├── FontWriter.js │ ├── GridLayouter.js │ ├── ImportUtil.js │ ├── JSONPath.js │ ├── Logger.js │ ├── MetaWriter.js │ ├── ModelTransformer.js │ ├── RestEngine.js │ └── ValidationUtil.js │ ├── ds │ ├── FigmaDesignSystem.js │ └── QUXDesignSystem.js │ ├── figma │ ├── Figma.vue │ ├── FigmaService.js │ └── FigmaUtil.js │ ├── index.js │ ├── mixins │ ├── Event.vue │ ├── Logic.vue │ ├── Script.vue │ └── Validation.vue │ ├── scripts │ ├── ScriptAPI.js │ ├── ScriptConsole.js │ ├── ScriptEngine.js │ ├── ScriptToModel.js │ └── ScriptWorker.js │ ├── scss │ ├── qux-button.scss │ ├── qux-camera.scss │ ├── qux-chart.scss │ ├── qux-checkbox.scss │ ├── qux-checkgroup.scss │ ├── qux-children-toggle.scss │ ├── qux-combo.scss │ ├── qux-component-set.scss │ ├── qux-container.scss │ ├── qux-date-picker.scss │ ├── qux-date.scss │ ├── qux-design-system-wrapper.scss │ ├── qux-dropdown.scss │ ├── qux-dsw.scss │ ├── qux-dynamic-container.scss │ ├── qux-figma.scss │ ├── qux-icon-toggle.scss │ ├── qux-icon.scss │ ├── qux-image.scss │ ├── qux-label.scss │ ├── qux-link.scss │ ├── qux-paging.scss │ ├── qux-radiobox.scss │ ├── qux-radiogroup.scss │ ├── qux-rating.scss │ ├── qux-repeater.scss │ ├── qux-rich-text.scss │ ├── qux-segment.scss │ ├── qux-slider.scss │ ├── qux-spinner.scss │ ├── qux-stepper.scss │ ├── qux-switch.scss │ ├── qux-table.scss │ ├── qux-textarea.scss │ ├── qux-textbox.scss │ ├── qux-timeline.scss │ ├── qux-toggle.scss │ ├── qux-upload-preview.scss │ ├── qux-upload.scss │ ├── qux-vector.scss │ └── qux.scss │ ├── transformer │ ├── Flat2Flex.js │ ├── Flat2Tree.js │ ├── GridLayouter.js │ ├── Inline.js │ ├── Quant2Flat.js │ ├── RowLayouter.js │ ├── Tree2Component.js │ └── XFlat2Flex.js │ └── web │ ├── Button.vue │ ├── Camera.vue │ ├── Chart.vue │ ├── CheckBox.vue │ ├── CheckBoxGroup.vue │ ├── ChildrenToggle.vue │ ├── Combo.vue │ ├── ComponentSet.vue │ ├── Container.vue │ ├── Date.vue │ ├── DatePicker.vue │ ├── DropDown.vue │ ├── DynamicContainer.vue │ ├── Icon.vue │ ├── IconToggle.vue │ ├── Image.vue │ ├── Label.vue │ ├── Link.vue │ ├── Paging.vue │ ├── RadioBox.vue │ ├── RadioGroup.vue │ ├── Rating.vue │ ├── Repeater.vue │ ├── RichText.vue │ ├── Segment.vue │ ├── Slider.vue │ ├── Spinner.vue │ ├── Stepper.vue │ ├── Switch.vue │ ├── Table.vue │ ├── TableFixHeader.vue │ ├── TextArea.vue │ ├── TextBox.vue │ ├── Timeline.vue │ ├── Toggle.vue │ ├── Upload.vue │ ├── UploadPreview.vue │ ├── Vector.vue │ ├── WebUtil.js │ ├── _Base.vue │ ├── _DND.vue │ └── css │ ├── CSSWidgetFactory.js │ ├── CameraCSS.js │ ├── ChartCSS.js │ ├── ComponentSetCSS.js │ ├── DynamicContainerCSS.js │ ├── ImageCSS.js │ ├── PagingCSS.js │ ├── RepeaterCSS.js │ ├── RichTextCSS.js │ ├── ScreenCSS.js │ ├── SegmentCSS.js │ ├── SpinnerCSS.js │ ├── TableCSS.js │ ├── TimelineCSS.js │ ├── UploadCSS.js │ ├── UploadPreviewCSS.js │ └── VectorCSS.js ├── tests └── unit │ ├── CSSFactory.spec.js │ ├── CSSGridFixed.spec.js │ ├── CSSGridInsteadRowBug.spec.js │ ├── CSSMarginBug.spec.js │ ├── CSSMinMaxCenter.spec.js │ ├── CSSOptimizer.spec.js │ ├── CSSOptimizer2.spec.js │ ├── CSSOptimizer3.spec.js │ ├── CSSPosition.spec.js │ ├── CSSTemlateBug.spec.js │ ├── CanHaveChildren.spec.js │ ├── DataBinding_Default_Bug.spec.js │ ├── DoubleNameBug.spec.js │ ├── Embedding.spec.js │ ├── Figma_Animation_Props.spec.js │ ├── Figma_Auto.spec.js │ ├── Figma_Auto_Fixed_Centered.spec.js │ ├── Figma_Auto_Fixed_Children.spec.js │ ├── Figma_Auto_Hor_Vert.spec.js │ ├── Figma_Auto_Hug.spec.js │ ├── Figma_Auto_Padding.spec.js │ ├── Figma_Auto_Root_Fill.spec.js │ ├── Figma_Auto_Screen.spec.js │ ├── Figma_Blog.spec.js │ ├── Figma_Blog2.spec.js │ ├── Figma_Children_Toggle.spec.js │ ├── Figma_DropDown.spec.js │ ├── Figma_Dynamic.spec.js │ ├── Figma_Dynamic_Animated.spec.js │ ├── Figma_Fixed_TopDown.spec.js │ ├── Figma_Gradient.spec.js │ ├── Figma_Grid.spec.js │ ├── Figma_Hidden.spec.js │ ├── Figma_Image_Download.spec.js │ ├── Figma_Image_Fills.spec.js │ ├── Figma_Input.spec.js │ ├── Figma_LineHeight.spec.js │ ├── Figma_Master_Component_Bug.spec.js │ ├── Figma_Meta.spec.js │ ├── Figma_Missing_Links.spec.js │ ├── Figma_Mixed_Border.spec.js │ ├── Figma_Nested_Kokos.spec.js │ ├── Figma_Nested_Vector.spec.js │ ├── Figma_Nesting_Bug.spec.js │ ├── Figma_Overflow_Scroll.spec.js │ ├── Figma_Page.spec.js │ ├── Figma_Responsive_Bug.spec.js │ ├── Figma_RichText.spec.js │ ├── Figma_Space_Between.spec.js │ ├── Figma_Text.spec.js │ ├── Figma_Variants.spec.js │ ├── Figma_VariantsOld.spec.js │ ├── Figma_VariantsUpdate.spec.js │ ├── Figma_Wrap.spec.js │ ├── Fixed_Nested_Elements.spec.js │ ├── Fixed_Removed_From_Layout.spec.js │ ├── Flat2TRee_Fixed_Footer.spec.js │ ├── Flat2Tree_Attach.spec.js │ ├── FontWriter.spec.js │ ├── GridLayouter.spec.js │ ├── Group_Wrapping.spec.js │ ├── ImportUtil.spec.js │ ├── JsonPath.spec.js │ ├── Layout_RowAndGrid.spec.js │ ├── Master_Groups.spec.js │ ├── RepeaterBug.spec.js │ ├── ResponsiveWidthPaddingBug.spec.js │ ├── RestEngineTest.spec.js │ ├── TestUtil.js │ ├── ValidationLabel.spec.js │ ├── WebUtilTest.js │ ├── WidthErrorTest.spec.js │ ├── data │ ├── DoubleNameBug.json │ ├── GridRowTopDown.json │ ├── ReaterBug.json │ ├── canHaveChildren.json │ ├── canHaveChildren2.json │ ├── cssTemplatePaddingBug.json │ ├── embedding.json │ ├── evilNamingBug.json │ ├── figmaApp.json │ ├── figmaAutoFixed.json │ ├── figmaAutoFixedChildren.json │ ├── figmaAutoHorizontalAndVertical.json │ ├── figmaAutoLayout.json │ ├── figmaAutoLayoutFixedCentered.json │ ├── figmaAutoLayoutFixedCentered2.json │ ├── figmaAutoLayoutGrow.json │ ├── figmaAutoLayoutHug.json │ ├── figmaAutoLayoutScreen.json │ ├── figmaAutoLayoutScreenWrapped.json │ ├── figmaAutoMixedPadding.json │ ├── figmaAutoRootFill.json │ ├── figmaBlog.json │ ├── figmaBlog2.json │ ├── figmaChildrenToggle.json │ ├── figmaComponentOptimizer.json │ ├── figmaDesignSystemUpdateBug.json │ ├── figmaDropDown.json │ ├── figmaDynamicToggle.json │ ├── figmaFixedTopDown.json │ ├── figmaFontTest.json │ ├── figmaGradient.json │ ├── figmaHiddenBug.json │ ├── figmaImportBugs.json │ ├── figmaLineHeight.json │ ├── figmaMasterComponentBug.json │ ├── figmaMeta.json │ ├── figmaMissingLink.json │ ├── figmaMixedBorders.json │ ├── figmaNestedBugKokos.json │ ├── figmaNestedBugKokos_ManualFix.json │ ├── figmaNestedLabel.json │ ├── figmaNestedVecors.json │ ├── figmaNestingBug.json │ ├── figmaNestingBug2.json │ ├── figmaResponsiveBug.json │ ├── figmaRichText.json │ ├── figmaSpaceBetween.json │ ├── figmaSwitch.json │ ├── figmaSwitchHover.json │ ├── figmaTextStyle.json │ ├── figmaTodoexample.json │ ├── figmaVariants.json │ ├── figmaWrap.json │ ├── fixedFooter.json │ ├── fixedNestedElements.json │ ├── fixedRemovedFromLayout.json │ ├── gridBug.json │ ├── gridFixedTest.json │ ├── gridInsteadOfRowsBug.json │ ├── groups.json │ ├── images.json │ ├── layoutRowAndGrid.json │ ├── luisaAttachLabelBug.json │ ├── luisaHome.json │ ├── masterGroups.json │ ├── minMaxCenter.json │ ├── penportImport.json │ ├── responsive.json │ ├── responsiveWidthPaddingBug.json │ ├── rowAndGridErrors.json │ ├── validationApp.json │ └── widthError.json │ └── evilNameBug.spec.js └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | rules: { 11 | 'no-console': 'off', 12 | 'no-debugger': 'off', 13 | 'no-async-promise-executor': 'off', 14 | 'vue/multi-word-component-names': 'off', 15 | 'vue/no-async-in-computed-properties': 'off', 16 | 'no-useless-escape': 'off' 17 | }, 18 | parserOptions: { 19 | parser: 'babel-eslint' 20 | }, 21 | overrides: [ 22 | { 23 | files: [ 24 | '**/__tests__/*.{j,t}s?(x)', 25 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 26 | ], 27 | env: { 28 | jest: true 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Klaus Schaefers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/Code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/Code.png -------------------------------------------------------------------------------- /assets/DesignletDataBinding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/DesignletDataBinding.png -------------------------------------------------------------------------------- /assets/LowCodeModes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/LowCodeModes.png -------------------------------------------------------------------------------- /assets/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/Settings.png -------------------------------------------------------------------------------- /assets/SimpleArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/SimpleArchitecture.png -------------------------------------------------------------------------------- /assets/Workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/assets/Workflow.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | 'bili/babel' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "luisa-vue", 3 | "version": "3.0.41", 4 | "description": "Luisa - VUE Renderer for FIGMA designs. Turn your Figma or Quant-UX design into VUE, or create re-usable designlets", 5 | "main": "./dist/luisa-vue3.common.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "export": "vue-cli-service build --target lib --name luisa-vue3 ./src/index.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/KlausSchaefers/qux-low-code.git" 13 | }, 14 | "keywords": [ 15 | "Quant-UX", 16 | "Figma", 17 | "Prototyping", 18 | "Low Code", 19 | "vue.js" 20 | ], 21 | "author": "Klaus Schaefers", 22 | "license": "BSD-2-Clause", 23 | "bugs": { 24 | "url": "https://github.com/KlausSchaefers/vue-low-code/issues" 25 | }, 26 | "homepage": "https://github.com/KlausSchaefers/vue-low-code#readme", 27 | "devDependencies": { 28 | "@babel/core": "^7.12.16", 29 | "@babel/eslint-parser": "^7.12.16", 30 | "@vue/cli-plugin-babel": "~5.0.0", 31 | "@vue/cli-plugin-eslint": "~5.0.0", 32 | "@vue/cli-plugin-router": "~5.0.0", 33 | "@vue/cli-plugin-unit-jest": "~5.0.0", 34 | "@vue/cli-service": "~5.0.0", 35 | "@vue/vue3-jest": "^27.0.0-alpha.2", 36 | "@vue/compiler-sfc": "^3.2.19", 37 | "@vue/test-utils": "^2.0.0-0", 38 | "babel-eslint": "^10.1.0", 39 | "bili": "^5.0.5", 40 | "chalk": "^4.1.2", 41 | "eslint": "^7.32.0", 42 | "eslint-plugin-vue": "^8.0.3", 43 | "sass": "^1.32.7", 44 | "sass-loader": "^12.0.0", 45 | "typescript": "~4.3", 46 | "babel-jest": "^27.0.6", 47 | "jest": "^27.5.1", 48 | "jest-environment-jsdom": "^27.5.1" 49 | }, 50 | "dependencies": { 51 | "core-js": "^3.18.2", 52 | "vue": "^3.2.13", 53 | "vue-router": "^4.0.3" 54 | }, 55 | "files": [ 56 | "dist/*" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | import FigmaService from "./qux/figma/FigmaService"; 3 | import Logger from "./qux/core/Logger"; 4 | 5 | import ModelTransformer from './qux/core/ModelTransformer' 6 | import CSSOptimizer from './qux/core/CSSOptimizer' 7 | import CSSFactory from './qux/core/CSSFactory' 8 | import CSSWriter from './qux/core/CSSWriter' 9 | import qux from './qux' 10 | 11 | import FigmaDesignSystem from './qux/ds/FigmaDesignSystem' 12 | import QUXDesignSystem from './qux/ds/QUXDesignSystem' 13 | 14 | export default { 15 | install(app) { 16 | qux.install(app) 17 | } 18 | }; 19 | 20 | export function createFigmaService (key) { 21 | return new FigmaService(key) 22 | } 23 | 24 | export function createModelTransformer (app, config = {}, selected = null) { 25 | return new ModelTransformer(app, config, selected) 26 | } 27 | 28 | export function createCSSOptimizer () { 29 | return new CSSOptimizer() 30 | } 31 | 32 | export function createCSSFactory (config = {}, imagePrefix='') { 33 | return new CSSFactory(config, imagePrefix) 34 | } 35 | 36 | export function createCSSWriter (css, id = 'default') { 37 | return new CSSWriter(css, id) 38 | } 39 | 40 | 41 | export function setLogLevel (level) { 42 | Logger.setLogLevel(level) 43 | } 44 | 45 | export async function createFigmaDesignlets (figmaFile, figmaAcessKey, config) { 46 | return FigmaDesignSystem.register(figmaFile, figmaAcessKey, config) 47 | } 48 | 49 | export async function createFigmaDesignSystem (figmaFile, figmaAcessKey, config) { 50 | return FigmaDesignSystem.register(figmaFile, figmaAcessKey, config) 51 | } 52 | 53 | 54 | export async function createQUXDesignlets (app, config) { 55 | return QUXDesignSystem.register(app, config) 56 | } 57 | 58 | export async function createQUXDesignSystem (app, config) { 59 | return QUXDesignSystem.register(app, config) 60 | } 61 | 62 | 63 | export function getDesignSystem () { 64 | return QUXDesignSystem 65 | } -------------------------------------------------------------------------------- /src/qux/ModelMixin.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | -------------------------------------------------------------------------------- /src/qux/actions/AbstractAction.js: -------------------------------------------------------------------------------- 1 | import Logger from '../core/Logger' 2 | 3 | export default class AbstractAction { 4 | 5 | _get(url, headers = {}) { 6 | return new Promise((resolve, reject) => { 7 | fetch(url, { 8 | method: 'get', 9 | headers: headers 10 | }).then((res) => { 11 | if (res.status === 200) { 12 | res.json().then(j => { 13 | Logger.log(6, '_get', 'exit ') 14 | resolve(j) 15 | }) 16 | } else { 17 | reject(res) 18 | } 19 | }).catch((err) => { 20 | reject(err) 21 | }) 22 | }) 23 | } 24 | 25 | _post(url, data, headers = {}) { 26 | return new Promise((resolve, reject) => { 27 | fetch(url, { 28 | method: 'post', 29 | body: JSON.stringify(data), 30 | headers: headers 31 | }).then((res) => { 32 | if (res.status === 200) { 33 | res.json().then(j => { 34 | Logger.log(6, 'post', 'exit ') 35 | resolve(j) 36 | }) 37 | } else { 38 | reject(res) 39 | } 40 | }).catch((err) => { 41 | reject(err) 42 | }) 43 | }) 44 | } 45 | } -------------------------------------------------------------------------------- /src/qux/actions/ActionEngine.js: -------------------------------------------------------------------------------- 1 | import Logger from '../core/Logger' 2 | import Mail from './Mail' 3 | import ContentFul from './ContentFul' 4 | 5 | export default class ActionEngine { 6 | 7 | constructor (actions) { 8 | Logger.log(-1, 'ActionEngine()', actions) 9 | this.actions = actions 10 | this.engines = { 11 | 'mail': new Mail(), 12 | 'contentful': new ContentFul() 13 | } 14 | } 15 | 16 | hasAction (actionName) { 17 | let filter = this.actions.filter(a => a.id === actionName) 18 | return filter.length === 1 19 | } 20 | 21 | async executeAction (app, actionName, viewModel) { 22 | Logger.log(-1, 'ActionEngine.executeAction()', actionName, viewModel) 23 | try { 24 | let action = this.actions.find(a => a.id === actionName) 25 | if (action && action.steps) { 26 | action.steps.forEach(async (step) => { 27 | await this.executeStep(step, viewModel, actionName) 28 | }) 29 | } else { 30 | Logger.warn('ActionEngine.executeAction() > No action with id', actionName) 31 | } 32 | } catch (err) { 33 | Logger.error('ActionEngine.executeAction() > Error during execution', err) 34 | } 35 | } 36 | 37 | async executeStep (step, viewModel, actionName) { 38 | if (this.engines[step.type]) { 39 | await this.engines[step.type].execute(step, viewModel) 40 | } else { 41 | Logger.error('ActionEngine.executeStep() > No method for step', 'execute_' + step.type, actionName) 42 | } 43 | } 44 | 45 | 46 | 47 | 48 | 49 | } -------------------------------------------------------------------------------- /src/qux/actions/Mail.js: -------------------------------------------------------------------------------- 1 | import Logger from '../core/Logger' 2 | import JSONPath from '../core/JSONPath' 3 | import AbstractAction from './AbstractAction' 4 | 5 | export default class Mail extends AbstractAction { 6 | 7 | constructor () { 8 | super() 9 | this.luisaUrl = 'https://api.luisa.cloud' 10 | } 11 | 12 | 13 | async execute (step, viewModel) { 14 | Logger.log(-1, 'Mail.execute() > enter ', step) 15 | let config = step.config 16 | try { 17 | let data = viewModel 18 | if (config.inputVariable) { 19 | Logger.log(-1, 'ActionEngine.execute() > crop view model ', config.inputVariable) 20 | data = JSONPath.get(viewModel, config.inputVariable) 21 | } 22 | let response = await this._post(`${this.luisaUrl}/api/actions/${config.appId}/mail2team`, { 23 | body: config.body, 24 | subject: config.subject, 25 | data: data 26 | }, { 27 | 'Content-Type': 'application/json', 28 | 'Accept': 'application/json' 29 | }) 30 | if (response) { 31 | Logger.log(-1, 'ActionEngine.execute() > exit ', response) 32 | } 33 | } catch (err) { 34 | Logger.warn('ActionEngine.execute() > Error ', err) 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /src/qux/core/Bus.js: -------------------------------------------------------------------------------- 1 | class Bus { 2 | 3 | constructor () { 4 | this.topicListeners = {} 5 | this._idCounter = 0 6 | this.TOPIC_LUISA_VALIDATION = 'luisa-validation' 7 | } 8 | 9 | subscribe (name, callback) { 10 | if (!this.topicListeners[name]) { 11 | this.topicListeners[name] = {} 12 | } 13 | const id = this._idCounter++ 14 | const listener = { 15 | 'topic': name, 16 | 'callback': callback, 17 | 'id': id, 18 | 'remove': () => { 19 | this.remove(name, id) 20 | } 21 | } 22 | this.topicListeners[name][id] = listener 23 | return listener 24 | } 25 | 26 | remove (name, id) { 27 | if (this.topicListeners[name]) { 28 | delete this.topicListeners[name][id] 29 | } 30 | } 31 | 32 | publish (name, data1, data2, data3, data4, data5, data6) { 33 | if (this.topicListeners[name]) { 34 | const listeners = this.topicListeners[name] 35 | Object.values(listeners).forEach(listener => { 36 | listener.callback(data1, data2, data3, data4, data5, data6) 37 | }) 38 | } 39 | } 40 | } 41 | 42 | export default new Bus() -------------------------------------------------------------------------------- /src/qux/core/CSSWriter.js: -------------------------------------------------------------------------------- 1 | import Logger from './Logger' 2 | class CSSWriter { 3 | 4 | constructor () { 5 | this.styleElement = {} 6 | } 7 | 8 | write (css, id = 'default') { 9 | let head = document.head || document.getElementsByTagName('head')[0]; 10 | if (this.styleElement[id]) { 11 | /** 12 | * This can lead to errors when the hash is changed. 13 | * This is edge case for development (I hope) 14 | */ 15 | Logger.log(5, 'CSSWriter.write() > Clean up old', id) 16 | head.removeChild(this.styleElement[id]) 17 | } 18 | 19 | let style = document.createElement('style'); 20 | style.type = 'text/css'; 21 | style.qux = true 22 | style.setAttribute('qux', id) 23 | style.appendChild(document.createTextNode(css)); 24 | head.appendChild(style); 25 | this.styleElement[id] = style 26 | } 27 | } 28 | export default new CSSWriter() -------------------------------------------------------------------------------- /src/qux/core/ColorUtil.js: -------------------------------------------------------------------------------- 1 | export function fromRgb (/*String*/ color){ 2 | var m = color.toLowerCase().match(/^rgba?\(([\s\\.,0-9]+)\)/); 3 | return m && fromArray(m[1].split(/\s*,\s*/)); // Color 4 | } 5 | 6 | export function fromHex (/*String*/ color ) { 7 | let result = {}; 8 | let bits = (color.length == 4) ? 4 : 8; 9 | let mask = (1 << bits) - 1; 10 | color = Number("0x" + color.substr(1)); 11 | if(isNaN(color)){ 12 | return 13 | } 14 | let rgb = ["b", "g", "r"] 15 | rgb.forEach(x => { 16 | var c = color & mask; 17 | color >>= bits; 18 | result[x] = bits == 4 ? 17 * c : c; 19 | }) 20 | result.a = 1; 21 | return result 22 | } 23 | 24 | export function fromArray (/** array */ a) { 25 | var result = {} 26 | let rgb = ["b", "g", "r"] 27 | rgb.forEach((x, i) => { 28 | result[x] = a[i] * 1 29 | }) 30 | if(isNaN(result.a)){ 31 | result.a = 1; 32 | } 33 | return result 34 | } 35 | 36 | 37 | export function fromString (str) { 38 | if (str === 'transparent') { 39 | return {r: 0, g:0, b:0, a:0} 40 | } else { 41 | return fromRgb(str) || fromHex(str); 42 | } 43 | } 44 | 45 | export function toString(color) { 46 | return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})` 47 | } 48 | -------------------------------------------------------------------------------- /src/qux/core/Const.js: -------------------------------------------------------------------------------- 1 | export const Layout = Object.freeze({ 2 | Row: 'row', // row means we render as rows top down. Thjis is different than felx rows! 3 | Grid: 'grid', 4 | Wrap: 'wrap', 5 | AutoHorizontal: 'auto-horizontal', 6 | AutoVertical: 'auto-vertical' 7 | }) -------------------------------------------------------------------------------- /src/qux/core/ImportUtil.js: -------------------------------------------------------------------------------- 1 | class ImportUtil { 2 | 3 | get(current, from) { 4 | let back = [] 5 | let forward = [] 6 | let currentParts = current.split('/') 7 | let fromParts = from.split('/') 8 | let notEqual = false 9 | for (let i=0; i< currentParts.length; i++) { 10 | let c = currentParts[i] 11 | let f = fromParts[i] 12 | if (c !== f) { 13 | notEqual = true 14 | } 15 | if (notEqual) { 16 | back.push('..') 17 | if (f) { 18 | forward.push(f) 19 | } 20 | } 21 | } 22 | return back.join('/') + '/' + forward.join('/') 23 | } 24 | 25 | print(screen, grid = false) { 26 | let res = [] 27 | this.printElement(res, screen, '', grid) 28 | screen.fixedChildren.forEach(e => { 29 | let pos = grid ? ` > col: ${e.gridColumnStart} - ${e.gridColumnEnd} > row: ${e.gridRowStart} - ${e.gridRowEnd}` : '' 30 | let row = e.row ? e.row : '' 31 | let actions ='' // e.lines ? ' -> ' + e.lines.map(l => l.event + ':' + l.screen.name) : '' 32 | res.push(` ${e.name}* ${pos} ${row} ${actions} `) 33 | }) 34 | return res.join('\n') 35 | } 36 | 37 | printElement(res, e, space='', grid) { 38 | // let actions ='' // e.lines ? ' -> ' + e.lines.map(l => l.event + ':' + l.screen.name) : '' 39 | // let row = e.row ? e.row : '' 40 | // let parent = e.parent ? e.parent.name + ' ' + e.parent._id : "null" 41 | // let pos = grid ? ` > col: ${e.gridColumnStart} - ${e.gridColumnEnd} > row: ${e.gridRowStart} - ${e.gridRowEnd}` : '' 42 | res.push(`${space}${e.name} - (${e.type} `) 43 | if (e.children) { 44 | e.children.forEach(c => { 45 | this.printElement(res, c, space + ' ', grid) 46 | }); 47 | } 48 | } 49 | 50 | 51 | } 52 | export default new ImportUtil() -------------------------------------------------------------------------------- /src/qux/core/Logger.js: -------------------------------------------------------------------------------- 1 | class Logger { 2 | 3 | constructor () { 4 | this.logLevel = 0 5 | } 6 | 7 | setLogLevel (l) { 8 | this.logLevel = l 9 | } 10 | 11 | warn(msg, obj) { 12 | if (obj !== undefined) { 13 | console.warn(msg, obj) 14 | } else { 15 | console.warn(msg) 16 | } 17 | } 18 | error(msg, obj) { 19 | if (obj !== undefined) { 20 | console.error(msg, obj) 21 | } else { 22 | console.error(msg) 23 | } 24 | } 25 | log(level, msg, obj, obj2="") { 26 | if (level < this.logLevel) { 27 | if (obj !== undefined) { 28 | console.debug(msg, obj, obj2) 29 | } else { 30 | console.debug(msg) 31 | } 32 | } 33 | } 34 | } 35 | export default new Logger() -------------------------------------------------------------------------------- /src/qux/core/MetaWriter.js: -------------------------------------------------------------------------------- 1 | import Logger from './Logger' 2 | 3 | class MetaWriter { 4 | 5 | 6 | write (screen) { 7 | if (screen.meta) { 8 | Logger.log(-1, 'MetaWriter.write()', screen.meta) 9 | this.setMetaTag('description', screen.meta.description) 10 | this.setMetaTag('keywords', screen.meta.keywords) 11 | } 12 | } 13 | 14 | setMetaTag (name, content) { 15 | try { 16 | let tag = document.querySelector(`meta[name="${name}"]`) 17 | if (!tag) { 18 | let tag = document.createElement('meta') 19 | document.getElementsByTagName('head')[0].appendChild(tag); 20 | } 21 | tag.content = content 22 | } catch (err){ 23 | Logger.warn('MetaWriter.write() > Could not write metag tag', err) 24 | } 25 | 26 | } 27 | 28 | } 29 | 30 | export default new MetaWriter() -------------------------------------------------------------------------------- /src/qux/core/ModelTransformer.js: -------------------------------------------------------------------------------- 1 | //import * as Util from './ExportUtil' 2 | import Logger from './Logger' 3 | 4 | import * as Flat2Tree from '../transformer/Flat2Tree' 5 | import * as Quant2Flat from '../transformer/Quant2Flat' 6 | import * as Tree2Component from '../transformer/Tree2Component' 7 | import * as Inline from '../transformer/Inline' 8 | 9 | /** 10 | * This class transforms an absolute quant-ux model into an 11 | * kind of HTML model, where the elements have a real parent 12 | * child relation child 13 | */ 14 | export default class ModelTransformer { 15 | 16 | constructor (app, config = {}, selected = null) { 17 | this.config = config 18 | this.model = app 19 | this.selected = selected 20 | } 21 | 22 | transform () { 23 | Logger.log(2, 'ModelTransformer.transform() > selected: ', this.selected) 24 | const model = this.model 25 | 26 | // 1) Make a flat model, e.g. merge in master screens 27 | const flatModel = Quant2Flat.transform(model, this.config) 28 | 29 | // 2) Build a tree and layout everything. This will also layout component sets! 30 | const treeModel = Flat2Tree.transform(flatModel, this.config) 31 | 32 | // 3) For dynamic widgets, inline all children. 33 | let inlineModel = Inline.transform(treeModel, this.config) 34 | 35 | if (this.selected) { 36 | inlineModel = Tree2Component.transform(inlineModel, this.selected, this.config) 37 | } 38 | 39 | return inlineModel 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/qux/ds/FigmaDesignSystem.js: -------------------------------------------------------------------------------- 1 | import Logger from "../core/Logger" 2 | import FigmaService from '../figma/FigmaService' 3 | import QUXDesignSystem from './QUXDesignSystem' 4 | 5 | class FigmaDesignSystem { 6 | 7 | async register(figmaFile, figmaAccessKey, config) { 8 | Logger.log(-1, "FigmaDesignSystem.register()", figmaFile) 9 | let app = figmaFile 10 | if (figmaFile && figmaAccessKey) { 11 | /** 12 | * Force the design system we pinn right for now 13 | */ 14 | if (!config) { 15 | config = {} 16 | } 17 | config.figma = { 18 | pinnRight: false 19 | } 20 | let figmaService = new FigmaService(figmaAccessKey, config) 21 | app = await figmaService.get(figmaFile, true) 22 | app = figmaService.setBackgroundImages(app) 23 | } 24 | 25 | /** 26 | * Pass here also the hasComponentSet = true! 27 | */ 28 | return QUXDesignSystem.register(app, config, true) 29 | } 30 | } 31 | 32 | export default new FigmaDesignSystem() -------------------------------------------------------------------------------- /src/qux/figma/FigmaUtil.js: -------------------------------------------------------------------------------- 1 | export function getAllChildren(fElement, result =[]) { 2 | if (fElement.children) { 3 | fElement.children.forEach(child => { 4 | result.push(child) 5 | getAllChildren(child, result) 6 | }); 7 | } 8 | return result 9 | } 10 | -------------------------------------------------------------------------------- /src/qux/mixins/Validation.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 27 | -------------------------------------------------------------------------------- /src/qux/scripts/ScriptConsole.js: -------------------------------------------------------------------------------- 1 | export default class ScriptConsole { 2 | 3 | constructor () { 4 | this.messages = [] 5 | } 6 | 7 | debug() { 8 | console.debug('ScriptConsole.debug() > ', arguments[0]) 9 | this.messages.push({ 10 | type: 'debug', 11 | args: arguments[0] 12 | }) 13 | } 14 | 15 | log() { 16 | console.log('ScriptConsole.log() > ', arguments[0]) 17 | this.messages.push({ 18 | type: 'log', 19 | args: arguments[0] 20 | }) 21 | } 22 | 23 | warn() { 24 | console.warn('ScriptConsole.warn() > ', arguments[0]) 25 | this.messages.push({ 26 | type: 'warn', 27 | args: arguments[0] 28 | }) 29 | } 30 | 31 | error() { 32 | console.warn('ScriptConsole.error() > ', arguments[0]) 33 | this.messages.push({ 34 | type: 'error', 35 | args: arguments[0] 36 | }) 37 | } 38 | } -------------------------------------------------------------------------------- /src/qux/scripts/ScriptEngine.js: -------------------------------------------------------------------------------- 1 | import Logger from '../core/Logger' 2 | import * as ExportUtil from '../core/ExportUtil' 3 | let worker = new Worker(new URL('./ScriptWorker.js', import.meta.url)) 4 | 5 | export default class ScriptEngine { 6 | 7 | 8 | run (js, model, viewModel, sourceEvent = {type: 'None'}) { 9 | Logger.log(1, 'ScriptEngine.run()') 10 | this.isDone = false 11 | return new Promise((resolve, reject) => { 12 | 13 | try { 14 | // TDOD: we could compress the model and just remove everything like styles etc... 15 | const start = new Date().getTime() 16 | worker.onmessage = (m) => this.onMessage(m, resolve, reject, start, js) 17 | worker.postMessage({ 18 | code: js, 19 | model: ExportUtil.clone(model), 20 | viewModel: ExportUtil.clone(viewModel), 21 | sourceEvent: sourceEvent 22 | }) 23 | 24 | 25 | setTimeout(() => { 26 | Logger.log(5, 'ScriptEngine.run() > isDone:', this.isDone) 27 | if (!this.isDone) { 28 | resolve({ 29 | status: 'error', 30 | error: 'Running too long' 31 | }) 32 | Logger.error('ScriptEngine.run() > need to terminate script') 33 | worker.terminate() 34 | worker = new Worker(new URL('./ScriptWorker.js', import.meta.url)) 35 | } 36 | 37 | }, 1000) 38 | 39 | } catch (error) { 40 | Logger.error('ScriptEngine.run() > Error', error) 41 | resolve({ 42 | status: 'error', 43 | error: error.message 44 | }) 45 | } 46 | Logger.log(1, 'ScriptEngine.run() > exit') 47 | }) 48 | } 49 | 50 | onMessage (message, resolve, reject, start) { 51 | const end = new Date().getTime() 52 | Logger.log(2, 'ScriptEngine.onMessage() > took',end - start) 53 | this.isDone = true 54 | resolve(message.data) 55 | } 56 | } -------------------------------------------------------------------------------- /src/qux/scripts/ScriptToModel.js: -------------------------------------------------------------------------------- 1 | import Logger from '../core/Logger' 2 | 3 | export function applyChange(model, change, renderFactory) { 4 | Logger.log(4, 'ScriptToModel.applyChange()', change, renderFactory) 5 | 6 | 7 | let element = getElementByChange(model, change) 8 | if (element) { 9 | let old = change.key === 'style' ? element.style : element.props 10 | let overwrites = change.key === 'style' ? change.style : change.props 11 | for (let key in overwrites) { 12 | old[key] = overwrites[key] 13 | } 14 | renderFactory.updateWidget(element) 15 | 16 | } else { 17 | Logger.error('ScriptToModel.applyChanges() > Cannot find element', change) 18 | } 19 | } 20 | 21 | 22 | function getElementByChange(model, change) { 23 | if (change.type === 'Widget') { 24 | return model.widgets[change.id] 25 | } 26 | if (change.type === 'Screen') { 27 | return model.screens[change.id] 28 | } 29 | } -------------------------------------------------------------------------------- /src/qux/scripts/ScriptWorker.js: -------------------------------------------------------------------------------- 1 | import ScriptAPI from './ScriptAPI' 2 | import ScriptConsole from './ScriptConsole' 3 | import Logger from '../core/Logger' 4 | 5 | self.addEventListener('message', async e => { 6 | Logger.log(3, 'ScriptWorker.message() > enter ', e) 7 | 8 | const js = e.data.code 9 | const model = e.data.model 10 | const viewModel = e.data.viewModel 11 | const sourceEvent = e.data.sourceEvent 12 | const qux = new ScriptAPI(model) 13 | const console = new ScriptConsole() 14 | 15 | let result = undefined 16 | try { 17 | result = await runCode(js, qux,viewModel, console, sourceEvent) 18 | self.postMessage({ 19 | to: result, 20 | viewModel: viewModel, 21 | appDeltas: qux.getAppDeltas(), 22 | console: console.messages, 23 | vibratePattern: qux.vibratePattern, 24 | status : 'ok' 25 | }) 26 | } catch (error) { 27 | Logger.error(1, 'ScriptWorker.message() > Error', error) 28 | console.error(error) 29 | self.postMessage({ 30 | status: 'error', 31 | console: console.messages, 32 | error: error.message, 33 | stack: error.stack 34 | }) 35 | } 36 | Logger.log(1, 'ScriptWoker.message() > exit ') 37 | }) 38 | 39 | async function runCode (js, qux,viewModel, console, sourceEvent) { 40 | if (js.indexOf('await ') > 0) { 41 | Logger.warn('ScriptWoker.runCode() > enter > ASYNC ', js) 42 | const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; 43 | const aysncJS = `return new Promise(async (resolve) => { 44 | ${js} 45 | resolve() 46 | });` 47 | console.log('ScriptWoker.runCode() > run aysnc...') 48 | const asycnCode = AsyncFunction('qux', 'data', 'console', 'event', aysncJS) 49 | return await asycnCode(qux,viewModel, console, sourceEvent); 50 | } else { 51 | Logger.log(-1, 'ScriptWoker.runCode() > enter sync') 52 | const code = new Function('qux', 'data', 'console', 'event', js); 53 | return code(qux,viewModel, console, sourceEvent) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/qux/scss/qux-button.scss: -------------------------------------------------------------------------------- 1 | .qux-button{ 2 | display: inline-flex; 3 | border-style: solid; 4 | border-width: 0px; 5 | } 6 | 7 | a.qux-button { 8 | text-decoration: none; 9 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-camera.scss: -------------------------------------------------------------------------------- 1 | .qux-camera{ 2 | cursor: pointer; 3 | transition: all 0.2s; 4 | border:1px solid red; 5 | position: relative; 6 | 7 | .qux-camera-icon { 8 | position: absolute; 9 | top:50%; 10 | left:50%; 11 | transform: translate(-50%, -50%); 12 | cursor: pointer; 13 | } 14 | 15 | input { 16 | height: 100%; 17 | width: 100%; 18 | opacity: 0; 19 | cursor: pointer; 20 | } 21 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-chart.scss: -------------------------------------------------------------------------------- 1 | .qux-chart{ 2 | border: 0px solid #333; 3 | 4 | 5 | 6 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-checkbox.scss: -------------------------------------------------------------------------------- 1 | .qux-checkbox{ 2 | display: inline-flex; 3 | align-items: center; 4 | gap: 8px; 5 | overflow: hidden; 6 | cursor: pointer; 7 | /* Use here somehow flex? */ 8 | 9 | .qux-checkbox-cntr{ 10 | position: relative; 11 | width: 100%; 12 | height: 100%; 13 | border:1px solid #333; 14 | box-sizing: border-box; 15 | } 16 | 17 | .qux-checkbox-hook{ 18 | opacity:0; 19 | transition-duration: 0.3s; 20 | transition-property: opacity; 21 | border-bottom: 3px solid #333; 22 | border-right: 3px solid #333; 23 | transform:rotate(45deg); 24 | -ms-transform:rotate(45deg); /* IE 9 */ 25 | -webkit-transform:rotate(45deg); /* Opera, Chrome, and Safari */ 26 | 27 | display: inline-block; 28 | height: 70%; 29 | width: 30%; 30 | left: 30%; 31 | position: absolute; 32 | opacity: 0; 33 | top: 0; 34 | } 35 | 36 | .qux-checkbox-label { 37 | vertical-align: middle; 38 | cursor: pointer; 39 | } 40 | } 41 | 42 | .qux-checkbox-checked { 43 | .qux-checkbox-hook{ 44 | opacity:1; 45 | } 46 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-checkgroup.scss: -------------------------------------------------------------------------------- 1 | .qux-checkgroup{ 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | 6 | .qux-checkbox{ 7 | display: flex; 8 | align-items: center; 9 | gap: 8px; 10 | 11 | .qux-checkbox-cntr{ 12 | box-sizing: border-box; 13 | } 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-children-toggle.scss: -------------------------------------------------------------------------------- 1 | .qux-children-toggle{ 2 | cursor: pointer; 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-combo.scss: -------------------------------------------------------------------------------- 1 | .qux-combo{ 2 | border:none; 3 | position: relative; 4 | transition: all 0.2s; 5 | 6 | .qux-combo-input{ 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | .qux-combo-input:focus{ 12 | outline: none; 13 | } 14 | 15 | .qux-combo-popup{ 16 | position: absolute; 17 | top: calc(100% + 1px); 18 | left: 0px; 19 | display: none; 20 | border:1px solid #333; 21 | width: 100%; 22 | z-index: 1000; 23 | max-height: 300px; 24 | overflow: auto; 25 | background: #fff; 26 | } 27 | 28 | .qux-combo-item { 29 | display: block; 30 | cursor: pointer; 31 | } 32 | 33 | &.qux-open .qux-combo-popup{ 34 | display: block; 35 | } 36 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-component-set.scss: -------------------------------------------------------------------------------- 1 | .qux-compoment-set { 2 | border-style: solid; 3 | border-width: 0px; 4 | } 5 | -------------------------------------------------------------------------------- /src/qux/scss/qux-container.scss: -------------------------------------------------------------------------------- 1 | .qux-container{ 2 | border-style: solid; 3 | border-width: 0px; 4 | 5 | .qux-container-debug{ 6 | color: red; 7 | } 8 | 9 | .qux-container-wrapper-placeholder{ 10 | background: red; 11 | height: 1px; 12 | width: 100px; 13 | } 14 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-date-picker.scss: -------------------------------------------------------------------------------- 1 | .qux-date-picker{ 2 | border:1px solid #333; 3 | position: relative; 4 | 5 | .qux-common-placeholder{ 6 | opacity: 0.7; 7 | } 8 | 9 | .qux-date-picker-icon { 10 | position: absolute; 11 | right: 50%; 12 | top: 50%; 13 | transform: translateY(-50%) translateX(50%); 14 | } 15 | 16 | .qux-date-picker-expend { 17 | position: absolute; 18 | height: 100%; 19 | top: 0px; 20 | right: 0px; 21 | width: 40px; 22 | } 23 | 24 | .qux-date-picker-carret { 25 | border-left: 4px solid transparent; 26 | border-right: 4px solid transparent; 27 | border-top: 4px solid; 28 | display: inline-block; 29 | height: 0; 30 | margin-left: 2px; 31 | vertical-align: middle; 32 | width: 0; 33 | position: absolute; 34 | right: 50%; 35 | top: 50%; 36 | transform: translateY(-50%) translateX(50%); 37 | cursor: pointer; 38 | } 39 | 40 | .qux-date-picker-popup{ 41 | position: absolute; 42 | top: calc(100% + 1px); 43 | left: 0px; 44 | display: none; 45 | border:1px solid #333; 46 | width: 100%; 47 | height: 300px; 48 | max-width: 300px; 49 | max-height: 300px; 50 | z-index: 1000; 51 | background: #fff; 52 | 53 | .qux-date{ 54 | height: 100%; 55 | } 56 | } 57 | 58 | .qux-date-picker-item { 59 | display: block; 60 | cursor: pointer; 61 | } 62 | 63 | &.qux-open .qux-date-picker-popup{ 64 | display: block; 65 | } 66 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-date.scss: -------------------------------------------------------------------------------- 1 | .qux-date { 2 | overflow: hidden; 3 | 4 | .qux-date-header { 5 | display: flex; 6 | width: 100%; 7 | height: 15%; 8 | font-size: 120%; 9 | 10 | .qux-date-header-btn { 11 | position: relative; 12 | width: 15%; 13 | text-align: center; 14 | cursor: pointer; 15 | } 16 | 17 | .qux-date-header-label { 18 | position: relative; 19 | text-align: center; 20 | flex-grow: 1; 21 | } 22 | } 23 | 24 | .qux-date-header-label .qux-common-valign-center{ 25 | width: 100%; 26 | } 27 | 28 | .qux-date-body { 29 | width: 100%; 30 | height: 85%; 31 | 32 | table { 33 | width: 100%; 34 | height: 100%; 35 | border-spacing: 0; 36 | border-collapse: collapse; 37 | text-align: center; 38 | } 39 | 40 | th, td { 41 | padding: 5px; 42 | width: 14.3%; 43 | } 44 | 45 | td { 46 | cursor: pointer; 47 | } 48 | 49 | .qux-date-selected, 50 | .qux-date-range-start, 51 | .qux-date-range-end { 52 | background: #333; 53 | color: #fff; 54 | } 55 | .qux-date-range-middle { 56 | background: #555; 57 | color: #fff; 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-design-system-wrapper.scss: -------------------------------------------------------------------------------- 1 | .qux-design-system-wrapper{ 2 | display: inline-block; 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-dropdown.scss: -------------------------------------------------------------------------------- 1 | .qux-dropdown{ 2 | 3 | transition: all 0.2s; 4 | display: inline-block; 5 | position: relative; 6 | cursor: pointer; 7 | 8 | .qux-dropdown-cntr{ 9 | height: 100%; 10 | width: 100%; 11 | border:1px solid #333; 12 | } 13 | 14 | .qux-dropdown-label { 15 | height: 100%; 16 | width: 100%; 17 | display: flex; 18 | align-items: center; 19 | 20 | .qux-dropdown-expend { 21 | position: absolute; 22 | height: 100%; 23 | top: 0px; 24 | right: 0px; 25 | width: 40px; 26 | } 27 | 28 | .qux-dropdown-carret { 29 | border-left: 4px solid transparent; 30 | border-right: 4px solid transparent; 31 | border-top: 4px solid; 32 | display: inline-block; 33 | height: 0; 34 | margin-left: 2px; 35 | vertical-align: middle; 36 | width: 0; 37 | position: absolute; 38 | right: 50%; 39 | top: 50%; 40 | transform: translateY(-50%) translateX(50%); 41 | cursor: pointer; 42 | } 43 | } 44 | 45 | 46 | 47 | .qux-dropdown-popup{ 48 | position: absolute; 49 | top: calc(100%); 50 | left: 0px; 51 | display: none; 52 | border:1px solid #333; 53 | width: 100%; 54 | z-index: 1000; 55 | background: #fff; 56 | max-height: 300px; 57 | overflow: auto; 58 | } 59 | 60 | .qux-dropdown-item { 61 | display: block; 62 | cursor: pointer; 63 | 64 | } 65 | 66 | &.qux-open .qux-dropdown-popup{ 67 | display: block; 68 | } 69 | 70 | 71 | &.qux-dropdown-mobile .qux-dropdown-popup{ 72 | position: fixed; 73 | top:50%; 74 | transform: translateY(-50%); 75 | left: 10%; 76 | width: 80%; 77 | max-height: 80%; 78 | overflow: auto; 79 | border-radius: 20px; 80 | background: #333; 81 | color:#fff; 82 | box-shadow: 0px 3px 100px rgba(0, 0, 0, 0.5); 83 | border:none; 84 | z-index: 10001; 85 | 86 | .qux-dropdown-item { 87 | display: block; 88 | cursor: pointer; 89 | padding: 20px; 90 | font-size: 20px; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-dsw.scss: -------------------------------------------------------------------------------- 1 | .qux-dsw{ 2 | border:1px solid red; 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-dynamic-container.scss: -------------------------------------------------------------------------------- 1 | .qux-dynamic-container{ 2 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-figma.scss: -------------------------------------------------------------------------------- 1 | .qux-figma{ 2 | 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-icon-toggle.scss: -------------------------------------------------------------------------------- 1 | .qux-icon-toggle{ 2 | display: flex; 3 | cursor: pointer; 4 | 5 | .qux-icon-toggle-label{ 6 | padding-left: 10px; 7 | } 8 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-icon.scss: -------------------------------------------------------------------------------- 1 | .qux-icon{ 2 | display: inline-block; 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-image.scss: -------------------------------------------------------------------------------- 1 | .qux-image{ 2 | 3 | } 4 | 5 | .qux-image-icon-back { 6 | background: #eee; 7 | color:#6c6b6b; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-label.scss: -------------------------------------------------------------------------------- 1 | .qux-label{ 2 | display: inline-block; 3 | 4 | .qux-richtext { 5 | h1 { 6 | font-size: 150%; 7 | font-weight: 600; 8 | } 9 | 10 | h2 { 11 | font-size: 140%; 12 | font-weight: 600; 13 | } 14 | 15 | h3 { 16 | font-size: 130%; 17 | font-weight: 600; 18 | } 19 | 20 | h4 { 21 | font-size: 120%; 22 | font-weight: 600; 23 | } 24 | 25 | h5 { 26 | font-size: 100%; 27 | font-weight: 600; 28 | } 29 | 30 | h6 { 31 | font-size: 100%; 32 | font-weight: 600; 33 | } 34 | 35 | } 36 | &.qux-error-label-hidden{ 37 | opacity: 0; 38 | transition: opacity 0.25s; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/qux/scss/qux-link.scss: -------------------------------------------------------------------------------- 1 | .qux-link{ 2 | display: inline-block; 3 | text-decoration: none; 4 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-paging.scss: -------------------------------------------------------------------------------- 1 | .qux-paging{ 2 | 3 | display: flex; 4 | justify-content: space-between; 5 | 6 | .qux-paging-item { 7 | cursor: pointer; 8 | position: relative; 9 | } 10 | 11 | .qux-paging-item-active{ 12 | background: #333; 13 | } 14 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-radiobox.scss: -------------------------------------------------------------------------------- 1 | .qux-radiobox{ 2 | display: inline-flex; 3 | align-items: center; 4 | gap:8px; 5 | overflow: hidden; 6 | 7 | .qux-radiobox-cntr{ 8 | position: relative; 9 | width: 100%; 10 | height: 100%; 11 | display: inline-block; 12 | vertical-align: middle; 13 | box-sizing: border-box; 14 | } 15 | 16 | .qux-radiobox-hook{ 17 | opacity:0; 18 | transition-duration: 0.3s; 19 | transition-property: opacity; 20 | background: #1973e8; 21 | border-radius: 50%; 22 | 23 | display: inline-block; 24 | height: 60%; 25 | width: 60%; 26 | left: 20%; 27 | position: absolute; 28 | opacity: 0; 29 | top: 20%; 30 | } 31 | 32 | .qux-radiobox-label { 33 | vertical-align: middle; 34 | cursor: pointer; 35 | } 36 | } 37 | 38 | .qux-radiobox-checked { 39 | .qux-radiobox-hook{ 40 | opacity:1; 41 | } 42 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-radiogroup.scss: -------------------------------------------------------------------------------- 1 | .qux-radiogroup{ 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | gap:8px; 6 | 7 | 8 | .qux-radiobox-label{ 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/qux/scss/qux-rating.scss: -------------------------------------------------------------------------------- 1 | .qux-rating{ 2 | display: flex; 3 | justify-content: space-between; 4 | 5 | .mdi{ 6 | cursor: pointer; 7 | } 8 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-repeater.scss: -------------------------------------------------------------------------------- 1 | .qux-repeater{ 2 | overflow: auto; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /src/qux/scss/qux-rich-text.scss: -------------------------------------------------------------------------------- 1 | .qux-rich-text{ 2 | display: inline-block; 3 | 4 | .qux-common-label{ 5 | width: 100%; 6 | 7 | span{ 8 | display: inline; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-segment.scss: -------------------------------------------------------------------------------- 1 | .qux-segment{ 2 | display: flex; 3 | justify-content: stretch; 4 | 5 | .qux-segment-item{ 6 | flex-grow: 1; 7 | height: 100%; 8 | cursor: pointer; 9 | display: flex; 10 | align-items: center; 11 | 12 | .qux-segment-item-label{ 13 | display: inline-block; 14 | width: 100%; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-slider.scss: -------------------------------------------------------------------------------- 1 | .qux-slider { 2 | position: relative; 3 | cursor: pointer; /* Cursor on hover */ 4 | 5 | .qux-slider-track{ 6 | width: 100%; 7 | height: 34%; 8 | position: absolute; 9 | top: calc(50% - 17%); 10 | left:0px; 11 | border: 0px; 12 | overflow: hidden; 13 | 14 | .qux-slider-progress{ 15 | height: 100%; 16 | } 17 | } 18 | 19 | .qux-slider-handle-cntr { 20 | position: absolute; 21 | top:0px; 22 | left:0px; 23 | } 24 | 25 | .qux-slider-handle{ 26 | position: absolute; 27 | top:0px; 28 | left:0px; 29 | width:40px; 30 | border-radius: 20px; 31 | cursor: pointer; 32 | } 33 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-spinner.scss: -------------------------------------------------------------------------------- 1 | .qux-spinner{ 2 | border: none; 3 | position: relative; 4 | overflow: hidden; 5 | cursor: pointer; 6 | 7 | .qux-spinner-border{ 8 | position: absolute; 9 | height: 33.3%; 10 | width: 100%; 11 | top: 33.3%; 12 | left: 0px; 13 | border-top:1px solid #333; 14 | border-bottom: 1px solid #333; 15 | } 16 | 17 | .qux-spinner-option-cntr{ 18 | position: absolute; 19 | left:0px; 20 | height: 300%; 21 | width: 100%; 22 | 23 | &.qux-spinner-option-cntr-animated { 24 | transition: 0.33s all; 25 | } 26 | } 27 | 28 | .qux-spinner-option { 29 | display: block; 30 | height: 20%; 31 | width: 100%; 32 | opacity: 0.6; 33 | position: relative; 34 | text-align: center; 35 | 36 | 37 | .qux-common-label{ 38 | width: 100%; 39 | } 40 | 41 | &.qux-spinner-option-selected{ 42 | opacity: 1; 43 | } 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-stepper.scss: -------------------------------------------------------------------------------- 1 | .qux-stepper{ 2 | display: inline-block; 3 | border: 1px solid #333; 4 | color:#333; 5 | display: flex; 6 | 7 | .qux-stepper-minus { 8 | width: 50%; 9 | position: relative; 10 | text-align: center; 11 | } 12 | .qux-stepper-plus { 13 | width: calc(50% - 1px); 14 | border-left: 1px solid #333; 15 | position: relative; 16 | text-align: center; 17 | } 18 | 19 | .qux-stepper-label{ 20 | position: absolute; 21 | top:45%; 22 | left:0px; 23 | width: 100%; 24 | transform: translateY(-50%); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/qux/scss/qux-switch.scss: -------------------------------------------------------------------------------- 1 | .qux-switch{ 2 | cursor: pointer; 3 | position: relative; 4 | 5 | .qux-switch-cntr{ 6 | position: absolute; 7 | top:0px; 8 | left:0px; 9 | width: 100%; 10 | height: 100%; 11 | overflow: hidden; 12 | 13 | &.qux-switch-cntr-thin{ 14 | height: 50%; 15 | top: 50%; 16 | transform: translateY(-50%); 17 | -webkit-transform: translateY(-50%); 18 | } 19 | 20 | .qux-switch-on{ 21 | position: absolute; 22 | width: 100%; 23 | height: 100%; 24 | top:0px; 25 | left:-100%; 26 | transition: all 0.3s; 27 | } 28 | 29 | .qux-switch-off{ 30 | position: absolute; 31 | width: 100%; 32 | height: 100%; 33 | top:0px; 34 | left:0%; 35 | transition: all 0.3s; 36 | } 37 | } 38 | 39 | .qux-switch-handle{ 40 | position: absolute; 41 | top:0px; 42 | left:0px; 43 | transition: all 0.3s; 44 | border:1px solid transparent; 45 | } 46 | 47 | 48 | &.qux-active { 49 | .qux-switch-handle{ 50 | position: absolute; 51 | top:0px; 52 | left:auto; 53 | right: 0px; 54 | } 55 | 56 | .qux-switch-on{ 57 | left:0% 58 | } 59 | 60 | .qux-switch-off{ 61 | left:100%; 62 | } 63 | } 64 | 65 | 66 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-table.scss: -------------------------------------------------------------------------------- 1 | .qux-table { 2 | overflow: auto; 3 | 4 | &.qux-table-fixed { 5 | display: flex; 6 | flex-direction: column; 7 | 8 | .qux-table-header { 9 | 10 | } 11 | 12 | .qux-table-body { 13 | overflow: auto; 14 | flex-grow: 1; 15 | } 16 | } 17 | 18 | table { 19 | width: 100%; 20 | height: 100%; 21 | border-spacing: 0; 22 | border-collapse: collapse; 23 | 24 | .qux-table-action{ 25 | cursor: pointer; 26 | } 27 | .qux-table-action:hover{ 28 | text-decoration: underline; 29 | } 30 | 31 | .qux-table-action-hover{ 32 | opacity: 0; 33 | } 34 | 35 | th { 36 | cursor: pointer; 37 | } 38 | 39 | tr:hover .qux-table-action-hover{ 40 | opacity: 1; 41 | } 42 | 43 | .qux-table-action-cntr{ 44 | text-align: right; 45 | } 46 | } 47 | 48 | 49 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-textarea.scss: -------------------------------------------------------------------------------- 1 | .qux-textarea{ 2 | border: 1px solid #333; 3 | resize: none; 4 | transition: all 0.2s; 5 | 6 | &:focus { 7 | outline: none; 8 | } 9 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-textbox.scss: -------------------------------------------------------------------------------- 1 | .qux-textbox{ 2 | border: 1px solid #333; 3 | transition: all 0.2s; 4 | padding: 0px; 5 | 6 | &:focus { 7 | outline: none; 8 | } 9 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-timeline.scss: -------------------------------------------------------------------------------- 1 | .qux-timeline{ 2 | position: relative; 3 | } 4 | 5 | .qux-timeline-line { 6 | position: absolute; 7 | height: 100%; 8 | background: #333; 9 | width: 8px; 10 | top:0px; 11 | left: 8px; 12 | border-radius:4px; 13 | } 14 | 15 | .qux-timeline-element { 16 | display: flex; 17 | align-items: center; 18 | position: relative; 19 | } 20 | 21 | .qux-timeline-circle{ 22 | display: inline-block; 23 | border-radius: 50%; 24 | border-style: solid; 25 | width: 32px; 26 | height: 32px; 27 | background: #fff; 28 | border: 2px solid #333; 29 | } 30 | 31 | .qux-timeline-label{ 32 | display: inline-block; 33 | flex-grow: 1; 34 | color: #333; 35 | padding-left: 8px; 36 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-toggle.scss: -------------------------------------------------------------------------------- 1 | .qux-toggle{ 2 | cursor: pointer; 3 | transition: all 0.2s; 4 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-upload-preview.scss: -------------------------------------------------------------------------------- 1 | .qux-upload-preview{ 2 | 3 | background-size: 100%; 4 | background-repeat: no-repeat; 5 | 6 | &.qux-upload-preview-border{ 7 | border: 0px solid #333333; 8 | } 9 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-upload.scss: -------------------------------------------------------------------------------- 1 | .qux-upload{ 2 | cursor: pointer; 3 | transition: all 0.2s; 4 | border:1px solid red; 5 | 6 | input { 7 | height: 100%; 8 | width: 100%; 9 | opacity: 0; 10 | cursor: pointer; 11 | } 12 | } -------------------------------------------------------------------------------- /src/qux/scss/qux-vector.scss: -------------------------------------------------------------------------------- 1 | .qux-vector{ 2 | background-size: 100% 100%; 3 | } -------------------------------------------------------------------------------- /src/qux/scss/qux.scss: -------------------------------------------------------------------------------- 1 | .qux{ 2 | background: none; 3 | height: 100%; 4 | 5 | &.qux-component-screen{ 6 | height: auto; 7 | display: inline-block; 8 | } 9 | } 10 | 11 | .qux-screen { 12 | display: flex; 13 | flex-direction: column; 14 | min-height: 100%; 15 | background-size: 100%; 16 | } 17 | 18 | .qux-element { 19 | box-sizing: border-box; 20 | } 21 | 22 | .qux-action { 23 | cursor: pointer; 24 | text-decoration: none; 25 | 26 | * { 27 | cursor: pointer; 28 | } 29 | } 30 | 31 | 32 | .qux-valign-middle { 33 | display: flex; 34 | align-items: center; 35 | align-content: center; 36 | 37 | .qux-common-label { 38 | width: 100%; 39 | } 40 | } 41 | 42 | .qux-valign-bottom { 43 | display: flex; 44 | align-items: flex-end; 45 | align-content: flex-end; 46 | 47 | .qux-common-label{ 48 | width: 100%; 49 | } 50 | } 51 | 52 | .qux-overlay-wrapper{ 53 | width: 100%; 54 | height: 100%; 55 | position: absolute; 56 | top:0px; 57 | left:0px; 58 | z-index: 99999; 59 | 60 | &.qux-overlay-wrapper-fixed{ 61 | position: fixed; 62 | } 63 | } 64 | 65 | .qux-screen-blurred{ 66 | filter: blur(10px); 67 | } 68 | .qux-resize-left::before { 69 | content:''; 70 | width: 3px; 71 | background: orange; 72 | } 73 | 74 | .qux-resize-right::after { 75 | content:''; 76 | width: 3px; 77 | background: orange; 78 | } 79 | 80 | .qux-common-no-select{ 81 | -webkit-touch-callout: none; /* iOS Safari */ 82 | -webkit-user-select: none; /* Safari */ 83 | -khtml-user-select: none; /* Konqueror HTML */ 84 | -moz-user-select: none; /* Old versions of Firefox */ 85 | -ms-user-select: none; /* Internet Explorer/Edge */ 86 | user-select: none; /* Non-prefixed version, currently 87 | supported by Chrome, Opera and Firefox */ 88 | } 89 | 90 | .qux-common-valign-center{ 91 | position: absolute; 92 | top:50%; 93 | left:50%; 94 | transform: translate(-50%, -50%); 95 | } 96 | 97 | .qux-noselect { 98 | -webkit-touch-callout: none; /* iOS Safari */ 99 | -webkit-user-select: none; /* Safari */ 100 | -khtml-user-select: none; /* Konqueror HTML */ 101 | -moz-user-select: none; /* Old versions of Firefox */ 102 | -ms-user-select: none; /* Internet Explorer/Edge */ 103 | user-select: none; /* Non-prefixed version, currently 104 | supported by Chrome, Edge, Opera and Firefox */ 105 | } -------------------------------------------------------------------------------- /src/qux/transformer/Inline.js: -------------------------------------------------------------------------------- 1 | import * as Util from "../core/ExportUtil" 2 | import Logger from "../core/Logger" 3 | 4 | 5 | export function transform(treeModel, config) { 6 | Logger.log(3, "Inline.transform () > enter", config) 7 | 8 | let elements = getTreeElements(treeModel) 9 | for (let id in elements) { 10 | let element = elements[id] 11 | if (element.type === 'DynamicContainer' && element.props.dynamicChildren) { 12 | Logger.log(4, "Inline.transform () > inline", element.name, element.figmaComponentId) 13 | 14 | /** 15 | * We have to set here the component class 16 | */ 17 | if (element.props.dynamicParent && elements[element.props.dynamicParent]) { 18 | let parent = elements[element.props.dynamicParent] 19 | element.cssComponentClasses = [parent.cssScreen] 20 | } 21 | 22 | /** 23 | * Attention. We set here only references. This means the 24 | * parent and css selectors are per so not correct 25 | */ 26 | element.children = [] 27 | element.props.dynamicChildren.forEach(childId => { 28 | let childElement = elements[childId] 29 | if (childElement) { 30 | element.children.push(childElement) 31 | } 32 | }) 33 | 34 | } 35 | } 36 | return treeModel 37 | } 38 | 39 | export function createCopy (element, count = 0) { 40 | let result = { 41 | id: element.id + ':' + count, 42 | name: element.name, 43 | style: Util.clone(element.style), 44 | props: Util.clone(element.props), 45 | } 46 | 47 | return result 48 | } 49 | 50 | 51 | function getTreeElements (treeModel) { 52 | let result = {} 53 | treeModel.screens.forEach(screen => { 54 | getChildElements(screen, result) 55 | }) 56 | return result 57 | } 58 | 59 | function getChildElements (element, result) { 60 | result[element.id] = element 61 | if (element.children) { 62 | element.children.forEach(child => getChildElements(child, result)) 63 | } 64 | } -------------------------------------------------------------------------------- /src/qux/transformer/Tree2Component.js: -------------------------------------------------------------------------------- 1 | // import * as Util from "../core/ExportUtil" 2 | import Logger from "../core/Logger" 3 | import * as Flat2Tree from './Flat2Tree' 4 | /** 5 | * Get a sub tree. This 6 | */ 7 | export function transform(tree, selectedName, config, postFix = '-WrapperScreen') { 8 | Logger.log(0, "Tree2Component.transform () > enter", selectedName, postFix) 9 | 10 | let componentTree = { 11 | id: tree.id, 12 | name: tree.name, 13 | templates: tree.templates, 14 | warnings: [], 15 | screens: [] 16 | } 17 | 18 | let matches = findElementsByName(tree, selectedName) 19 | if (matches.length === 1) { 20 | let match = matches[0] 21 | 22 | /** 23 | * We create here a wrapping screen 24 | */ 25 | let screen = { 26 | id: match.id + postFix, 27 | name: match.name + postFix, 28 | x: match.x, 29 | y: match.y, 30 | w: match.w, 31 | h: match.h, 32 | qType: 'qContainer', 33 | type: 'Screen', 34 | cssClass: match.cssClass + postFix, 35 | cssSelector: `.qux-screen.${match.cssClass}${postFix}`, 36 | children: [match], 37 | fixedChildren: [], 38 | actions: [], 39 | props: {}, 40 | style: {}, 41 | isComponentScreen: true 42 | } 43 | componentTree.screens.push(screen) 44 | componentTree.id = match.id + '-' + componentTree.id 45 | 46 | /** 47 | * We make a special rendering (100% height) in the CSSFactory. This 48 | * we force a grid, but what ever... 49 | */ 50 | match.hasComponentScreenParent = true 51 | 52 | /** 53 | * Do not forget to update the css Selectors 54 | */ 55 | Flat2Tree.setCSSClassNames(match, match.name+postFix) 56 | 57 | } else if (matches.length > 1){ 58 | Logger.error('Tree2Component.transform() > ERROR: More than one component with name', selectedName) 59 | } else { 60 | Logger.error('Tree2Component.transform() > ERROR: No component with name', selectedName) 61 | } 62 | 63 | return componentTree 64 | 65 | } 66 | 67 | export function findElementsByName (tree, name, result = []) { 68 | if (tree.screens) { 69 | tree.screens.forEach(screen => { 70 | findElementsByNameInScreen(screen, name, result) 71 | }) 72 | } 73 | return result 74 | } 75 | 76 | export function findElementsByNameInScreen (e, name, result = []) { 77 | if (e.children) { 78 | e.children.forEach(c => { 79 | if (c.name === name) { 80 | result.push(c) 81 | } 82 | findElementsByName(c, name, result) 83 | }) 84 | } 85 | return result 86 | } -------------------------------------------------------------------------------- /src/qux/web/Button.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{dataBindingLabel}} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{dataBindingLabel}} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 38 | -------------------------------------------------------------------------------- /src/qux/web/Camera.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /src/qux/web/CheckBox.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{label}} 8 | 9 | 10 | 13 | 85 | -------------------------------------------------------------------------------- /src/qux/web/CheckBoxGroup.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 13 | 93 | -------------------------------------------------------------------------------- /src/qux/web/Icon.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 29 | -------------------------------------------------------------------------------- /src/qux/web/IconToggle.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{label}} 6 | 7 | 8 | 11 | 62 | -------------------------------------------------------------------------------- /src/qux/web/Image.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 42 | -------------------------------------------------------------------------------- /src/qux/web/Link.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{dataBindingLabel}} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 46 | -------------------------------------------------------------------------------- /src/qux/web/RadioBox.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{label}} 8 | 9 | 10 | 13 | 70 | -------------------------------------------------------------------------------- /src/qux/web/RadioGroup.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 13 | 51 | -------------------------------------------------------------------------------- /src/qux/web/Rating.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 15 | 51 | -------------------------------------------------------------------------------- /src/qux/web/RichText.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{child}} 5 | 6 | 7 | 8 | 9 | {{child}} 10 | 11 | 12 | 13 | 16 | 40 | -------------------------------------------------------------------------------- /src/qux/web/Segment.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{option.label}} 6 | 7 | 8 | 9 | 10 | 13 | 95 | -------------------------------------------------------------------------------- /src/qux/web/Stepper.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | - 5 | 6 | 7 | 8 | + 9 | 10 | 11 | 12 | 13 | 16 | 66 | -------------------------------------------------------------------------------- /src/qux/web/Switch.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 74 | -------------------------------------------------------------------------------- /src/qux/web/TextArea.vue: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 15 | 80 | -------------------------------------------------------------------------------- /src/qux/web/Timeline.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{opt}} 9 | 10 | 11 | 12 | 13 | 16 | 67 | -------------------------------------------------------------------------------- /src/qux/web/Toggle.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{label}} 5 | 6 | 7 | 8 | 11 | 65 | -------------------------------------------------------------------------------- /src/qux/web/Upload.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{label}} 6 | 7 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/qux/web/UploadPreview.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /src/qux/web/Vector.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 28 | -------------------------------------------------------------------------------- /src/qux/web/WebUtil.js: -------------------------------------------------------------------------------- 1 | export function cleanInnerHTML(s) { 2 | const decoder = document.createElement('div') 3 | decoder.innerText = s 4 | let sanitized = decoder.innerHTML 5 | return replaceAllowedTags(sanitized) 6 | } 7 | 8 | export function replaceAllowedTags (sanitized, allowedTags = ['b', 'i', 'div', 'p', 'label', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol',' li', 'br', 'hr', 'blockquote']) { 9 | allowedTags.forEach(tag => { 10 | let needle = new RegExp(`<${tag}>`, 'gi') 11 | let replace = `<${tag}>` 12 | sanitized = sanitized.replaceAll(needle, replace) 13 | 14 | needle = new RegExp(`</${tag}>`, 'gi') 15 | replace = `${tag}>` 16 | sanitized = sanitized.replaceAll(needle, replace) 17 | }) 18 | return sanitized 19 | } 20 | -------------------------------------------------------------------------------- /src/qux/web/css/CameraCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class CameraCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'CameraCSS()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += this.cssFactory.getRawStyle(style, widget); 17 | result += '}\n\n' 18 | 19 | if (widget.hover) { 20 | let hover = widget.hover 21 | result += selector + ':hover {\n' 22 | result += ` background:${hover.background};\n` 23 | result += ` color:${hover.color};\n` 24 | result += this.cssFactory.getStyleByKey(hover, widget, this.cssFactory.borderColorProperties) 25 | result += '}\n\n' 26 | } 27 | 28 | if (widget.active) { 29 | let active = widget.active 30 | result += selector + '.qux-upload-has-files {\n' 31 | result += ` background:${active.background};\n` 32 | result += ` color:${active.color};\n` 33 | result += this.cssFactory.getStyleByKey(active, widget, this.cssFactory.borderColorProperties) 34 | result += '}\n\n' 35 | } 36 | 37 | result += selector + ' .qux-camera-icon {\n' 38 | result += ` font-size:${Math.min(widget.w, widget.h) * 0.6}px;\n` 39 | result += '}\n\n' 40 | 41 | return result 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/qux/web/css/ChartCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class ChartCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'ChartCSS.constructor()') 7 | this.cssFactory = cssFactory 8 | } 9 | 10 | run (selector, style, widget) { 11 | let result = '' 12 | result += selector + ' {\n' 13 | result += this.cssFactory.getPosition(widget); 14 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 15 | result += '}\n\n' 16 | return result 17 | } 18 | } -------------------------------------------------------------------------------- /src/qux/web/css/ComponentSetCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | import * as Util from '../../core/ExportUtil' 3 | 4 | export default class ComponentSetCSS { 5 | 6 | constructor(cssFactory) { 7 | Logger.log(5, 'ComponentSetCSS.constructor()') 8 | this.cssFactory = cssFactory 9 | } 10 | 11 | run (selector, style, widget) { 12 | 13 | let result = '' 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += '}\n\n' 17 | 18 | 19 | /** 20 | * We also layout the childrem here 21 | */ 22 | widget.children.forEach(child => { 23 | result += child.cssSelector +' {\n' 24 | if (Util.isFixedHorizontal(child)) { 25 | result += ` width: ${this.cssFactory.getCorrectedWidth(child, true)};\n` 26 | } else if (Util.hasMinMaxWdith(child)) { 27 | result += this.getMinMaxWidth(child, false) 28 | }else { 29 | result += ` min-width: ${this.cssFactory.getCorrectedWidth(child, true)};\n` 30 | } 31 | 32 | if (Util.isFixedVertical(child)) { 33 | result += ` height: ${this.cssFactory.getCorrectedHeight(child, true)};\n` 34 | } else { 35 | result += ` min-height: ${this.cssFactory.getCorrectedHeight(child, true)};\n` 36 | } 37 | 38 | result += '}\n\n' 39 | }) 40 | 41 | return result 42 | } 43 | } -------------------------------------------------------------------------------- /src/qux/web/css/DynamicContainerCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class DynamicContainerCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'DynamicContainerCSS.constructor()') 7 | this.cssFactory = cssFactory 8 | } 9 | 10 | run (selector, style, widget) { 11 | let result = '' 12 | result += selector + ' {\n' 13 | result += this.cssFactory.getPosition(widget); 14 | result += '}\n\n' 15 | return result 16 | } 17 | } -------------------------------------------------------------------------------- /src/qux/web/css/ImageCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class ImageCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'ImageCSS.constructor()') 7 | this.cssFactory = cssFactory 8 | } 9 | 10 | run (selector, style, widget) { 11 | let result = '' 12 | result += selector + ' {\n' 13 | result += this.cssFactory.getPosition(widget); 14 | if (widget.style.backgroundImage) { 15 | result += this.cssFactory.getRawStyle(widget.style, widget); 16 | } else { 17 | Logger.log(5, 'ImageCSS.run()', 'No background for image > ' + widget.name) 18 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 19 | if (widget?.has?.iconPlaceholder) { 20 | const s = Math.round(Math.min(widget.w, widget.h) * 0.3) 21 | result += ` font-size: ${s}px;\n`; 22 | } else { 23 | result += ` background-image:${this.getImagePlaceHolder(widget)};\n` 24 | result += ` background-size: 100% 100%;\n` 25 | result += ` border: 1px solid #333;\n` 26 | } 27 | } 28 | result += '}\n\n' 29 | 30 | return result 31 | } 32 | 33 | getImagePlaceHolder (widget) { 34 | try { 35 | var w = widget.w * 2; 36 | var h = widget.h * 2; 37 | var c = document.createElement("canvas"); 38 | var context = c.getContext("2d"); 39 | c.width = w; 40 | c.height = h; 41 | h += 0.5; 42 | w += 0.5; 43 | var n = 0.5; 44 | context.moveTo(n, n); 45 | context.lineTo(w, h); 46 | context.moveTo(w, n); 47 | context.lineTo(n, h); 48 | context.strokeStyle = "#333"; 49 | context.strokeWidth = 2; 50 | context.imageSmoothingEnabled = false; 51 | context.stroke(); 52 | let url = 'url(' + c.toDataURL("image/png") + ')'; 53 | return url 54 | } catch (err) { 55 | /** 56 | * can happen in jest 57 | */ 58 | Logger.log(5, 'ImageCSS.getImagePlaceHolder() error',) 59 | } 60 | 61 | } 62 | } -------------------------------------------------------------------------------- /src/qux/web/css/PagingCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class PaginCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'PaginCSS.constructor()') 7 | this.cssFactory = cssFactory 8 | } 9 | 10 | run (selector, style, widget) { 11 | let result = '' 12 | result += selector + ' {\n' 13 | result += this.cssFactory.getPosition(widget); 14 | switch (widget.props.justifyContent) { 15 | case 'left': 16 | result += ` justify-content: flex-start;\n` 17 | break; 18 | 19 | case 'right': 20 | result += ` justify-content: flex-end;\n` 21 | break 22 | 23 | case 'center': 24 | result += ` justify-content: center;\n` 25 | break 26 | 27 | default: 28 | result += ` justify-content: space-between;\n` 29 | break 30 | } 31 | result += '}\n\n' 32 | 33 | 34 | result += selector + ' .qux-paging-item {\n' 35 | result += ` width:${style.fontSize * 2}px;\n` 36 | result += ` height:100%;\n` 37 | result += ` background:${style.background};\n` 38 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.textProperties) 39 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 40 | result += '}\n\n' 41 | 42 | 43 | if (widget.hover) { 44 | let hover = widget.hover 45 | result += selector + ' .qux-paging-item:hover {\n' 46 | result += ` background:${hover.background};\n` 47 | result += this.cssFactory.getStyleByKey(hover, widget, this.cssFactory.textProperties) 48 | result += this.cssFactory.getStyleByKey(hover, widget, this.cssFactory.borderProperties) 49 | result += '}\n\n' 50 | } 51 | 52 | if (widget.active) { 53 | let active = widget.active 54 | result += selector + ' .qux-paging-item.qux-paging-item-active {\n' 55 | result += ` background:${active.background};\n` 56 | result += this.cssFactory.getStyleByKey(active, widget, this.cssFactory.textProperties) 57 | result += this.cssFactory.getStyleByKey(active, widget, this.cssFactory.borderProperties) 58 | result += '}\n\n' 59 | } 60 | 61 | 62 | return result 63 | } 64 | } -------------------------------------------------------------------------------- /src/qux/web/css/RichTextCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class RichTextCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'RichTextCSS()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += this.cssFactory.getRawStyle(style, widget); 17 | result += '}\n\n' 18 | 19 | if (widget.props && widget.props.richTextLabel) { 20 | let richText = widget.props.richTextLabel 21 | richText.forEach((child, i) => { 22 | if (child.style) { 23 | result += selector + ` .qux-rich-text-child-${i} {\n` 24 | result += this.cssFactory.getRawStyle(child.style, widget) 25 | result += '}\n\n' 26 | } 27 | }) 28 | } 29 | 30 | return result 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/qux/web/css/ScreenCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | import * as Util from '../../core/ExportUtil' 3 | 4 | export default class ScreenCSS { 5 | 6 | constructor(cssFactory) { 7 | Logger.log(5, 'ScreenCSS.constructor()') 8 | this.cssFactory = cssFactory 9 | this.imagePrefix = cssFactory.imagePrefix 10 | } 11 | 12 | run (selector, style, widget) { 13 | 14 | let result = '' 15 | result += selector + ' {\n' 16 | result += this.cssFactory.getPosition(widget); 17 | if (!Util.isOverlay(widget) || Util.hasOverlayBackground(widget)) { 18 | if (style.background) { 19 | if (style.background.colors) { 20 | let background = style.background 21 | let gradient = "(" + background.direction + "deg"; 22 | for (var i = 0; i < background.colors.length; i++) { 23 | var color = background.colors[i]; 24 | gradient += "," + color.c + " " + color.p + "% "; 25 | } 26 | gradient += ")"; 27 | result += ` background: linear-gradient${gradient};\n` 28 | } else { 29 | result += ` background-color: ${style.background};\n` 30 | } 31 | } 32 | if (style.backgroundColor) { 33 | result += ` background-color: ${style.backgroundColor};\n` 34 | } 35 | if (style.backgroundImage && style.backgroundImage.url) { 36 | if (style.backgroundImage.url.indexOf('http') === 0) { 37 | result += ` background-image: url(${style.backgroundImage.url});\n` 38 | } else { 39 | result += ` background-image: url(${this.imagePrefix}/${style.backgroundImage.url});\n` 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Check if we have full screen (height = 100%) or fixed size, component 46 | */ 47 | if (widget.isComponentScreen) { 48 | result += ` height:${widget.h}px;\n` 49 | result += ` width:${widget.w}px;\n` 50 | result += this.cssFactory.getStyleByKey(style, widget, ['boxShadow']) 51 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 52 | } else { 53 | result += ' height:100%;\n' 54 | } 55 | 56 | 57 | result += '}\n\n' 58 | return result 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /src/qux/web/css/SegmentCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class SegmentCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'SegmentCSS()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += '}\n\n' 17 | 18 | result += selector + ' .qux-segment-item {\n' 19 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.textProperties) 20 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 21 | result += this.cssFactory.getBackGround(style, widget) 22 | result += ` border-left: none;\n` 23 | result += ` border-radius: 0px;\n` 24 | result += '}\n\n' 25 | 26 | 27 | result += selector + ' .qux-segment-item:first-child {\n' 28 | result += ` border-left-color: ${style._borderLeftColor};\n` 29 | let borderLeftStyle = style._borderLeftStyle ? style._borderLeftStyle : 'solid' 30 | result += ` border-left-style: ${borderLeftStyle};\n` 31 | result += ` border-left-width: ${style._borderLeftWidth}px;\n` 32 | if (style._borderTopLeftRadius) { 33 | result += ` border-top-left-radius: ${style._borderTopLeftRadius}px;\n` 34 | } 35 | if (style._borderBottomLeftRadius) { 36 | result += ` border-bottom-left-radius: ${style._borderBottomLeftRadius}px;\n` 37 | } 38 | result += '}\n\n' 39 | 40 | 41 | result += selector + ' .qux-segment-item:last-child {\n' 42 | if (style._borderTopRightRadius) { 43 | result += ` border-top-right-radius: ${style._borderTopRightRadius}px;\n` 44 | } 45 | if (style._borderBottomRightRadius) { 46 | result += ` border-bottom-right-radius: ${style._borderBottomRightRadius}px;\n` 47 | } 48 | result += '}\n\n' 49 | 50 | 51 | if (widget.active) { 52 | let active = widget.active 53 | result += selector + ' .qux-segment-item.qux-segment-item-selected{\n' 54 | result += ` background:${active.background};\n` 55 | result += ` color:${active.color};\n` 56 | result += this.cssFactory.getStyleByKey(active, widget, this.cssFactory.borderColorProperties) 57 | result += '}\n\n' 58 | } 59 | 60 | return result 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/qux/web/css/SpinnerCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class SpinnerCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'SpinnerCSS()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.textProperties) 17 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 18 | result += '}\n\n' 19 | 20 | if (widget.hover) { 21 | let hover = widget.hover 22 | result += selector + ':hover {\n' 23 | result += ` color:${hover.color};\n` 24 | result += '}\n\n' 25 | } 26 | 27 | result += selector + ' .qux-spinner-option-cntr {\n' 28 | result += ` height:${Math.ceil(widget.h * 3)}px;\n` 29 | result += '}\n\n' 30 | 31 | result += selector + ' .qux-spinner-option {\n' 32 | result += ` height:${Math.floor(widget.h / 3)}px;\n` 33 | result += '}\n\n' 34 | 35 | 36 | result += selector + ' .qux-spinner-border {\n' 37 | result += ` border-top-color:${style.borderBoxColor};\n` 38 | result += ` border-bottom-color:${style.borderBoxColor};\n` 39 | result += '}\n\n' 40 | 41 | return result 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/qux/web/css/TimelineCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class VectorCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'Timeline()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | result += selector + ' {\n' 14 | result += this.cssFactory.getPosition(widget); 15 | if (style.elementSpacing === -1) { 16 | result += ` display:flex;\n` 17 | result += ` flex-direction: column;\n` 18 | result += ` justify-content: space-around;\n` 19 | } 20 | result += '}\n\n' 21 | 22 | let circleSize = style.circleSize - style.circleBorderWidth 23 | let left = (circleSize - style.lineWidth) / 2 + 1 24 | 25 | result += selector + ' .qux-timeline-line{\n' 26 | result += ` background:${style.lineBackground};\n` 27 | result += ` width:${style.lineWidth}px;\n` 28 | result += ` border-radius:${Math.round(style.lineWidth / 2)}px;\n` 29 | result += ` left:${Math.round(left)}px;\n` 30 | result += '}\n\n' 31 | 32 | if (style.elementSpacing > 0) { 33 | result += selector + ' .qux-timeline-element{\n' 34 | result += ` margin-top:${style.elementSpacing}px;\n` 35 | result += '}\n\n' 36 | } 37 | 38 | 39 | result += selector + ' .qux-timeline-element-selected .qux-timeline-circle{\n' 40 | result += ` background:${style.cicleActiveBackground};\n` 41 | result += ` border-color:${style.cicleActiveBorderColor};\n` 42 | result += '}\n\n' 43 | 44 | 45 | result += selector + ' .qux-timeline-element-selected .qux-timeline-label{\n' 46 | result += ` color:${style.cicleActiveTextColor};\n` 47 | result += '}\n\n' 48 | 49 | result += selector + ' .qux-timeline-label{\n' 50 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.textProperties) 51 | result += '}\n\n' 52 | 53 | 54 | result += selector + ' .qux-timeline-circle{\n' 55 | result += ` background:${style.cicleBackground};\n` 56 | result += ` width:${circleSize}px;\n` 57 | result += ` height:${circleSize}px;\n` 58 | result += ` border-color:${style.cicleBorderColor};\n` 59 | result += ` border-width:${style.circleBorderWidth}px;\n` 60 | result += '}\n\n' 61 | 62 | return result 63 | } 64 | 65 | } 66 | 67 | /** 68 | * "lineWidth": 8, 69 | "lineBackground": "#333333", 70 | "lineBorderColor": "#ffffff", 71 | "lineBorderWidth": 0, 72 | "circleSize": 32, 73 | "cicleBackground": "#ffffff", 74 | "cicleBorderColor": "#333333", 75 | "circleBorderWidth": 2, 76 | "elementSpacing": -1, 77 | "cicleActiveBackground": "#cccccc", 78 | "cicleActiveTextColor": "#cccccc", 79 | "cicleActiveBorderColor": "#333333" 80 | */ -------------------------------------------------------------------------------- /src/qux/web/css/UploadCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class UploadCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'UploadCSS()') 7 | this.cssFactory = cssFactory 8 | this.imagePrefix = cssFactory.imagePrefix 9 | } 10 | 11 | run (selector, style, widget) { 12 | let result = '' 13 | 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | result += this.cssFactory.getRawStyle(style, widget); 17 | result += '}\n\n' 18 | 19 | if (widget.hover) { 20 | let hover = widget.hover 21 | result += selector + ':hover {\n' 22 | result += ` background:${hover.background};\n` 23 | result += ` color:${hover.color};\n` 24 | result += this.cssFactory.getStyleByKey(hover, widget, this.cssFactory.borderColorProperties) 25 | result += '}\n\n' 26 | } 27 | 28 | if (widget.active) { 29 | let active = widget.active 30 | result += selector + '.qux-upload-has-files {\n' 31 | result += ` background:${active.background};\n` 32 | result += ` color:${active.color};\n` 33 | result += this.cssFactory.getStyleByKey(active, widget, this.cssFactory.borderColorProperties) 34 | result += '}\n\n' 35 | } 36 | 37 | return result 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /src/qux/web/css/UploadPreviewCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | 3 | export default class UploadPreviewCSS { 4 | 5 | constructor(cssFactory) { 6 | Logger.log(5, 'UploadPreviewCSS.constructor()') 7 | this.cssFactory = cssFactory 8 | } 9 | 10 | run (selector, style, widget) { 11 | let result = '' 12 | result += selector + ' {\n' 13 | result += this.cssFactory.getPosition(widget); 14 | result += this.cssFactory.getStyleByKey(style, widget, this.cssFactory.borderProperties) 15 | result += '}\n\n' 16 | return result 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /src/qux/web/css/VectorCSS.js: -------------------------------------------------------------------------------- 1 | import Logger from '../../core/Logger' 2 | import * as Util from '../../core/ExportUtil' 3 | 4 | export default class VectorCSS { 5 | 6 | constructor(cssFactory) { 7 | Logger.log(5, 'VectorCSS()') 8 | this.cssFactory = cssFactory 9 | this.imagePrefix = cssFactory.imagePrefix 10 | } 11 | 12 | run (selector, style, widget) { 13 | let result = '' 14 | result += selector + ' {\n' 15 | result += this.cssFactory.getPosition(widget); 16 | 17 | 18 | if (Util.isFixedHorizontal(widget)) { 19 | result += ` width:${this.cssFactory.getCorrectedWidth(widget, true)};\n` 20 | result += ` height:${this.cssFactory.getCorrectedHeight(widget, true)};\n` 21 | } 22 | 23 | if (style.backgroundImage && style.backgroundImage.url) { 24 | if (style.backgroundImage.url.indexOf('http') === 0) { 25 | result += ` background-image: url(${style.backgroundImage.url});\n` 26 | } else { 27 | result += ` background-image: url(${this.imagePrefix}/${style.backgroundImage.url});\n` 28 | } 29 | } 30 | result += '}\n\n' 31 | return result 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /tests/unit/CSSFactory.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import CSSFactory from '../../src/qux/core/CSSFactory' 3 | 4 | test('Test getGridColumnTracks', () => { 5 | 6 | 7 | let f = new CSSFactory(true, '', true).positionFactory 8 | let track = f.getGridColumnTracks(600, [ 9 | { v: 0, start: [], end: [], fixed: true, l: 100 }, 10 | { v: 48, start: [], end: [], fixed: true, l: 100 }, 11 | { v: 234, start: [], end: [], fixed: false, l: 200 } 12 | ]) 13 | expect(track).toBe('100px 100px minmax(0,1fr)') 14 | 15 | track = f.getGridColumnTracks(600, [ 16 | { v: 0, start: [], end: [], fixed: false, l: 150 }, 17 | { v: 48, start: [], end: [], fixed: true, l: 100 }, 18 | { v: 234, start: [], end: [], fixed: false, l: 200 } 19 | ]) 20 | expect(track).toBe('25% 100px minmax(0,1fr)') 21 | }); 22 | 23 | 24 | 25 | test('Test getGridRowTracks', () => { 26 | 27 | let f = new CSSFactory(true, '', true).positionFactory 28 | let track = f.getGridRowTracks(600, [ 29 | { v: 0, start: [{}], end: [], fixed: false, l: 50 }, 30 | { v: 50, start: [{}, {}], end: [], fixed: false, l: 440 }, 31 | { v: 490, start: [], end: [{},{}], fixed: false, l: 230 } 32 | ]) 33 | expect(track).toBe('50px minmax(440px, auto) 1fr') 34 | 35 | }); 36 | 37 | -------------------------------------------------------------------------------- /tests/unit/CSSGridFixed.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import gridFixedTest from './data/gridFixedTest.json' 3 | import * as TestUtil from './TestUtil' 4 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 5 | import CSSFactory from '../../src/qux/core/CSSFactory' 6 | 7 | test('Test Horizontal', async () => { 8 | 9 | /** 10 | * first call figma service to check taht all the boolean stuff and so is ignored 11 | */ 12 | 13 | let t = new ModelTransformer(gridFixedTest) 14 | let model = t.transform() 15 | 16 | expect(model).not.toBeNull() 17 | 18 | 19 | let compressed = new CSSOptimizer().runTree(model) 20 | let classes = new CSSFactory().generate(compressed) 21 | 22 | // no template 23 | let pinned = TestUtil.findCSSBySelector(classes, '.CntrLeftPinned')[0] 24 | assertCSS(pinned, 'grid-template-columns: 80px minmax(0,1fr) 80px minmax(0,1fr) 80px minmax(0,1fr) 16%;') 25 | 26 | let rel = TestUtil.findCSSBySelector(classes, '.CntrRel')[0] 27 | assertCSS(rel, 'grid-template-columns: 6.7% minmax(0,1fr) 6.7% minmax(0,1fr) 6.7% minmax(0,1fr) 16%;') 28 | 29 | let fixed = TestUtil.findCSSBySelector(classes, '.CntrFixed')[0] 30 | assertCSS(fixed, 'grid-template-columns: 6.7% 256px 6.7% 256px 6.7% 256px minmax(0,1fr)') 31 | 32 | 33 | }); 34 | 35 | 36 | export function assertCSS (file, statementToFind) { 37 | expect(file.code.indexOf(statementToFind)).toBeGreaterThanOrEqual(0) 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/unit/CSSGridInsteadRowBug.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import app from './data/rowAndGridErrors.json' 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 7 | import CSSFactory from '../../src/qux/core/CSSFactory' 8 | 9 | test('Test Grid', () => { 10 | 11 | let t = new ModelTransformer(app, { 12 | css: { 13 | grid: true 14 | } 15 | }) 16 | let tree = t.transform() 17 | 18 | expect(tree).not.toBeNull() 19 | expect(tree.screens.length).toBe(5) 20 | 21 | /** 22 | * Get the copy on the other screen and make sure we have the same width 23 | */ 24 | let q16 = TestUtil.findScreen(tree, 'Question16') 25 | expect(q16).not.toBeNull() 26 | 27 | /** 28 | * Now lets check what is wrong with the margins 29 | */ 30 | let compressed = new CSSOptimizer().runTree(tree) 31 | let classes = new CSSFactory().generate(compressed) 32 | 33 | let css16 = TestUtil.findCSSBySelector(classes, '.Question16')[0] 34 | 35 | /** They should have a grid, becasue there is a an overlap */ 36 | expect(css16).not.toBeNull() 37 | expect(css16.code.indexOf('display: grid')).toBeGreaterThan(0) 38 | 39 | 40 | }); 41 | 42 | test('Test Row', () => { 43 | 44 | /** 45 | * We remove the over lap, this will get a nice row layout 46 | */ 47 | delete app.widgets.w11938 48 | app.screens.s11924.children = app.screens.s11924.children.filter(c => c !== 'w11938') 49 | 50 | let t = new ModelTransformer(app, { 51 | css: { 52 | grid: true 53 | } 54 | }) 55 | let tree = t.transform() 56 | 57 | expect(tree).not.toBeNull() 58 | expect(tree.screens.length).toBe(5) 59 | 60 | /** 61 | * Get the copy on the other screen and make sure we have the same width 62 | */ 63 | let q16 = TestUtil.findScreen(tree, 'Question16') 64 | expect(q16).not.toBeNull() 65 | 66 | /** 67 | * Now lets check what is wrong with the margins 68 | */ 69 | let compressed = new CSSOptimizer().runTree(tree) 70 | let classes = new CSSFactory().generate(compressed) 71 | 72 | let css16 = TestUtil.findCSSBySelector(classes, '.Question16')[0] 73 | 74 | /** They should have a grid, becasue there is a an overlap */ 75 | expect(css16).not.toBeNull() 76 | expect(css16.code.indexOf('display: flex')).toBeGreaterThan(0) 77 | expect(css16.code.indexOf('display: grid')).toBe(-1) 78 | 79 | }); 80 | 81 | -------------------------------------------------------------------------------- /tests/unit/CSSMarginBug.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import app from './data/rowAndGridErrors.json' 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 7 | import CSSFactory from '../../src/qux/core/CSSFactory' 8 | import CSSPosition from '../../src/qux/core/CSSPosition' 9 | 10 | test('Test wrong margins', () => { 11 | 12 | /** 13 | * This error is caused because the screens have different widths! 14 | */ 15 | 16 | 17 | let t = new ModelTransformer(app, { 18 | css: { 19 | grid: true 20 | } 21 | }) 22 | let tree = t.transform() 23 | 24 | expect(tree).not.toBeNull() 25 | expect(tree.screens.length).toBe(5) 26 | 27 | /** 28 | * Get the copy on the other screen and make sure we have the same width 29 | */ 30 | let q2 = TestUtil.findScreen(tree, 'Question2') 31 | let e1 = TestUtil.findOneElementsByName(q2, 'A1-1') 32 | 33 | let q3 = TestUtil.findScreen(tree, 'Question3') 34 | let e2 = TestUtil.findOneElementsByName(q3, 'A1-1') 35 | 36 | expect(e1.x).toBe(e2.x) 37 | expect(e1.top).toBe(e2.top) 38 | expect(e1.w).toBe(e2.w) 39 | 40 | expect(e2.x).toBe(271) 41 | expect(e2.top).toBe(40) 42 | 43 | 44 | let position = new CSSPosition({}) 45 | expect(position.getResponsiveRight(e1)).toBe(position.getResponsiveRight(e2)) 46 | expect(position.getResponsiveLeft(e1)).toBe(position.getResponsiveLeft(e2)) 47 | 48 | 49 | /** 50 | * Now lets check what is wrong with the margins 51 | */ 52 | let compressed = new CSSOptimizer().runTree(tree) 53 | let classes = new CSSFactory().generate(compressed) 54 | 55 | let css1 = TestUtil.findCSSBySelector(classes, '.Question2 .A1-1')[0] 56 | let css2 = TestUtil.findCSSBySelector(classes, '.Question3 .A1-1')[0] 57 | 58 | /** They should have the same margins! */ 59 | expect(css1).not.toBeNull() 60 | expect(css1.code.indexOf('margin-right: 9%')).toBeGreaterThan(0) 61 | expect(css1.code.indexOf('margin-left: 72%;')).toBeGreaterThan(0) 62 | 63 | 64 | expect(css2).not.toBeNull() 65 | expect(css2.code.indexOf('margin-right: 9%')).toBeGreaterThan(0) 66 | expect(css2.code.indexOf('margin-left: 72%;')).toBeGreaterThan(0) 67 | 68 | }); 69 | 70 | -------------------------------------------------------------------------------- /tests/unit/CSSMinMaxCenter.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import app from './data/minMaxCenter.json' 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 7 | import CSSFactory from '../../src/qux/core/CSSFactory' 8 | import CSSPosition from '../../src/qux/core/CSSPosition' 9 | 10 | test('Test min max center', () => { 11 | 12 | /** 13 | * This error is caused because the screens have different widths! 14 | */ 15 | 16 | 17 | let t = new ModelTransformer(app, { 18 | css: { 19 | grid: true 20 | } 21 | }) 22 | let tree = t.transform() 23 | 24 | expect(tree).not.toBeNull() 25 | expect(tree.screens.length).toBe(4) 26 | 27 | /** 28 | * Now lets check what is wrong with the margins 29 | */ 30 | let compressed = new CSSOptimizer().runTree(tree) 31 | let classes = new CSSFactory().generate(compressed) 32 | 33 | let minMaxCenter = TestUtil.findCSSBySelector(classes, '.MinMaxCenterCntr')[0] 34 | let fixedCenter = TestUtil.findCSSBySelector(classes, '.FixedCenterCntr')[0] 35 | let reponsiveCenter = TestUtil.findCSSBySelector(classes, '.ResponsiveCenterCntr')[0] 36 | 37 | /** They should have the same margins! */ 38 | expect(minMaxCenter).not.toBeNull() 39 | expect(minMaxCenter.code.indexOf('margin-right: auto')).toBeGreaterThan(0) 40 | expect(minMaxCenter.code.indexOf('margin-left: auto;')).toBeGreaterThan(0) 41 | expect(minMaxCenter.code.indexOf('width: 70%')).toBeGreaterThan(0) 42 | expect(minMaxCenter.code.indexOf('min-width: 400px')).toBeGreaterThan(0) 43 | expect(minMaxCenter.code.indexOf('max-width: 1200px')).toBeGreaterThan(0) 44 | 45 | expect(fixedCenter).not.toBeNull() 46 | expect(fixedCenter.code.indexOf('margin-right: auto')).toBeGreaterThan(0) 47 | expect(fixedCenter.code.indexOf('margin-left: auto;')).toBeGreaterThan(0) 48 | expect(fixedCenter.code.indexOf('width: 70%')).toBeGreaterThan(0) 49 | 50 | expect(reponsiveCenter).not.toBeNull() 51 | expect(reponsiveCenter.code.indexOf('margin-right: auto')).toBeGreaterThan(0) 52 | expect(reponsiveCenter.code.indexOf('margin-left: auto;')).toBeGreaterThan(0) 53 | expect(reponsiveCenter.code.indexOf('width: 70%')).toBeGreaterThan(-1) 54 | 55 | }); -------------------------------------------------------------------------------- /tests/unit/CSSTemlateBug.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 3 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 4 | import CSSFactory from '../../src/qux/core/CSSFactory' 5 | import * as TestUtil from './TestUtil' 6 | import app from './data/cssTemplatePaddingBug.json' 7 | 8 | test('Test Nest Groups', () => { 9 | 10 | 11 | let t = new ModelTransformer(app) 12 | let tree = t.transform() 13 | 14 | let compressed = new CSSOptimizer().runTree(tree) 15 | let classes = new CSSFactory().generate(compressed) 16 | 17 | // no template 18 | let okBox = TestUtil.findCSSBySelector(classes, '.OKBox')[0] 19 | expect(okBox).not.toBeNull() 20 | expect(okBox.code.indexOf('min-height: 32px;')).toBeGreaterThan(0) 21 | 22 | // template 23 | let buggyBox1 = TestUtil.findCSSBySelector(classes, '.BuggyBox1')[0] 24 | expect(buggyBox1).not.toBeNull() 25 | expect(buggyBox1.code.indexOf('min-height: 32px;')).toBeGreaterThan(0) 26 | 27 | // template and pading overwrite 28 | let buggyBox2 = TestUtil.findCSSBySelector(classes, '.BuggyBox2')[0] 29 | expect(buggyBox2).not.toBeNull() 30 | expect(buggyBox2.code.indexOf('min-height: 24px;')).toBeGreaterThan(0) 31 | 32 | 33 | // template with fixed height 34 | let templateBoxFixed1 = TestUtil.findCSSBySelector(classes, '.TemplateBoxFixed1')[0] 35 | expect(templateBoxFixed1).not.toBeNull() 36 | expect(templateBoxFixed1.code.indexOf('height: 32px;')).toBeGreaterThan(0) 37 | 38 | // template width fixed heigth and padding overwrite 39 | let templateBoxFixed2 = TestUtil.findCSSBySelector(classes, '.TemplateBoxFixed2')[0] 40 | expect(templateBoxFixed2).not.toBeNull() 41 | expect(templateBoxFixed2.code.indexOf('height: 16px;')).toBeGreaterThan(0) // 64 - (2 * 24) 42 | 43 | // template with fixed height 44 | let fixedBox = TestUtil.findCSSBySelector(classes, '.FixedBox')[0] 45 | expect(fixedBox).not.toBeNull() 46 | expect(fixedBox.code.indexOf('height: 32px;')).toBeGreaterThan(0) 47 | 48 | 49 | 50 | // make sure the templates do no have height or so 51 | let redTemplateBox = TestUtil.findCSSBySelector(classes, 'RedTemplateBox')[0] 52 | expect(redTemplateBox).not.toBeNull() 53 | expect(redTemplateBox.code.indexOf('min-height')).toBe(-1) 54 | 55 | let whiteTemplate = TestUtil.findCSSBySelector(classes, 'WhiteBoxTemplate')[0] 56 | expect(whiteTemplate).not.toBeNull() 57 | expect(whiteTemplate.code.indexOf('min-height')).toBe(-1) 58 | console.debug(classes) 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /tests/unit/CanHaveChildren.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import canHaveChildren from './data/canHaveChildren.json' 3 | import canHaveChildren2 from './data/canHaveChildren2.json' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Rings', () => { 7 | 8 | let t = new ModelTransformer(canHaveChildren, { 9 | css: { 10 | grid: true 11 | } 12 | }) 13 | let model = t.transform() 14 | 15 | expect(model).not.toBeNull() 16 | 17 | let screen = model.screens[0] 18 | expect(screen.children.length).toBe(4) 19 | expect(screen.children[0].type).toBe('RingChart') 20 | expect(screen.children[1].type).toBe('RingChart') 21 | expect(screen.children[2].children.length).toBe(1) 22 | expect(screen.children[2].type).toBe('Button') 23 | expect(screen.children[3].children.length).toBe(1) 24 | expect(screen.children[3].type).toBe('Image') 25 | 26 | }); 27 | 28 | test('Test Header', () => { 29 | 30 | let t = new ModelTransformer(canHaveChildren2, { 31 | css: { 32 | grid: true 33 | } 34 | }) 35 | let model = t.transform() 36 | 37 | expect(model).not.toBeNull() 38 | 39 | let screen = model.screens[0] 40 | TestUtil.print(screen) 41 | 42 | expect(screen.children.length).toBe(1) 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /tests/unit/DataBinding_Default_Bug.spec.js: -------------------------------------------------------------------------------- 1 | import figmaTodoexample from './data/figmaTodoexample.json' 2 | 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Default DataBinding', async () => { 7 | 8 | 9 | let t = new ModelTransformer(figmaTodoexample, { 10 | css: { 11 | grid: true, 12 | attachLabels: true 13 | }, 14 | addDefaultDatabinding: true 15 | }) 16 | let model = t.transform() 17 | 18 | expect(model).not.toBeNull() 19 | 20 | let newToDo = model.screens.find(s => s.name === 'NewTodo') 21 | console.debug(TestUtil.print(newToDo)) 22 | 23 | let detailTextArea = TestUtil.findOneElementsByName(newToDo, 'DetailTextArea') 24 | expect(detailTextArea.props.databinding.default).toBe('newTodo.details') 25 | 26 | let searchBox = TestUtil.findOneElementsByName(newToDo, 'SearchBox') 27 | expect(searchBox.props.databinding.default).toBe('newTodo.name') 28 | 29 | 30 | }); -------------------------------------------------------------------------------- /tests/unit/DoubleNameBug.spec.js: -------------------------------------------------------------------------------- 1 | import DoubleNameBug from './data/DoubleNameBug.json' 2 | 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Emdedding', async () => { 7 | 8 | let t = new ModelTransformer(DoubleNameBug, { 9 | css: { 10 | grid: true 11 | } 12 | }) 13 | let model = t.transform() 14 | 15 | expect(model).not.toBeNull() 16 | 17 | let screen = model.screens[0] 18 | console.debug(TestUtil.print(screen, e => e.cssSelector)) 19 | 20 | let shouldBeEmpty = TestUtil.findOneElementsByProp(screen, '.Contact .Contact', 'cssSelector',) 21 | expect(shouldBeEmpty).toBeUndefined() 22 | 23 | let found1 = TestUtil.findOneElementsByProp(screen, '.Contact .Contact_0', 'cssSelector') 24 | expect(found1).not.toBeNull() 25 | expect(found1).not.toBeUndefined() 26 | 27 | 28 | let found2 = TestUtil.findOneElementsByProp(screen, '.Contact .Contact_2', 'cssSelector') 29 | expect(found2).not.toBeNull() 30 | expect(found2).not.toBeUndefined() 31 | }); -------------------------------------------------------------------------------- /tests/unit/Embedding.spec.js: -------------------------------------------------------------------------------- 1 | import app from './data/embedding.json' 2 | 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Emdedding', async () => { 7 | 8 | let t = new ModelTransformer(app, { 9 | css: { 10 | grid: true 11 | } 12 | }, 'LoginBox') 13 | let model = t.transform() 14 | 15 | expect(model).not.toBeNull() 16 | expect(model.screens.length).toBe(1) 17 | 18 | let screen = model.screens[0] 19 | expect(screen.isComponentScreen).toBe(true) 20 | 21 | console.debug(TestUtil.print(screen)) 22 | 23 | 24 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Animation_Props.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaSwitch from './data/figmaSwitch.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Figma Animation Props', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaSwitch.id, figmaSwitch) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | expect(model.screens.length).toBe(2) 29 | 30 | let componentScreen = model.screens[1] 31 | expect(componentScreen.isComponentScreen).toBe(true) 32 | expect(componentScreen.children.length).toBe(1) 33 | expect(componentScreen.children[0].children.length).toBe(2) 34 | 35 | let screen = model.screens[0] 36 | console.debug(TestUtil.print(screen, e => e.props.figmaAnimation ? ' - Animation' : " - X")) 37 | 38 | 39 | 40 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Auto.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayout from './data/figmaAutoFixed.json' 4 | import * as Util from '../../src/qux/core/ExportUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Figma Auto Fixed', async () => { 10 | 11 | /** 12 | * first call figma service to check taht all the boolean stuff and so is ignored 13 | */ 14 | let figmaService = new FigmaService() 15 | figmaService.setDownloadVectors(false) 16 | let app = await figmaService.parse(figmaAutoLayout.id, figmaAutoLayout) 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | 28 | expect(model).not.toBeNull() 29 | 30 | let screen = model.screens[0] 31 | 32 | console.debug(TestUtil.print(screen)) 33 | 34 | let cntr1 = TestUtil.findElementsByName(screen, 'AutoContainer')[0] 35 | expect(Util.isFixedHorizontal(cntr1)).toBe(false) 36 | 37 | let cntr2 = TestUtil.findElementsByName(screen, 'AutoContainer2')[0] 38 | expect(Util.isFixedHorizontal(cntr2)).toBe(false) 39 | 40 | 41 | 42 | let fixed1 = TestUtil.findElementsByName(screen, 'Fixed1')[0] 43 | expect(fixed1).not.toBeNull() 44 | expect(Util.isFixedHorizontal(fixed1)).toBe(true) 45 | expect(Util.isLayoutGrow(fixed1)).toBe(false) 46 | 47 | let grow = TestUtil.findElementsByName(screen, 'Grow')[0] 48 | expect(grow).not.toBeNull() 49 | expect(Util.isFixedHorizontal(grow)).toBe(false) 50 | expect(Util.isLayoutGrow(grow)).toBe(true) 51 | 52 | 53 | 54 | let compressed = new CSSOptimizer().runTree(model) 55 | let classes = new CSSFactory().generate(compressed) 56 | 57 | // test css a little 58 | let growCSS = TestUtil.findCSSBySelector(classes, '.Grow')[0] 59 | expect(growCSS.code.indexOf('flex-grow: 1')).toBeGreaterThanOrEqual(0) 60 | 61 | let fixedCSS = TestUtil.findCSSBySelector(classes, '.Fixed1')[0] 62 | expect(fixedCSS.code.indexOf('flex-grow: 1')).toBe(-1) 63 | 64 | }); 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/unit/Figma_Auto_Fixed_Children.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoFixedChildren from './data/figmaAutoFixedChildren.json' 4 | 5 | 6 | import * as Util from '../../src/qux/core/ExportUtil' 7 | import Logger from '../../src/qux/core/Logger' 8 | import * as TestUtil from './TestUtil' 9 | 10 | test('Test Figma Auto Fixed Childen', async () => { 11 | Logger.logLevel = 1 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaAutoFixedChildren.id, figmaAutoFixedChildren) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, { 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | let screen = model.screens.find(s => s.name === 'why') 29 | 30 | let fixed = TestUtil.findOneElementsByName(screen, 'Fixed') 31 | expect(Util.isFixedHorizontal(fixed)).toBe(false) 32 | 33 | let css = TestUtil.generateCSS(model) 34 | 35 | expect(TestUtil.hasCSSBySelector(css, '.Fill', 'width: 100%;')).toBe(true) 36 | expect(TestUtil.hasCSSBySelector(css, '.Fixed', 'width: 100%;')).toBe(true) 37 | 38 | }); 39 | 40 | test('Test Figma Auto Fixed Childen 2', async () => { 41 | Logger.logLevel = 1 42 | 43 | /** 44 | * first call figma service to check taht all the boolean stuff and so is ignored 45 | */ 46 | let figmaService = new FigmaService('', {figma:{fixed2Fill: false}}) 47 | figmaService.setDownloadVectors(false) 48 | let app = await figmaService.parse(figmaAutoFixedChildren.id, figmaAutoFixedChildren) 49 | 50 | /** 51 | * Check if transform works correctly 52 | */ 53 | let t = new ModelTransformer(app, { 54 | }) 55 | let model = t.transform() 56 | 57 | expect(model).not.toBeNull() 58 | 59 | let css = TestUtil.generateCSS(model) 60 | expect(TestUtil.hasCSSBySelector(css, '.Fill', 'width: 100%;')).toBe(true) 61 | expect(TestUtil.hasCSSBySelector(css, '.Fixed', 'width: 100%;')).toBe(false) 62 | expect(TestUtil.hasCSSBySelector(css, '.Fixed', 'width: 366px;')).toBe(true) 63 | }); 64 | -------------------------------------------------------------------------------- /tests/unit/Figma_Auto_Hug.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayoutHug from './data/figmaAutoLayoutHug.json' 4 | 5 | 6 | import * as Util from '../../src/qux/core/ExportUtil' 7 | import Logger from '../../src/qux/core/Logger' 8 | import * as TestUtil from './TestUtil' 9 | 10 | test('Test Figma Auto Fixed Cenrer', async () => { 11 | Logger.logLevel = 1 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaAutoLayoutHug.id, figmaAutoLayoutHug) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, { 24 | css: { 25 | grid: true 26 | } 27 | }) 28 | let model = t.transform() 29 | 30 | expect(model).not.toBeNull() 31 | let screen = model.screens.find(s => s.name === 'Home') 32 | console.debug(TestUtil.print(screen)) 33 | 34 | let fix = TestUtil.findOneElementsByName(screen, 'FixedButton') 35 | expect(fix.w).toBe(130) 36 | expect(Util.isFixedHorizontal(fix)).toBe(true) 37 | expect(Util.isHugHorizontal(fix)).toBe(false) 38 | 39 | 40 | let hug = TestUtil.findOneElementsByName(screen, 'HugButton') 41 | console.debug('XXXX', hug.props) 42 | expect(hug.w).toBe(141) 43 | expect(Util.isFixedHorizontal(hug)).toBe(true) 44 | expect(Util.isHugHorizontal(hug)).toBe(true) 45 | 46 | 47 | 48 | 49 | }); 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /tests/unit/Figma_Auto_Padding.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoMixedPadding from './data/figmaAutoMixedPadding.json' 4 | import * as TestUtil from './TestUtil' 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | 7 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 8 | import CSSFactory from '../../src/qux/core/CSSFactory' 9 | 10 | 11 | test('Test Auto Padding', async () => { 12 | 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaAutoMixedPadding.id, figmaAutoMixedPadding) 16 | 17 | let t = new ModelTransformer(app) 18 | let model = t.transform() 19 | expect(model).not.toBeNull() 20 | 21 | let screen = model.screens.find(s => s.name === 'Screen') 22 | let auto = TestUtil.findOneElementsByName(screen, 'Auto') 23 | expect(auto.layout.paddingTop).toBe(16) 24 | expect(auto.layout.paddingBottom).toBe(30) 25 | 26 | 27 | let left = TestUtil.findOneElementsByName(screen, 'Left') 28 | expect(left.w).toBe(100) 29 | expect(Util.isFixedHorizontal(left)).toBe(true) 30 | expect(Util.isHugHorizontal(left)).toBe(false) 31 | 32 | let compressed = new CSSOptimizer().runTree(model) 33 | let classes = new CSSFactory().generate(compressed) 34 | 35 | // test css a little 36 | let cssLeft = TestUtil.findCSSBySelector(classes, '.Screen .Left')[0] 37 | console.debug(cssLeft.code) 38 | assertCSS(cssLeft, 'width: 100px') 39 | 40 | }); 41 | 42 | function assertCSS (file, statementToFind) { 43 | expect(file.code.indexOf(statementToFind)).toBeGreaterThanOrEqual(0) 44 | } 45 | 46 | function assertNotCSS (file, statementToFind) { 47 | expect(file.code.indexOf(statementToFind)).toBe(-1) 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /tests/unit/Figma_Auto_Root_Fill.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoRootFill from './data/figmaAutoRootFill.json' 4 | import * as TestUtil from './TestUtil' 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | 7 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 8 | import CSSFactory from '../../src/qux/core/CSSFactory' 9 | 10 | 11 | test('Test Auto Root Fill Width', async () => { 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaAutoRootFill.id, figmaAutoRootFill) 19 | 20 | let t = new ModelTransformer(app) 21 | let model = t.transform() 22 | expect(model).not.toBeNull() 23 | 24 | let autoScreen = model.screens.find(s => s.name === 'AutoScreen') 25 | let recFill = TestUtil.findOneElementsByName(autoScreen, 'RecFill') 26 | let recFixed = TestUtil.findOneElementsByName(autoScreen, 'RecFixed') 27 | expect(Util.isFixedHorizontal(recFixed)).toBe(true) 28 | expect(Util.isFixedHorizontal(recFill)).toBe(false) 29 | 30 | 31 | let absScreen = model.screens.find(s => s.name === 'AbsScreen') 32 | let recFill2 = TestUtil.findOneElementsByName(absScreen, 'RecFill') 33 | let recFixed2 = TestUtil.findOneElementsByName(absScreen, 'RecFixed') 34 | expect(Util.isFixedHorizontal(recFixed2)).toBe(true) 35 | expect(Util.isFixedHorizontal(recFill2)).toBe(false) 36 | 37 | 38 | let compressed = new CSSOptimizer().runTree(model) 39 | let classes = new CSSFactory().generate(compressed) 40 | 41 | // test css a little 42 | let cssRecFill = TestUtil.findCSSBySelector(classes, '.AutoScreen .RecFill')[0] 43 | console.debug(cssRecFill.code) 44 | assertCSS(cssRecFill, 'width: 100%') 45 | assertNotCSS(cssRecFill, 'box-sizing: border-box') // this should be assertNotCSS 46 | // assertCSS(cssRecFill, 'calc(100%') could have been minus 0 47 | 48 | let cssRecFill2 = TestUtil.findCSSBySelector(classes, '.AbsScreen .RecFill')[0] 49 | assertCSS(cssRecFill2, 'width: 100%') 50 | 51 | }); 52 | 53 | function assertCSS (file, statementToFind) { 54 | expect(file.code.indexOf(statementToFind)).toBeGreaterThanOrEqual(0) 55 | } 56 | 57 | function assertNotCSS (file, statementToFind) { 58 | expect(file.code.indexOf(statementToFind)).toBe(-1) 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /tests/unit/Figma_Auto_Screen.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayoutScreen from './data/figmaAutoLayoutScreen.json' 4 | import figmaAutoLayoutScreenWrapped from './data/figmaAutoLayoutScreenWrapped.json' 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | import Logger from '../../src/qux/core/Logger' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Figma Auto Screen', async () => { 10 | Logger.logLevel = 1 11 | 12 | /** 13 | * first call figma service to check taht all the boolean stuff and so is ignored 14 | */ 15 | let figmaService = new FigmaService() 16 | figmaService.setDownloadVectors(false) 17 | let app = await figmaService.parse(figmaAutoLayoutScreen.id, figmaAutoLayoutScreen) 18 | 19 | /** 20 | * Check if transform works correctly 21 | */ 22 | let t = new ModelTransformer(app, { 23 | css: { 24 | grid: true 25 | } 26 | }) 27 | let model = t.transform() 28 | 29 | expect(model).not.toBeNull() 30 | let screen = model.screens[0] 31 | expect(Util.isLayoutAutoHorizontal(screen)).toBe(true) 32 | 33 | let css = TestUtil.generateCSS(model) 34 | expect(TestUtil.hasCSSBySelector(css, '.qux-screen.metaHeader', 'padding-left: 64px;')).toBe(true) 35 | 36 | }); 37 | 38 | 39 | 40 | 41 | test('Test Figma Auto Screen Wrapped', async () => { 42 | Logger.logLevel = 1 43 | 44 | /** 45 | * first call figma service to check taht all the boolean stuff and so is ignored 46 | */ 47 | let figmaService = new FigmaService() 48 | figmaService.setDownloadVectors(false) 49 | let app = await figmaService.parse(figmaAutoLayoutScreenWrapped.id, figmaAutoLayoutScreenWrapped) 50 | 51 | /** 52 | * Check if transform works correctly 53 | */ 54 | let t = new ModelTransformer(app, { 55 | css: { 56 | grid: true 57 | } 58 | }) 59 | let model = t.transform() 60 | expect(model).not.toBeNull() 61 | 62 | let screen = model.screens[0] 63 | expect(Util.isLayoutAutoHorizontal(screen)).toBe(false) 64 | expect(Util.isLayoutAutoHorizontal(screen.children[0])).toBe(true) 65 | 66 | let css = TestUtil.generateCSS(model) 67 | expect(TestUtil.hasCSSBySelector(css, '.metaHeader', 'padding-left: 64px;')).toBe(true) 68 | expect(TestUtil.hasCSSBySelector(css, '.metaHeader', 'width: 896px;')).toBe(true) 69 | }); 70 | -------------------------------------------------------------------------------- /tests/unit/Figma_Blog.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaBlog from './data/figmaBlog.json' 4 | import * as Util from '../../src/qux/core/ExportUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Bounding Box error', async () => { 10 | 11 | /** 12 | * first call figma service to check taht all the boolean stuff and so is ignored 13 | */ 14 | let figmaService = new FigmaService() 15 | figmaService.setDownloadVectors(false) 16 | let app = await figmaService.parse(figmaBlog.id, figmaBlog) 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | 28 | expect(model).not.toBeNull() 29 | 30 | //let screen = model.screens[0] 31 | //console.debug(TestUtil.print(screen)) 32 | 33 | 34 | //let compressed = new CSSOptimizer().runTree(model) 35 | //let classes = new CSSFactory().generate(compressed) 36 | //expect(classes).not.toBeNull(classes) 37 | // expect(classes).not.toBeUndefined(classes) 38 | 39 | }); 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/unit/Figma_Blog2.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaBlog2 from './data/figmaBlog2.json' 4 | import * as Util from '../../src/qux/core/ExportUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test missing grid in entry', async () => { 10 | 11 | /** 12 | * first call figma service to check taht all the boolean stuff and so is ignored 13 | */ 14 | let figmaService = new FigmaService() 15 | figmaService.setDownloadVectors(false) 16 | let app = await figmaService.parse(figmaBlog2.id, figmaBlog2) 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | 28 | expect(model).not.toBeNull() 29 | 30 | let screen = model.screens[0] 31 | console.debug(TestUtil.print(screen)) 32 | 33 | let entry = TestUtil.findElementsByName(screen, 'Entry')[0] 34 | expect(Util.isLayoutGrid(entry)).toBe(true) 35 | console.debug(entry.layout) 36 | 37 | 38 | //let compressed = new CSSOptimizer().runTree(model) 39 | //let classes = new CSSFactory().generate(compressed) 40 | //expect(classes).not.toBeNull(classes) 41 | //expect(classes).not.toBeUndefined(classes) 42 | 43 | }); 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/unit/Figma_Children_Toggle.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaChildrenToggle from './data/figmaChildrenToggle.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Wrong Nesting', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaChildrenToggle.id, figmaChildrenToggle) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | 32 | expect(screen.children.length).toBe(2) 33 | 34 | let checkBoxBorder = TestUtil.findElementsByName(screen, 'CheckBoxBorder')[0] 35 | expect(checkBoxBorder).not.toBeNull() 36 | expect(checkBoxBorder.children.length).toBe(1) 37 | expect(checkBoxBorder.qtype).toBe('qChildrenToggle') 38 | 39 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_DropDown.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaDropDown from './data/figmaDropDown.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test DropDown', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaDropDown.id, figmaDropDown) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | console.debug(TestUtil.print(screen)) 32 | 33 | let dropDown = TestUtil.findOneElementsByName(screen, 'DropDown') 34 | 35 | expect(dropDown.type).toBe('DropDown') 36 | expect(dropDown.props.options[0]).toBe('aaaa') 37 | expect(dropDown.props.options[3]).toBe('dddd') 38 | expect(dropDown.props.label).toBe('Klaus Drop Down') 39 | 40 | let frameDropDown = TestUtil.findOneElementsByName(screen, 'FrameDropDown') 41 | expect(frameDropDown.type).toBe('DropDown') 42 | expect(frameDropDown.props.options[0]).toBe('f1') 43 | expect(frameDropDown.props.options[3]).toBe('f4') 44 | expect(frameDropDown.props.label).toBe('FrameDropDown') 45 | 46 | 47 | let dataDropDown = TestUtil.findOneElementsByName(screen, 'DataDropDown') 48 | expect(dataDropDown.type).toBe('DropDown') 49 | expect(dataDropDown.props.databinding.default).toBe('selected') 50 | expect(dataDropDown.props.databinding.options).toBe('options') 51 | 52 | 53 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Dynamic.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaDynamicToggle from './data/figmaDynamicToggle.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Figma Dynamic', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaDynamicToggle.id, figmaDynamicToggle) 16 | 17 | 18 | /** 19 | * Make sure we have two screens and four widgets 20 | */ 21 | expect(Object.values(app.screens).length).toBe(2) 22 | expect(Object.values(app.widgets).length).toBe(10) 23 | expect(Object.values(app.lines).length).toBe(3) // there is one line from the instance to the component 24 | 25 | let instance = Object.values(app.widgets).find(w => w.name === 'Instance 1') 26 | expect(instance).not.toBeUndefined() 27 | expect(instance.type).toBe('DynamicContainer') 28 | expect(instance.props.dynamicChildren.length).toBe(2) 29 | expect(instance.props.dynamicStart).not.toBeUndefined() 30 | expect(instance.props.dynamicLines).not.toBeUndefined() 31 | expect(instance.props.dynamicLines.length).toBe(2) 32 | 33 | let comp1 = Object.values(app.widgets).find(w => w.name === 'Component 1-ToggleOn') 34 | expect(comp1).not.toBeUndefined() 35 | expect(comp1.props.dataValue).toBe('true') 36 | 37 | let comp2 = Object.values(app.widgets).find(w => w.name === 'Component 1-ToggleOff') 38 | expect(comp2).not.toBeUndefined() 39 | expect(comp2.props.dataValue).toBe('false') 40 | 41 | /** 42 | * Check if transform works correctly 43 | */ 44 | let t = new ModelTransformer(app, { 45 | css: { 46 | grid: true 47 | } 48 | }) 49 | let model = t.transform() 50 | 51 | expect(model).not.toBeNull() 52 | expect(model.screens.length).toBe(2) 53 | 54 | let componentScreen = model.screens[0] 55 | 56 | expect(componentScreen.isComponentScreen).toBe(true) 57 | expect(componentScreen.children.length).toBe(1) 58 | expect(componentScreen.children[0].children.length).toBe(2) 59 | 60 | let screen = model.screens[1] 61 | console.debug(TestUtil.print(screen)) 62 | expect(screen.isComponentScreen).toBeUndefined() 63 | expect(screen.children.length).toBe(1) 64 | 65 | let instance2 = screen.children[0] 66 | // make sure elements are inlined 67 | expect(instance2.type).toBe('DynamicContainer') 68 | expect(instance2.children.length).toBe(2) 69 | //console.debug(TestUtil.print(componentScreen)) 70 | //console.debug(TestUtil.print(screen)) 71 | 72 | 73 | 74 | 75 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Fixed_TopDown.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayoutHug from './data/figmaAutoLayoutHug.json' 4 | 5 | 6 | import * as Util from '../../src/qux/core/ExportUtil' 7 | import Logger from '../../src/qux/core/Logger' 8 | import * as TestUtil from './TestUtil' 9 | import figmaFixedTopDown from './data/figmaFixedTopDown.json' 10 | 11 | test('Test Figma Fixed Top Down', async () => { 12 | Logger.logLevel = 1 13 | 14 | /** 15 | * first call figma service to check taht all the boolean stuff and so is ignored 16 | */ 17 | let figmaService = new FigmaService() 18 | figmaService.setDownloadVectors(false) 19 | let app = await figmaService.parse(figmaFixedTopDown.id, figmaFixedTopDown) 20 | 21 | /** 22 | * Check if transform works correctly 23 | */ 24 | let t = new ModelTransformer(app, { 25 | css: { 26 | grid: true 27 | } 28 | }) 29 | let model = t.transform() 30 | 31 | expect(model).not.toBeNull() 32 | let screen = model.screens.find(s => s.name === 'MobileMenu') 33 | console.debug(TestUtil.print(screen)) 34 | 35 | 36 | let cntr = TestUtil.findOneElementsByName(screen, 'Container') 37 | expect(Util.isPinnedUp(cntr)).toBe(true) 38 | expect(Util.isPinnedUp(cntr)).toBe(true) 39 | expect(cntr.top).toBe(0) 40 | expect(cntr.bottom).toBe(0) 41 | console.debug(cntr.bottom) 42 | 43 | 44 | 45 | 46 | }); 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /tests/unit/Figma_Gradient.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaChildrenToggle from './data/figmaGradient.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Gradients', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaChildrenToggle.id, figmaChildrenToggle) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | // console.debug(TestUtil.print(screen)) 32 | 33 | let topDown = TestUtil.findElementsByName(screen, 'TopDown')[0] 34 | 35 | expect(topDown).not.toBeNull() 36 | expect(topDown.qtype).not.toBe('qVector') 37 | expect(topDown.style.background.direction).toBe(180) 38 | 39 | let downTop = TestUtil.findElementsByName(screen, 'DownUp')[0] 40 | expect(downTop).not.toBeNull() 41 | expect(downTop.qtype).not.toBe('qVector') 42 | expect(downTop.style.background.direction).toBe(0) 43 | 44 | 45 | let radial = TestUtil.findElementsByName(screen, 'Radial')[0] 46 | expect(radial).not.toBeNull() 47 | expect(radial.qtype).not.toBe('qVector') 48 | expect(radial.style.background.radial).toBe(true) 49 | 50 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Grid.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaChildrenToggle from './data/figmaChildrenToggle.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Grid', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaChildrenToggle.id, figmaChildrenToggle) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | console.debug(TestUtil.print(screen)) 32 | 33 | expect(screen.children.length).toBe(2) 34 | 35 | let searchCntr = TestUtil.findOneElementsByName(screen, 'SearchCntr') 36 | expect(searchCntr).not.toBeNull() 37 | expect(searchCntr.grid).not.toBeNull() 38 | expect(searchCntr.grid.columns.length).toBe(2) 39 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Hidden.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaHiddenBug from './data/figmaHiddenBug.json' 4 | 5 | import * as TestUtil from './TestUtil' 6 | 7 | test('Figma Hidden', async () => { 8 | 9 | /** 10 | * first call figma service to check taht all the boolean stuff and so is ignored 11 | */ 12 | let figmaService = new FigmaService() 13 | figmaService.setDownloadVectors(false) 14 | let app = await figmaService.parse(figmaHiddenBug.id, figmaHiddenBug) 15 | 16 | /** 17 | * Check if transform works correctly 18 | */ 19 | let t = new ModelTransformer(app, { 20 | css: { 21 | grid: true 22 | } 23 | }) 24 | let model = t.transform() 25 | 26 | expect(model).not.toBeNull() 27 | 28 | let screen = model.screens[0] 29 | expect(screen.pageName).toBe('Page 1') 30 | console.debug(TestUtil.print(screen)) 31 | 32 | let hiddenInstance = TestUtil.findOneElementsByName(screen, 'Rectangle1Instance') 33 | expect(hiddenInstance).toBeUndefined() 34 | 35 | 36 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Image_Download.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import luisaAttachLabelBug from './data/luisaAttachLabelBug.json' 4 | 5 | 6 | test('Test Luisa Landing', async () => { 7 | 8 | 9 | let figmaService = new FigmaService() 10 | figmaService.setDownloadVectors(false) 11 | let app = await figmaService.parse(luisaAttachLabelBug.id, luisaAttachLabelBug) 12 | expect(app).not.toBeUndefined() 13 | 14 | let placeHolderWidget = Object.values(app.widgets).find(w => w.figmaId === '124:388') 15 | expect(placeHolderWidget).not.toBeUndefined() 16 | 17 | let nodesWithImages = figmaService.getElementsWithBackgroundIMage(app, true) 18 | let placeHolderWidget2 = nodesWithImages.find( w => w.figmaId === '124:388') 19 | console.debug(placeHolderWidget2) 20 | 21 | 22 | }); 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/unit/Figma_Image_Fills.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaMasterComponentBug from './data/figmaMasterComponentBug.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Image Fills', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaMasterComponentBug.id, figmaMasterComponentBug) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | expect(screen).not.toBeUndefined() 31 | 32 | // here should be only two children, but the scrolls are created as vectors, 33 | // that's why the things are not embedded 34 | 35 | //console.debug(TestUtil.print(screen)) 36 | 37 | // FIXME: now test that the elements are correctly embedded. 38 | // expect(screen.children.length).toBe(2) 39 | 40 | 41 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Input.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaNestingBug2 from './data/figmaNestingBug2.json' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test TextBox', async () => { 7 | 8 | /** 9 | * first call figma service to check taht all the boolean stuff and so is ignored 10 | */ 11 | let figmaService = new FigmaService() 12 | figmaService.setDownloadVectors(false) 13 | let app = await figmaService.parse(figmaNestingBug2.id, figmaNestingBug2) 14 | 15 | 16 | /** 17 | * Check if transform works correctly 18 | */ 19 | let t = new ModelTransformer(app, { 20 | css: { 21 | grid: true 22 | } 23 | }) 24 | let model = t.transform() 25 | 26 | expect(model).not.toBeNull() 27 | 28 | let screen = model.screens[0] 29 | console.debug(TestUtil.print(screen)) 30 | expect(screen.children.length).toBe(2) 31 | expect(screen.fixedChildren.length).toBe(1) 32 | 33 | let searchCntr = TestUtil.findElementsByName(screen, 'SearchCntr')[0] 34 | expect(searchCntr).not.toBeNull() 35 | expect(searchCntr.children.length).toBe(2) 36 | 37 | let searchBox = TestUtil.findElementsByName(screen, 'SearchBox')[0] 38 | expect(searchBox).not.toBeNull() 39 | 40 | expect(searchBox.props.label).toBe('Brocoli') 41 | expect(searchBox.type).toBe('TextBox') 42 | expect(searchBox.qtype).toBe('qTextBox') 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /tests/unit/Figma_Master_Component_Bug.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaMasterComponentBug from './data/figmaMasterComponentBug.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Master Components rendered', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaMasterComponentBug.id, figmaMasterComponentBug) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | // here should be only two children, but the scrolls are created as vectors, 32 | // that's why the things are not embedded 33 | 34 | //console.debug(TestUtil.print(screen)) 35 | 36 | // instance is correct, component not... 37 | let instanceComponent = TestUtil.findOneElementsByProp(screen, '44:10', 'figmaId') 38 | expect(instanceComponent.qtype).not.toBe('qVector') 39 | 40 | let masterComponent = TestUtil.findOneElementsByProp(screen, '44:3', 'figmaId') 41 | expect(masterComponent.qtype).not.toBe('qVector') 42 | 43 | let scroll = TestUtil.findOneElementsByProp(screen, '45:8', 'figmaId') 44 | expect(scroll).not.toBeNull() 45 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Meta.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaMeta from './data/figmaMeta.json' 4 | 5 | 6 | import * as Util from '../../src/qux/core/ExportUtil' 7 | import Logger from '../../src/qux/core/Logger' 8 | import * as TestUtil from './TestUtil' 9 | 10 | test('Test Figma Auto Fixed Childen', async () => { 11 | Logger.logLevel = 1 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaMeta.id, figmaMeta) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, {}) 24 | let model = t.transform() 25 | 26 | expect(model).not.toBeNull() 27 | let screen = model.screens.find(s => s.name === 'why') 28 | expect(screen.meta.keywords).toBe('key1, key2, key3') 29 | expect(screen.meta.description).toBe('This is a cool page') 30 | 31 | 32 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Missing_Links.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaMissingLink from './data/figmaMissingLink.json' 4 | 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | import Logger from '../../src/qux/core/Logger' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Figma Missing links', async () => { 10 | Logger.logLevel = 1 11 | 12 | /** 13 | * first call figma service to check taht all the boolean stuff and so is ignored 14 | */ 15 | let figmaService = new FigmaService() 16 | figmaService.setDownloadVectors(false) 17 | let app = await figmaService.parse(figmaMissingLink.id, figmaMissingLink) 18 | 19 | /** 20 | * Check if transform works correctly 21 | */ 22 | let t = new ModelTransformer(app, { 23 | css: { 24 | grid: true 25 | } 26 | }) 27 | let model = t.transform() 28 | 29 | expect(model).not.toBeNull() 30 | let screen = model.screens.find(s => s.name === 'Home') 31 | 32 | let btn = TestUtil.findOneElementsByName(screen, 'ShowMoreBtn') 33 | expect(btn.lines.length).toBe(1) 34 | expect(btn.lines[0].figmaTo).toBe('3:6') 35 | 36 | 37 | 38 | }); 39 | 40 | -------------------------------------------------------------------------------- /tests/unit/Figma_Mixed_Border.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import * as FigmaUtil from '../../src/qux/figma/FigmaUtil' 4 | import figmaMixedBorders from './data/figmaMixedBorders.json' 5 | 6 | import * as Util from '../../src/qux/core/ExportUtil' 7 | import Logger from '../../src/qux/core/Logger' 8 | import * as TestUtil from './TestUtil' 9 | 10 | test('Test Figma Mixed Borders', async () => { 11 | Logger.logLevel = 1 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaMixedBorders.id, figmaMixedBorders) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, { 24 | css: { 25 | grid: true 26 | } 27 | }) 28 | let model = t.transform() 29 | 30 | expect(model).not.toBeNull() 31 | let screen = model.screens.find(s => s.name === 'Frame1') 32 | expect(screen).not.toBeUndefined() 33 | 34 | let btn = TestUtil.findOneElementsByName(screen, 'Mixed') 35 | expect(btn.style.borderBottomWidth).toBe(2) 36 | expect(btn.style.borderTopWidth).toBe(5) 37 | expect(btn.style.borderLeftWidth).toBe(4) 38 | expect(btn.style.borderRightWidth).toBe(3) 39 | 40 | let top = TestUtil.findOneElementsByName(screen, 'Top') 41 | expect(top.style.borderBottomWidth).toBe(0) 42 | expect(top.style.borderTopWidth).toBe(1) 43 | expect(top.style.borderLeftWidth).toBe(0) 44 | expect(top.style.borderRightWidth).toBe(0) 45 | 46 | 47 | 48 | }); 49 | 50 | -------------------------------------------------------------------------------- /tests/unit/Figma_Nested_Kokos.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaNestBugKokos from './data/figmaNestedBugKokos_ManualFix.json' 4 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 5 | import CSSFactory from '../../src/qux/core/CSSFactory' 6 | 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Wrong Nesting', async () => { 10 | 11 | /** 12 | * first call figma service to check taht all the boolean stuff and so is ignored 13 | */ 14 | let figmaService = new FigmaService() 15 | figmaService.setDownloadVectors(false) 16 | let app = await figmaService.parse(figmaNestBugKokos.id, figmaNestBugKokos) 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | 28 | expect(model).not.toBeNull() 29 | 30 | let screen = model.screens[0] 31 | 32 | console.debug(TestUtil.print(screen)) 33 | 34 | expect(screen.children.length).toBe(1) 35 | let background = TestUtil.findOneElementsByName(screen, 'Background') 36 | expect(background.children.length).toBe(4) 37 | 38 | 39 | let backgroundImage = TestUtil.findOneElementsByName(screen, 'BackgroundImage') 40 | backgroundImage.style.backgroundImage = { 41 | url: 'test.png' 42 | } 43 | 44 | let compressed = new CSSOptimizer().runTree(model) 45 | let classes = new CSSFactory().generate(compressed) 46 | 47 | let cssBackground = TestUtil.findCSSBySelector(classes, '.Background')[0] 48 | expect(cssBackground.code.indexOf('grid-template-columns: 391px 152px 36px minmax(0,1fr) 36px 151px 392px;')).toBeGreaterThan(0) 49 | 50 | 51 | let cssBackgroundImage = TestUtil.findCSSBySelector(classes, '.BackgroundImage')[0] 52 | expect(cssBackgroundImage.code.indexOf('width')).toBe(-1) 53 | expect(cssBackgroundImage.code.indexOf('height')).toBe(-1) 54 | 55 | 56 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Nested_Vector.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaNestedVecors from './data/figmaNestedVecors.json' 4 | 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | import Logger from '../../src/qux/core/Logger' 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Test Figma Nested Vector', async () => { 10 | Logger.logLevel = 1 11 | 12 | /** 13 | * first call figma service to check taht all the boolean stuff and so is ignored 14 | */ 15 | let figmaService = new FigmaService() 16 | figmaService.setDownloadVectors(false) 17 | let app = await figmaService.parse(figmaNestedVecors.id, figmaNestedVecors, true, ['Mobile']) 18 | 19 | /** 20 | * Check if transform works correctly 21 | */ 22 | let t = new ModelTransformer(app, { 23 | css: { 24 | grid: true 25 | } 26 | }) 27 | let model = t.transform() 28 | 29 | expect(model).not.toBeNull() 30 | let screen = model.screens.find(s => s.name === 'Home') 31 | console.debug(TestUtil.print(screen)) 32 | 33 | let hero = TestUtil.findOneElementsByName(screen, 'Hero') 34 | expect(hero.children.length).toBe(2) 35 | 36 | 37 | let logo = TestUtil.findOneElementsByName(screen, 'luisa-cloud-logo') 38 | expect(logo.children.length).toBe(0) 39 | expect(logo.type).toBe('Vector') 40 | }); 41 | 42 | -------------------------------------------------------------------------------- /tests/unit/Figma_Nesting_Bug.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaNestingBug from './data/figmaNestingBug.json' 4 | import figmaNestingBug2 from './data/figmaNestingBug2.json' 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Wrong Nesting', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaNestingBug.id, figmaNestingBug) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | TestUtil.print(screen) 32 | 33 | expect(screen.children.length).toBe(3) 34 | 35 | 36 | }); 37 | 38 | test('Test Wrong Nesting 2', async () => { 39 | 40 | /** 41 | * first call figma service to check taht all the boolean stuff and so is ignored 42 | */ 43 | let figmaService = new FigmaService() 44 | figmaService.setDownloadVectors(false) 45 | let app = await figmaService.parse(figmaNestingBug2.id, figmaNestingBug2) 46 | 47 | 48 | /** 49 | * Check if transform works correctly 50 | */ 51 | let t = new ModelTransformer(app, { 52 | css: { 53 | grid: true 54 | } 55 | }) 56 | let model = t.transform() 57 | 58 | expect(model).not.toBeNull() 59 | 60 | let screen = model.screens[0] 61 | console.debug(TestUtil.print(screen)) 62 | expect(screen.children.length).toBe(2) 63 | 64 | /** 65 | * Test the the fixed stuff is also correctly set 66 | */ 67 | expect(screen.fixedChildren.length).toBe(1) 68 | expect(screen.fixedChildren[0].children.length).toBe(1) 69 | }); 70 | 71 | 72 | -------------------------------------------------------------------------------- /tests/unit/Figma_Overflow_Scroll.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayout from './data/figmaAutoLayout.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Wrong Nesting', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaAutoLayout.id, figmaAutoLayout) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | console.debug(TestUtil.print(screen)) 32 | 33 | expect(screen.children.length).toBe(2) 34 | /** 35 | * The scroll container should be foound because of parent child relation ship 36 | */ 37 | expect(screen.children[0].name).toBe('ScrollContainer') 38 | expect(screen.children[0].children[0].name).toBe('ItemContainer') 39 | /** 40 | * Visual contaimner because of visual relation ship 41 | */ 42 | expect(screen.children[1].name).toBe('VisualContainer') 43 | expect(screen.children[1].children[0].name).toBe('VisualChild') 44 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Page.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaAutoLayout from './data/figmaAutoLayout.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Page name added', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaAutoLayout.id, figmaAutoLayout) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | console.debug(screen) 31 | expect(screen.pageName).toBe('Page 1') 32 | 33 | 34 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Responsive_Bug.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaResponsiveBug from './data/figmaResponsiveBug.json' 4 | 5 | import Logger from '../../src/qux/core/Logger' 6 | import * as TestUtil from './TestUtil' 7 | 8 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 9 | import CSSFactory from '../../src/qux/core/CSSFactory' 10 | 11 | 12 | test('Test Figma Responsive Bug', async () => { 13 | Logger.logLevel = 1 14 | 15 | /** 16 | * first call figma service to check taht all the boolean stuff and so is ignored 17 | */ 18 | let figmaService = new FigmaService() 19 | figmaService.setDownloadVectors(false) 20 | let app = await figmaService.parse(figmaResponsiveBug.id, figmaResponsiveBug) 21 | 22 | const config = { 23 | css: { 24 | grid: true 25 | }, 26 | responsive: [ 27 | { page: "Desktop", types: ["desktop"] }, 28 | { page: "Mobile", types: ["tablet", "mobile"] }, 29 | ] 30 | } 31 | 32 | /** 33 | * Check if transform works correctly 34 | */ 35 | let t = new ModelTransformer(app, config) 36 | let model = t.transform() 37 | 38 | expect(model).not.toBeNull() 39 | 40 | let compressed = new CSSOptimizer().runTree(model) 41 | let classes = new CSSFactory(config).generate(compressed) 42 | 43 | expect(classes).not.toBeNull() 44 | 45 | 46 | }); 47 | 48 | -------------------------------------------------------------------------------- /tests/unit/Figma_Space_Between.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | 4 | import * as Util from '../../src/qux/core/ExportUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | import * as TestUtil from './TestUtil' 8 | 9 | import figmaSpaceBetween from './data/figmaSpaceBetween.json' 10 | 11 | test('Test SpaceBetween ', async () => { 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaSpaceBetween.id, figmaSpaceBetween) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, { 24 | css: { 25 | grid: true 26 | } 27 | }) 28 | let model = t.transform() 29 | 30 | expect(model).not.toBeNull() 31 | 32 | let screen = model.screens[0] 33 | 34 | console.debug(TestUtil.print(screen)) 35 | 36 | let spaceBetween = TestUtil.findElementsByName(screen, 'SpaceBetween')[0] 37 | 38 | expect(Util.isFixedHorizontal(spaceBetween)).toBe(false) 39 | expect(spaceBetween.layout.justifyContent).toBe('space-between') 40 | expect(Util.isAutoLayoutSpaceBetween(spaceBetween)).toBe(true) 41 | expect(spaceBetween.layout.itemSpacing).toBe(10) 42 | 43 | let gap = TestUtil.findElementsByName(screen, 'Gap')[0] 44 | expect(Util.isFixedHorizontal(gap)).toBe(false) 45 | expect(gap.layout.justifyContent).toBe('flex-start') 46 | expect(Util.isAutoLayoutSpaceBetween(gap)).toBe(false) 47 | expect(gap.layout.itemSpacing).toBe(10) 48 | 49 | let compressed = new CSSOptimizer().runTree(model) 50 | let classes = new CSSFactory().generate(compressed) 51 | 52 | // test css a little 53 | let spaceBetweenCSS = TestUtil.findCSSBySelector(classes, '.SpaceBetween')[0] 54 | expect(spaceBetweenCSS.code.indexOf('justify-content: space-between;')).toBeGreaterThan(0) 55 | expect(spaceBetweenCSS.code.indexOf('gap:')).toBe(-1) 56 | //expect(wrapCSS.code.indexOf('max-width: 1200px')).toBeGreaterThan(0) 57 | //expect(wrapCSS.code.indexOf('display: flex')).toBeGreaterThan(0) 58 | 59 | let gapCSS = TestUtil.findCSSBySelector(classes, '.Gap')[0] 60 | expect(gapCSS.code.indexOf('justify-content: flex-start;')).toBeGreaterThan(0) 61 | expect(gapCSS.code.indexOf('gap: 10px;')).toBeGreaterThan(0) 62 | 63 | //console.debug(wrapCSS.code) 64 | 65 | }); 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /tests/unit/Figma_Text.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaTextStyle from './data/figmaTextStyle.json' 4 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 5 | import CSSFactory from '../../src/qux/core/CSSFactory' 6 | 7 | import * as TestUtil from './TestUtil' 8 | 9 | test('Figma Text Styles', async () => { 10 | 11 | /** 12 | * first call figma service to check taht all the boolean stuff and so is ignored 13 | */ 14 | let figmaService = new FigmaService() 15 | figmaService.setDownloadVectors(false) 16 | let app = await figmaService.parse(figmaTextStyle.id, figmaTextStyle) 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | 28 | expect(model).not.toBeNull() 29 | 30 | let screen = model.screens[0] 31 | expect(screen.pageName).toBe('Page 1') 32 | 33 | let normal = TestUtil.findOneElementsByName(screen, 'Normal') 34 | expect(normal).not.toBeUndefined() 35 | expect(normal.style.textTransform).toBeUndefined() 36 | 37 | let upper = TestUtil.findOneElementsByName(screen, 'Upper') 38 | expect(upper).not.toBeUndefined() 39 | expect(upper.style.textTransform).toBe('uppercase') 40 | 41 | let lower = TestUtil.findOneElementsByName(screen, 'Lower') 42 | expect(lower).not.toBeUndefined() 43 | expect(lower.style.textTransform).toBe('lowercase') 44 | 45 | let caps = TestUtil.findOneElementsByName(screen, 'Caps') 46 | expect(caps).not.toBeUndefined() 47 | expect(caps.style.textTransform).toBe('capitalize') 48 | 49 | 50 | let compressed = new CSSOptimizer().runTree(model) 51 | let classes = new CSSFactory().generate(compressed) 52 | 53 | let cssNormal = TestUtil.findCSSBySelector(classes, '.Normal')[0] 54 | expect(cssNormal.code.indexOf('text-transform:;')).toBe(-1) 55 | 56 | let cssUpper = TestUtil.findCSSBySelector(classes, '.Upper')[0] 57 | expect(cssUpper.code.indexOf('text-transform: uppercase;')).toBeGreaterThan(0) 58 | 59 | let cssLower = TestUtil.findCSSBySelector(classes, '.Lower')[0] 60 | expect(cssLower.code.indexOf('text-transform: lowercase;')).toBeGreaterThan(0) 61 | 62 | let cssCaps = TestUtil.findCSSBySelector(classes, '.Caps')[0] 63 | expect(cssCaps.code.indexOf('text-transform: capitalize;')).toBeGreaterThan(0) 64 | 65 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Variants.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaVariants from './data/figmaVariants.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Varients', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaVariants.id, figmaVariants) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | console.debug(TestUtil.print(screen)) 32 | 33 | expect(screen.children.length).toBe(1) 34 | /** 35 | * The scroll container should be foound because of parent child relation ship 36 | */ 37 | expect(screen.children[0].name).toBe('VarientButton') 38 | expect(screen.children[0].props.isComponet).toBe(undefined) 39 | expect(screen.children[0].children.length).toBe(2) 40 | expect(screen.children[0].children[0].name).toBe('VarientButton-Primary') 41 | expect(screen.children[0].children[1].name).toBe('VarientButton-Secondary') 42 | 43 | 44 | }); 45 | 46 | 47 | test('Test parseVarinet', async () => { 48 | 49 | /** 50 | * first call figma service to check taht all the boolean stuff and so is ignored 51 | */ 52 | let figmaService = new FigmaService() 53 | 54 | let parsed = figmaService.parseVariant('mouse=Default, size=Default') 55 | expect(parsed.mouse).toBe('Default') 56 | expect(parsed.size).toBe('Default') 57 | 58 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_VariantsOld.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaVariants from './data/figmaVariants.json' 4 | 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Varients', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaVariants.id, figmaVariants) 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | 31 | console.debug(TestUtil.print(screen)) 32 | 33 | expect(screen.children.length).toBe(1) 34 | /** 35 | * The scroll container should be foound because of parent child relation ship 36 | */ 37 | expect(screen.children[0].name).toBe('VarientButton') 38 | expect(screen.children[0].props.isComponet).toBe(undefined) 39 | expect(screen.children[0].children.length).toBe(2) 40 | expect(screen.children[0].children[0].name).toBe('VarientButton-Primary') 41 | expect(screen.children[0].children[1].name).toBe('VarientButton-Secondary') 42 | 43 | 44 | }); 45 | 46 | 47 | test('Test parseVarinet', async () => { 48 | 49 | /** 50 | * first call figma service to check taht all the boolean stuff and so is ignored 51 | */ 52 | let figmaService = new FigmaService() 53 | 54 | let parsed = figmaService.parseVariant('mouse=A, size=B') 55 | expect(parsed.mouse).toBe('A') 56 | expect(parsed.size).toBe('B') 57 | 58 | parsed = figmaService.parseVariant('mouse=A- size=B') 59 | expect(parsed.mouse).toBe('A') 60 | expect(parsed.size).toBe('B') 61 | 62 | parsed = figmaService.parseVariant('type=C') 63 | expect(parsed.type).toBe('C') 64 | 65 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_VariantsUpdate.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaDesignSystemUpdateBug from './data/figmaDesignSystemUpdateBug.json' 4 | 5 | import * as TestUtil from './TestUtil' 6 | 7 | test('Figma Text Styles', async () => { 8 | 9 | /** 10 | * first call figma service to check taht all the boolean stuff and so is ignored 11 | */ 12 | let figmaService = new FigmaService() 13 | figmaService.setDownloadVectors(false) 14 | let app = await figmaService.parse(figmaDesignSystemUpdateBug.id, figmaDesignSystemUpdateBug) 15 | 16 | 17 | /** 18 | * Check if transform works correctly 19 | */ 20 | let t = new ModelTransformer(app, { 21 | css: { 22 | grid: true 23 | } 24 | }) 25 | let model = t.transform() 26 | 27 | expect(model).not.toBeNull() 28 | 29 | 30 | let screen = model.screens[0] 31 | 32 | 33 | let variantRed = TestUtil.findOneElementsByName(screen, 'VarientButton-Primary-Out') 34 | expect(variantRed).not.toBeUndefined() 35 | expect(variantRed.variant).not.toBeUndefined() 36 | expect(variantRed.variant.type).toBe('Primary') 37 | expect(variantRed.variant.state).toBe('Out') 38 | console.debug(variantRed.variant) 39 | }); -------------------------------------------------------------------------------- /tests/unit/Figma_Wrap.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | 4 | import * as Util from '../../src/qux/core/ExportUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | import * as TestUtil from './TestUtil' 8 | 9 | import figmaWrap from './data/figmaWrap.json' 10 | 11 | test('Test Figma Warp', async () => { 12 | 13 | /** 14 | * first call figma service to check taht all the boolean stuff and so is ignored 15 | */ 16 | let figmaService = new FigmaService() 17 | figmaService.setDownloadVectors(false) 18 | let app = await figmaService.parse(figmaWrap.id, figmaWrap) 19 | 20 | /** 21 | * Check if transform works correctly 22 | */ 23 | let t = new ModelTransformer(app, { 24 | css: { 25 | grid: true 26 | } 27 | }) 28 | let model = t.transform() 29 | 30 | expect(model).not.toBeNull() 31 | 32 | let screen = model.screens[0] 33 | 34 | //console.debug(TestUtil.print(screen)) 35 | 36 | let cntr = TestUtil.findElementsByName(screen, 'WrapCntr')[0] 37 | expect(Util.isFixedHorizontal(cntr)).toBe(true) 38 | expect(Util.hasMinMaxWdith(cntr)).toBe(true) 39 | expect(Util.isLayoutWrap(cntr)).toBe(true) 40 | 41 | 42 | let autoCntr = TestUtil.findElementsByName(screen, 'WrapeAutoCntr')[0] 43 | expect(Util.isFixedHorizontal(autoCntr)).toBe(true) 44 | expect(Util.hasMinMaxWdith(autoCntr)).toBe(true) 45 | // the auto layout will overwite the wrap! The Flat2Tree will ignore the style.layout 46 | expect(Util.isLayoutAutoHorizontal(autoCntr)).toBe(true) 47 | /** 48 | * Still the wrao is there! 49 | */ 50 | expect(Util.isWrappedContainer(autoCntr)).toBe(true) 51 | 52 | let compressed = new CSSOptimizer().runTree(model) 53 | let classes = new CSSFactory().generate(compressed) 54 | 55 | // test css a little 56 | let wrapCSS = TestUtil.findCSSBySelector(classes, '.WrapCntr')[0] 57 | expect(wrapCSS.code.indexOf('min-width: 500px;')).toBeGreaterThan(0) 58 | expect(wrapCSS.code.indexOf('max-width: 1200px')).toBeGreaterThan(0) 59 | expect(wrapCSS.code.indexOf('display: flex')).toBeGreaterThan(0) 60 | 61 | //console.debug(wrapCSS.code) 62 | 63 | }); 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /tests/unit/Fixed_Nested_Elements.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import app from './data/fixedNestedElements.json' 3 | 4 | import * as TestUtil from './TestUtil' 5 | 6 | 7 | test('Test Fixed', () => { 8 | 9 | let t = new ModelTransformer(app) 10 | let model = t.transform() 11 | 12 | expect(model.screens.length).toBe(1) 13 | 14 | let screen = model.screens.find(s => s.id === 's10000') 15 | expect(screen).not.toBe(null) 16 | 17 | console.debug(TestUtil.print(screen)) 18 | 19 | expect(screen.children.length).toBe(2) 20 | expect(screen.fixedChildren.length).toBe(1) 21 | expect(screen.fixedChildren[0].children.length).toBe(2) 22 | 23 | let fixedCntr = screen.fixedChildren[0] 24 | expect(fixedCntr.x).toBe(88) 25 | expect(fixedCntr.y).toBe(364) 26 | 27 | let fixedChild1 = TestUtil.findElementsByName(fixedCntr, 'FixedChild1')[0] 28 | expect(fixedChild1).not.toBeNull() 29 | expect(fixedChild1.y).toBe(20) 30 | 31 | }); 32 | 33 | -------------------------------------------------------------------------------- /tests/unit/Fixed_Removed_From_Layout.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import fixedRemovedFromLayout from './data/fixedRemovedFromLayout.json' 4 | 5 | import * as TestUtil from './TestUtil' 6 | 7 | 8 | test('Test Fixed', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(fixedRemovedFromLayout.id, fixedRemovedFromLayout) 16 | 17 | 18 | /** 19 | * Check if transform works correctly 20 | */ 21 | let t = new ModelTransformer(app, { 22 | css: { 23 | grid: true 24 | } 25 | }) 26 | let model = t.transform() 27 | expect(model).not.toBeNull() 28 | 29 | let screen = model.screens[0] 30 | console.debug(TestUtil.print(screen)) 31 | 32 | expect(screen.children.length).toBe(3) 33 | 34 | let lowerBox = TestUtil.findOneElementsByName(screen, 'LowerBox') 35 | expect(lowerBox).not.toBeNull() 36 | console.debug(lowerBox) 37 | expect(lowerBox.children.length).toBe(2) 38 | expect(lowerBox.top).toBe(221) 39 | }); 40 | 41 | -------------------------------------------------------------------------------- /tests/unit/Flat2TRee_Fixed_Footer.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import fixedFooter from './data/fixedFooter.json' 3 | 4 | test('Test Fixed Parent correct', async () => { 5 | 6 | let t = new ModelTransformer(fixedFooter) 7 | let model = t.transform() 8 | 9 | expect(model).not.toBeNull() 10 | 11 | let screen = model.screens[1] 12 | expect(screen.name).toBe('Home') 13 | expect(screen.parent).toBeUndefined() // we should not have a parent. This was the bug 14 | expect(screen.cssSelector).not.toBe('.Home .Home') 15 | 16 | 17 | //console.debug(TestUtil.print(screen)) 18 | }); -------------------------------------------------------------------------------- /tests/unit/Flat2Tree_Attach.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | 4 | import * as TestUtil from './TestUtil' 5 | import * as Util from '../../src/qux/core/ExportUtil' 6 | 7 | import luisaAttachLabelBug from './data/luisaAttachLabelBug.json' 8 | 9 | test('Test Auto layout with single label', async () => { 10 | 11 | 12 | let figmaService = new FigmaService() 13 | figmaService.setDownloadVectors(false) 14 | let app = await figmaService.parse(luisaAttachLabelBug.id, luisaAttachLabelBug) 15 | 16 | /** 17 | * Check if transform works correctly 18 | */ 19 | let t = new ModelTransformer(app, { 20 | css: { 21 | attachLabels: true 22 | } 23 | }) 24 | let model = t.transform() 25 | 26 | expect(model).not.toBeNull() 27 | 28 | let screen = model.screens[0] 29 | 30 | let btn = TestUtil.findOneElementsByName(screen, 'cta-button_17') 31 | expect(Util.isLayoutRow(btn)).toBeTruthy() 32 | 33 | 34 | }); -------------------------------------------------------------------------------- /tests/unit/FontWriter.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import FigmaService from '../../src/qux/figma/FigmaService' 3 | import figmaFontTest from './data/figmaFontTest.json' 4 | import FontWriter from '../../src/qux/core/FontWriter' 5 | 6 | import * as TestUtil from './TestUtil' 7 | 8 | test('Test Custom Fonts Figma', async () => { 9 | 10 | /** 11 | * first call figma service to check taht all the boolean stuff and so is ignored 12 | */ 13 | let figmaService = new FigmaService() 14 | figmaService.setDownloadVectors(false) 15 | let app = await figmaService.parse(figmaFontTest.id, figmaFontTest) 16 | 17 | 18 | let fonts = FontWriter.getCustomFonts(app) 19 | console.debug(fonts) 20 | expect(Object.values(fonts).length).toBe(3) 21 | //expect(fonts['Roboto'].length).toBe(2) 22 | 23 | }); -------------------------------------------------------------------------------- /tests/unit/Group_Wrapping.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 3 | import * as Util from '../../src/qux/core/ExportUtil' 4 | 5 | import groups from './data/groups.json' 6 | 7 | 8 | test('Test Nest Groups', () => { 9 | delete groups.screens.s10000 10 | delete groups.screens.s10015 11 | 12 | let t = new ModelTransformer(groups) 13 | let model = t.transform() 14 | 15 | expect(model.screens.length).toBe(1) 16 | 17 | let screen = model.screens.find(s => s.name === 'NormalNestGroups') 18 | expect(screen).not.toBeNull() 19 | 20 | console.debug(Util.print(screen)) 21 | 22 | 23 | expect(screen.children.length).toBe(1) 24 | expect(screen.children[0].name).toBe('OuterGroup') 25 | expect(screen.children[0].children.length).toBe(2) 26 | expect(screen.children[0].children[0].name).toBe('Button') 27 | expect(screen.children[0].children[1].name).toBe('InnerGroup') 28 | 29 | }); 30 | -------------------------------------------------------------------------------- /tests/unit/ImportUtil.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import ImportUtil from '../../src/qux/core/ImportUtil' 3 | 4 | 5 | test('Test ImportUtil', () => { 6 | 7 | let prefix = ImportUtil.get('generated/src/components', 'generated/src/images') 8 | expect(prefix).toBe('../images') 9 | 10 | prefix = ImportUtil.get('generated/src/components', 'generated/images') 11 | expect(prefix).toBe('../../images') 12 | 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /tests/unit/JsonPath.spec.js: -------------------------------------------------------------------------------- 1 | import JSONPath from '../../src/qux/core/JSONPath' 2 | 3 | const viewModel = { 4 | searchFilter: "", 5 | todos: [ 6 | { 7 | id:1, 8 | name: "111", 9 | details: "11111111", 10 | }, 11 | { 12 | id:2, 13 | name: "2222", 14 | details: "222222", 15 | } 16 | ], 17 | newTodo: { 18 | name: "", 19 | details: "", 20 | }, 21 | selectedTodo: { 22 | name: "", 23 | details: "", 24 | }, 25 | } 26 | 27 | test('Test JSON Path', () => { 28 | 29 | 30 | expect(JSONPath.has(viewModel, 'searchFilter')).toBe(true) 31 | expect(JSONPath.has(viewModel, 'todos')).toBe(true) 32 | }); -------------------------------------------------------------------------------- /tests/unit/Layout_RowAndGrid.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import layoutRowAndGrid from './data/layoutRowAndGrid.json' 3 | import {Layout} from '../../src/qux/core/Const' 4 | import * as TestUtil from './TestUtil' 5 | 6 | 7 | test('Test Correct layout property', () => { 8 | 9 | let t = new ModelTransformer(layoutRowAndGrid) 10 | let model = t.transform() 11 | 12 | expect(model.screens.length).toBe(1) 13 | 14 | let screen = model.screens.find(s => s.id === 's10000') 15 | expect(screen).not.toBe(null) 16 | expect(screen.layout.type).toBe(Layout.Row) 17 | 18 | console.debug(TestUtil.print(screen)) 19 | 20 | assertLayout(screen, 'RowGrid', Layout.Grid ) 21 | assertLayout(screen, 'RowRow', Layout.Row ) 22 | assertLayout(screen, 'RowWrap', Layout.Wrap ) 23 | }); 24 | 25 | function assertLayout(screen, name, type) { 26 | let e = TestUtil.findOneElementsByName(screen, name) 27 | expect(e).not.toBeUndefined() 28 | expect(e.layout.type).toBe(type) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /tests/unit/Master_Groups.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import masterGroups from './data/masterGroups.json' 3 | import * as Util from '../../src/qux/core/ExportUtil' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Master Groups', () => { 7 | 8 | /** 9 | * Check that group is copied 10 | */ 11 | let inModel = Util.createInheritedModel(masterGroups) 12 | expect(Object.values(inModel.groups).length).toBe(2) 13 | 14 | let t = new ModelTransformer(masterGroups, { 15 | css: { 16 | grid: true 17 | } 18 | }) 19 | let model = t.transform() 20 | 21 | expect(model).not.toBeNull() 22 | 23 | let master = model.screens[1] 24 | console.debug(TestUtil.print(master)) 25 | /** 26 | * Expect the group to be copied which should have resulted in main child 27 | */ 28 | expect(master.children.length).toBe(1) 29 | expect(master.children[0].children.length).toBe(2) 30 | 31 | let screen = model.screens[0] 32 | //console.debug(TestUtil.print(screen)) 33 | expect(screen.children.length).toBe(1) 34 | expect(screen.children[0].children.length).toBe(2) 35 | 36 | }); -------------------------------------------------------------------------------- /tests/unit/RepeaterBug.spec.js: -------------------------------------------------------------------------------- 1 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 2 | import reaterBug from './data/ReaterBug.json' 3 | import * as Util from '../../src/qux/core/ExportUtil' 4 | import * as TestUtil from './TestUtil' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | 8 | test('Test Repeater Bug', () => { 9 | 10 | /** 11 | * Check that group is copied 12 | */ 13 | 14 | const t = new ModelTransformer(reaterBug, { 15 | css: { 16 | grid: true 17 | } 18 | }) 19 | const model = t.transform() 20 | 21 | expect(model).not.toBeNull() 22 | 23 | const start = model.screens[0] 24 | console.debug(TestUtil.print(start)) 25 | 26 | 27 | const grid = TestUtil.findOneElementsByName(start, "Grid") 28 | expect(grid.children.length).toBe(1) 29 | expect(grid.children[0].name).toBe("GridWrapper") 30 | 31 | }); -------------------------------------------------------------------------------- /tests/unit/ResponsiveWidthPaddingBug.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 3 | import * as TestUtil from './TestUtil' 4 | import responsiveWidthPaddingBug from './data/responsiveWidthPaddingBug.json' 5 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 6 | import CSSFactory from '../../src/qux/core/CSSFactory' 7 | 8 | test('Responsive Padding Bug', () => { 9 | 10 | 11 | let t = new ModelTransformer(responsiveWidthPaddingBug) 12 | let model = t.transform() 13 | 14 | expect(model.screens.length).toBe(1) 15 | 16 | let screen = model.screens.find(s => s.name === 'Search') 17 | expect(screen).not.toBeNull() 18 | 19 | let compressed = new CSSOptimizer().runTree(model) 20 | let classes = new CSSFactory().generate(compressed) 21 | 22 | let textBoxCSS = TestUtil.findCSSBySelector(classes, '.Search .TextBox')[0] 23 | expect(textBoxCSS.code.indexOf("width: 87%")).toBe(-1) 24 | expect(textBoxCSS.code.indexOf("width: calc(87% - 26px);")).toBeGreaterThan(0) 25 | 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /tests/unit/RestEngineTest.spec.js: -------------------------------------------------------------------------------- 1 | import RestEngine from '../../src/qux/core/RestEngine' 2 | 3 | test('Test fillString()', async () => { 4 | 5 | let result = await RestEngine.fillString('http://localhost:8080/api?first=${selectedFirst.id}&second=${selectedSecond}', { 6 | 'selectedFirst.id':1, 7 | 'selectedSecond': 2 8 | }) 9 | expect(result).toBe('http://localhost:8080/api?first=1&second=2') 10 | 11 | 12 | }); 13 | 14 | test('Test getNeededDataBings()', async () => { 15 | 16 | let requiredDataBindings = RestEngine.getNeededDataBings({ 17 | method: 'GET', 18 | url: 'http://localhost:8080/api?first=${selectedFirst.id}&second=${selectedSecond}', 19 | token: '' 20 | }) 21 | expect(requiredDataBindings.length).toBe(2) 22 | expect(requiredDataBindings[0]).toBe('selectedFirst.id') 23 | expect(requiredDataBindings[1]).toBe('selectedSecond') 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /tests/unit/ValidationLabel.spec.js: -------------------------------------------------------------------------------- 1 | import app from './data/validationApp.json' 2 | 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test ValidationLabel', async () => { 7 | 8 | 9 | const t = new ModelTransformer(app, { 10 | css: { 11 | grid: true, 12 | attachLabels: true 13 | }, 14 | addDefaultDatabinding: true 15 | }) 16 | const model = t.transform() 17 | 18 | const screen = TestUtil.findScreen(model, 'Screen') 19 | const label = TestUtil.findOneElementsByName(screen, 'ErrorEmail') 20 | expect(label.props.errorLabelSource).not.toBe(null) 21 | expect(label.props.errorLabelSource).not.toBe(undefined) 22 | expect(label.props.errorLabelSource).toBe('w10003_12882') 23 | 24 | 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /tests/unit/WebUtilTest.js: -------------------------------------------------------------------------------- 1 | import {replaceAllowedTags} from '../../src/qux/web/WebUtil' 2 | 3 | test('Test cleanInnerHTML', async () => { 4 | 5 | let clean = replaceAllowedTags('<b>Rhis is fat</b> not fat <b>fat agaib</b> - <B>FAT</B><script>alert("XXX")</script>') 6 | expect(clean).toBe('Rhis is fat not fat fat agaib - FAT<script>alert("XXX")</script>') 7 | 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /tests/unit/WidthErrorTest.spec.js: -------------------------------------------------------------------------------- 1 | 2 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 3 | import * as Util from '../../src/qux/core/ExportUtil' 4 | import * as TestUtil from './TestUtil' 5 | import app from './data/widthError.json' 6 | import CSSOptimizer from '../../src/qux/core/CSSOptimizer' 7 | import CSSFactory from '../../src/qux/core/CSSFactory' 8 | 9 | test('Test Nest Groups', () => { 10 | 11 | let t = new ModelTransformer(app) 12 | let model = t.transform() 13 | 14 | expect(model.screens.length).toBe(3) 15 | 16 | let screenStart = model.screens.find(s => s.name === 'Start') 17 | expect(screenStart).not.toBeNull() 18 | let startNotification1 = TestUtil.findOneElementsByName(screenStart, 'Notification1Background') 19 | expect(startNotification1.w).toBe(344) 20 | 21 | let startScenario1 = TestUtil.findOneElementsByName(screenStart, 'Scenario1') 22 | expect(startScenario1.w).toBe(344) 23 | 24 | 25 | 26 | let notificationScreen = model.screens.find(s => s.name === 'NotificationSoftCrying') 27 | expect(notificationScreen).not.toBeNull() 28 | // console.debug(Util.print(notificationScreen)) 29 | 30 | let notificationBack = TestUtil.findOneElementsByName(notificationScreen, 'NotificationBackend') 31 | expect(notificationBack.w).toBe(344) 32 | 33 | /** 34 | * Now lets check what is wrong with the margins 35 | */ 36 | let compressed = new CSSOptimizer().runTree(model) 37 | let classes = new CSSFactory().generate(compressed) 38 | 39 | let cssScenario = TestUtil.findCSSBySelector(classes, '.Scenario1')[0] 40 | 41 | expect(cssScenario.code.indexOf('width: 92%;')).toBeGreaterThan(0) 42 | 43 | let cssNotificationBack = TestUtil.findCSSBySelector(classes, '.NotificationBackend')[0] 44 | 45 | /** They should have a grid, becasue there is a an overlap */ 46 | expect(cssNotificationBack).not.toBeNull() 47 | //console.debug(cssNotificationBack) 48 | expect(cssNotificationBack.code.indexOf('width: 91%;')).toBe(-1) 49 | expect(cssNotificationBack.code.indexOf('width: 92%;')).toBeGreaterThan(0) 50 | 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /tests/unit/data/figmaImportBugs.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/tests/unit/data/figmaImportBugs.json -------------------------------------------------------------------------------- /tests/unit/data/images.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KlausSchaefers/vue-low-code/ebb1a69892d51af1c069764d0392ef3ca83599df/tests/unit/data/images.json -------------------------------------------------------------------------------- /tests/unit/evilNameBug.spec.js: -------------------------------------------------------------------------------- 1 | import app from './data/evilNamingBug.json' 2 | 3 | import ModelTransformer from '../../src/qux/core/ModelTransformer' 4 | import * as TestUtil from './TestUtil' 5 | 6 | test('Test Evil Names', async () => { 7 | 8 | let t = new ModelTransformer(app, { 9 | css: { 10 | grid: true 11 | } 12 | }) 13 | let model = t.transform() 14 | 15 | expect(model).not.toBeNull() 16 | 17 | let overlay = model.screens[2] 18 | console.debug(TestUtil.print(overlay)) 19 | 20 | let password = overlay.children[0].children[1] 21 | expect(password.name.indexOf('(')).toBeLessThan(0) 22 | expect(password.cssSelector.indexOf('(')).toBeLessThan(0) 23 | }); -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | css: { extract: false } 3 | } --------------------------------------------------------------------------------