├── .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 | 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 | 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 | 19 | 23 | 38 | -------------------------------------------------------------------------------- /src/qux/web/Camera.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /src/qux/web/CheckBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 85 | -------------------------------------------------------------------------------- /src/qux/web/CheckBoxGroup.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 93 | -------------------------------------------------------------------------------- /src/qux/web/Icon.vue: -------------------------------------------------------------------------------- 1 | 5 | 8 | 29 | -------------------------------------------------------------------------------- /src/qux/web/IconToggle.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 62 | -------------------------------------------------------------------------------- /src/qux/web/Image.vue: -------------------------------------------------------------------------------- 1 | 8 | 11 | 42 | -------------------------------------------------------------------------------- /src/qux/web/Link.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | 46 | -------------------------------------------------------------------------------- /src/qux/web/RadioBox.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 70 | -------------------------------------------------------------------------------- /src/qux/web/RadioGroup.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 51 | -------------------------------------------------------------------------------- /src/qux/web/Rating.vue: -------------------------------------------------------------------------------- 1 | 12 | 15 | 51 | -------------------------------------------------------------------------------- /src/qux/web/RichText.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | 40 | -------------------------------------------------------------------------------- /src/qux/web/Segment.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 95 | -------------------------------------------------------------------------------- /src/qux/web/Stepper.vue: -------------------------------------------------------------------------------- 1 | 13 | 16 | 66 | -------------------------------------------------------------------------------- /src/qux/web/Switch.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | 74 | -------------------------------------------------------------------------------- /src/qux/web/TextArea.vue: -------------------------------------------------------------------------------- 1 |