├── .eslintrc.cjs ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── bug_report_accessibility.md │ └── feature_request.md ├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── components │ ├── app │ │ ├── app.html │ │ └── app.ts │ └── click-counter │ │ ├── click-counter-runtime.html │ │ ├── click-counter-runtime.ts │ │ ├── clickCounter.design.module.ts │ │ ├── clickCounter.html │ │ ├── clickCounter.publish.module.ts │ │ ├── clickCounter.runtime.module.ts │ │ ├── clickCounter.ts │ │ ├── clickCounterContract.ts │ │ ├── clickCounterEditor.html │ │ ├── clickCounterEditor.ts │ │ ├── clickCounterHandlers.ts │ │ ├── clickCounterModel.ts │ │ ├── clickCounterModelBinder.ts │ │ ├── clickCounterViewModelBinder.ts │ │ └── index.ts ├── config.design.json ├── config.publish.json ├── config.runtime.json ├── configuration │ └── staticSettingsProvider.ts ├── data │ ├── block-snippets.json │ ├── demo.json │ ├── grid-snippets.json │ ├── icon-fonts.json │ └── style-snippets.json ├── modules.d.ts ├── modules │ ├── demo.design.module.ts │ ├── demo.publish.module.ts │ └── demo.runtime.module.ts ├── persistence │ ├── fileSystemBlobStorage.ts │ ├── fileSystemDataProvider.ts │ ├── fileSystemObjectStorage.ts │ ├── httpDataProvider.ts │ ├── memoryBlobStorage.ts │ └── memoryObjectStorage.ts ├── polyfills.ts ├── routing │ └── staticRouter.ts ├── startup.design.ts ├── startup.publish.ts ├── startup.runtime.ts ├── themes │ ├── designer │ │ ├── assets │ │ │ └── index.html │ │ ├── styles │ │ │ ├── animation.scss │ │ │ ├── arrows.scss │ │ │ ├── balloons.scss │ │ │ ├── blocks.scss │ │ │ ├── buttons.scss │ │ │ ├── confirmation.scss │ │ │ ├── cropper.scss │ │ │ ├── draggables.scss │ │ │ ├── dropbucket.scss │ │ │ ├── dropzones.scss │ │ │ ├── editors │ │ │ │ ├── boxEditor.scss │ │ │ │ ├── colorPicker.scss │ │ │ │ ├── colorSelector.scss │ │ │ │ ├── fontEditor.scss │ │ │ │ ├── fontSelector.scss │ │ │ │ ├── rowLayoutSelector.scss │ │ │ │ └── videoEditor.scss │ │ │ ├── flex.scss │ │ │ ├── fonts │ │ │ │ ├── OpenSans-Bold.ttf │ │ │ │ ├── OpenSans-BoldItalic.ttf │ │ │ │ ├── OpenSans-Italic.ttf │ │ │ │ ├── OpenSans-Light.ttf │ │ │ │ ├── OpenSans-LightItalic.ttf │ │ │ │ ├── OpenSans-Regular.ttf │ │ │ │ ├── paperbits.eot │ │ │ │ ├── paperbits.svg │ │ │ │ ├── paperbits.ttf │ │ │ │ ├── paperbits.woff │ │ │ │ └── paperbits.woff2 │ │ │ ├── forms.scss │ │ │ ├── grid.scss │ │ │ ├── host.scss │ │ │ ├── icons-toolboxes.scss │ │ │ ├── icons-widgets.scss │ │ │ ├── icons │ │ │ │ ├── icon-accordion.svg │ │ │ │ ├── icon-button.svg │ │ │ │ ├── icon-card.svg │ │ │ │ ├── icon-carousel.svg │ │ │ │ ├── icon-checkbox.svg │ │ │ │ ├── icon-collapsible-panel.svg │ │ │ │ ├── icon-component.svg │ │ │ │ ├── icon-date-input.svg │ │ │ │ ├── icon-email-input.svg │ │ │ │ ├── icon-form.svg │ │ │ │ ├── icon-map.svg │ │ │ │ ├── icon-menu.svg │ │ │ │ ├── icon-multi-line-input.svg │ │ │ │ ├── icon-number-input.svg │ │ │ │ ├── icon-password-input.svg │ │ │ │ ├── icon-picture-gallery.svg │ │ │ │ ├── icon-picture.svg │ │ │ │ ├── icon-range-input.svg │ │ │ │ ├── icon-search-box.svg │ │ │ │ ├── icon-select-input.svg │ │ │ │ ├── icon-splitter.svg │ │ │ │ ├── icon-submit-form-button.svg │ │ │ │ ├── icon-tab-panel.svg │ │ │ │ ├── icon-table.svg │ │ │ │ ├── icon-testimonials.svg │ │ │ │ ├── icon-text-block.svg │ │ │ │ ├── icon-text-input.svg │ │ │ │ ├── icon-time-input.svg │ │ │ │ ├── icon-url-input.svg │ │ │ │ ├── icon-video-player.svg │ │ │ │ └── icon-youtube-player.svg │ │ │ ├── layouts.scss │ │ │ ├── lightbox.scss │ │ │ ├── lists.scss │ │ │ ├── mixins.scss │ │ │ ├── scaffolding.scss │ │ │ ├── scrollbars.scss │ │ │ ├── slider.scss │ │ │ ├── spinners.scss │ │ │ ├── styles.scss │ │ │ ├── tabs.scss │ │ │ ├── thumbnails.scss │ │ │ ├── toasts.scss │ │ │ ├── toolbox.paragraph.scss │ │ │ ├── toolboxes.scss │ │ │ ├── tooltips.scss │ │ │ ├── typography.scss │ │ │ ├── utils.scss │ │ │ ├── variables.scss │ │ │ ├── widgets.scss │ │ │ ├── widgets │ │ │ │ ├── map.scss │ │ │ │ └── video.scss │ │ │ └── workshops.scss │ │ └── svgs │ │ │ ├── arrow.svg │ │ │ └── drag-arrow.svg │ └── website │ │ ├── assets │ │ └── page.html │ │ └── styles │ │ ├── animations.scss │ │ ├── emails │ │ ├── emails.scss │ │ └── layouts.scss │ │ ├── flex.scss │ │ ├── fonts │ │ ├── icons.eot │ │ ├── icons.svg │ │ ├── icons.ttf │ │ ├── icons.woff │ │ └── icons.woff2 │ │ ├── forms.scss │ │ ├── icons.scss │ │ ├── layouts.scss │ │ ├── mixins.scss │ │ ├── navs.scss │ │ ├── reboot.scss │ │ ├── styles.design.scss │ │ ├── styles.scss │ │ ├── text.scss │ │ ├── utils.scss │ │ ├── variables.scss │ │ └── widgets │ │ ├── button.scss │ │ ├── card.scss │ │ ├── carousel.scss │ │ ├── collapsibles.scss │ │ ├── map.scss │ │ ├── picture.scss │ │ ├── popups.scss │ │ ├── tables.scss │ │ ├── tabs.scss │ │ ├── textblock.scss │ │ └── widgets.scss ├── user │ ├── staticRoleService.ts │ └── staticUserService.ts └── utils.ts ├── tsconfig.json ├── webpack.build.js ├── webpack.designer.js ├── webpack.develop.js ├── webpack.publisher.js └── webpack.runtime.js /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: "@typescript-eslint/parser", 4 | plugins: [ 5 | "@typescript-eslint", 6 | ], 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | ], 11 | rules: { 12 | "@typescript-eslint/ban-types": "off", 13 | "@typescript-eslint/no-explicit-any": "off" 14 | } 15 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pfx binary 2 | *.ico binary 3 | *.svg binary 4 | *.jpg binary 5 | *.png binary 6 | *.exe binary 7 | *.eot binary 8 | *.woff binary 9 | *.woff2 binary 10 | *.ttf binary 11 | *.otf binary 12 | *.cdr binary -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_accessibility.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Accessibility bug report 3 | about: Create a report to help us improve 4 | labels: accessibility, bug 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .vscode/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 4, 3 | "editor.insertSpaces": true, 4 | "editor.detectIndentation": false, 5 | "files.exclude": { 6 | "**/dist": true, 7 | "**/node_modules": true, 8 | "**/.vscode": true 9 | }, 10 | "search.exclude": { 11 | "**/dist": true, 12 | "**/node_modules": true, 13 | "**/.vscode": true 14 | }, 15 | "mochaExplorer.files": "**/*.spec.ts", 16 | "mochaExplorer.require": "mocha", 17 | "mochaExplorer.timeout": 30000 18 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at support@paperbits.io. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2020 Paperbits. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paperbits demo 2 | 3 | This repository contains an example of how using Paperbits you can enable advanced content authoring tools in your web apps. 4 | 5 | [Live demo](https://demo.paperbits.io) 6 | 7 | [![Join the chat at https://gitter.im/paperbits/discussions](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/paperbits/discussions) 8 | [![Build status](https://dev.azure.com/paperbits/Paperbits/_apis/build/status/PR%20build%20-%20paperbits-demo)](https://dev.azure.com/paperbits/Paperbits/_build/latest?definitionId=4) 9 | 10 | 11 | 12 | 13 | ## Quick start 14 | To run demo website locally, you'll need to: 15 | 16 | 1. Clone the demo project from GitHub: 17 | ``` 18 | git clone https://github.com/paperbits/paperbits-demo.git 19 | ``` 20 | 21 | 2. Switch into just cloned directory: 22 | 23 | ```bash 24 | cd paperbits-demo 25 | ``` 26 | 27 | 3. Install packages required for work: 28 | 29 | ```bash 30 | npm install 31 | ``` 32 | 33 | 4. Run demo site: 34 | ```bash 35 | npm start 36 | ``` 37 | 38 | ## What's next? 39 | 40 | [Getting started](https://paperbits.io/wiki/get-started) 41 | 42 | [Blog](https://paperbits.io/blog) 43 | 44 | [Contributing guide](https://paperbits.io/contributing) 45 | 46 | 47 | ## License 48 | Use of this source code is governed by an MIT-style license that can be found in the LICENSE file and at https://paperbits.io/license/mit. 49 | 50 | 2022 (c) Copyright Paperbits. All Rights Reserved. 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paperbits-demo", 3 | "version": "0.1.526", 4 | "description": "An example of using Paperbits to build web apps with rich content editing capability.", 5 | "author": "Paperbits", 6 | "license": "MIT", 7 | "keywords": [ 8 | "paperbits", 9 | "demo" 10 | ], 11 | "scripts": { 12 | "start": "webpack serve --open --config webpack.develop.js", 13 | "build": "webpack --config webpack.build.js", 14 | "build-designer": "webpack --config webpack.designer.js", 15 | "build-publisher": "webpack --config webpack.publisher.js", 16 | "build-runtime": "webpack --config webpack.runtime.js", 17 | "publish": "webpack --config webpack.publisher.js && node dist/publisher/index.js && npm run serve-website", 18 | "serve-website": "webpack serve --open --static ./dist/website --no-stats", 19 | "lint": "eslint src/**/*.ts" 20 | }, 21 | "devDependencies": { 22 | "@types/google.maps": "^3.54.2", 23 | "@types/knockout.mapping": "^2.0.38", 24 | "@types/knockout.validation": "0.0.40", 25 | "@types/mime": "^3.0.2", 26 | "@types/node": "^22.10.7", 27 | "@typescript-eslint/eslint-plugin": "^6.7.3", 28 | "@typescript-eslint/parser": "^6.7.3", 29 | "autoprefixer": "10.4.16", 30 | "buffer": "^6.0.3", 31 | "bufferutil": "^4.0.7", 32 | "copy-webpack-plugin": "^11.0.0", 33 | "css-loader": "6.8.1", 34 | "eslint": "^8.50.0", 35 | "file-loader": "^6.2.0", 36 | "html-loader": "^4.2.0", 37 | "install": "^0.13.0", 38 | "mini-css-extract-plugin": "^2.7.6", 39 | "npm": "^11.0.0", 40 | "postcss-loader": "^7.3.3", 41 | "raw-loader": "^4.0.2", 42 | "sass": "^1.68.0", 43 | "sass-loader": "13.3.2", 44 | "stream-browserify": "^3.0.0", 45 | "style-loader": "3.3.3", 46 | "terser": "5.20.0", 47 | "terser-webpack-plugin": "5.3.9", 48 | "ts-loader": "^9.4.4", 49 | "typescript": "^5.2.2", 50 | "url-loader": "4.1.1", 51 | "utf-8-validate": "^6.0.3", 52 | "webpack": "5.88.2", 53 | "webpack-cli": "^5.1.4", 54 | "webpack-dev-server": "4.15.1", 55 | "webpack-merge": "^5.9.0" 56 | }, 57 | "dependencies": { 58 | "@paperbits/common": "0.1.634", 59 | "@paperbits/core": "0.1.634", 60 | "@paperbits/emails": "0.1.634", 61 | "@paperbits/forms": "0.1.634", 62 | "@paperbits/gtm": "0.1.634", 63 | "@paperbits/intercom": "0.1.634", 64 | "@paperbits/styles": "0.1.634", 65 | "@webcomponents/custom-elements": "^1.6.0", 66 | "core-js": "^3.32.2", 67 | "knockout": "^3.5.1" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/app/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/components/app/app.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import template from "./app.html"; 10 | import { Component, OnMounted } from "@paperbits/common/ko/decorators"; 11 | import { Logger } from "@paperbits/common/logging"; 12 | import { EventManager } from "@paperbits/common/events"; 13 | import { ViewManager } from "@paperbits/common/ui/viewManager"; 14 | 15 | @Component({ 16 | selector: "app", 17 | template: template 18 | }) 19 | export class App { 20 | constructor( 21 | private readonly logger: Logger, 22 | private readonly viewManager: ViewManager, 23 | private readonly eventManager: EventManager 24 | ) { } 25 | 26 | @OnMounted() 27 | public async initialize(): Promise { 28 | this.viewManager.setHost({ name: "page-host" }); 29 | this.viewManager.showToolboxes(); 30 | this.logger.trackEvent("Startup", { message: `App started.` }); 31 | 32 | setTimeout(() => this.eventManager.dispatchEvent("displayHint", { 33 | key: "a69b", 34 | content: `When you're in the administrative view, you still can navigate any website hyperlink by clicking on it holding Ctrl (Windows) or ⌘ (Mac) key.` 35 | }), 5000); 36 | } 37 | } -------------------------------------------------------------------------------- /src/components/click-counter/click-counter-runtime.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | 7 | 8 |
9 |
-------------------------------------------------------------------------------- /src/components/click-counter/click-counter-runtime.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as ko from "knockout"; 10 | import template from "./click-counter-runtime.html"; 11 | import { Component, RuntimeComponent, Param, OnMounted, OnDestroyed } from "@paperbits/common/ko/decorators"; 12 | 13 | 14 | @RuntimeComponent({ 15 | selector: "click-counter-runtime" 16 | }) 17 | @Component({ 18 | selector: "click-counter-runtime", 19 | template: template 20 | }) 21 | export class ClickCounterRuntime { 22 | public readonly clickCount: ko.Observable; 23 | 24 | constructor() { 25 | this.clickCount = ko.observable(0); 26 | this.initialCount = ko.observable(0); 27 | } 28 | 29 | @Param() 30 | public readonly initialCount: ko.Observable; 31 | 32 | @OnMounted() 33 | public async initialize(): Promise { 34 | // Your initialization logic 35 | this.clickCount(this.initialCount()); 36 | } 37 | 38 | @OnDestroyed() 39 | public async dispose(): Promise { 40 | // Your cleanup widget logic 41 | } 42 | 43 | public increaseCount(): void { 44 | this.clickCount(this.clickCount() + 1); 45 | } 46 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounter.design.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 10 | import { IWidgetService } from "@paperbits/common/widgets"; 11 | import { KnockoutComponentBinder } from "@paperbits/core/ko/knockoutComponentBinder"; 12 | import { ClickCounter } from "./clickCounter"; 13 | import { ClickCounterEditor } from "./clickCounterEditor"; 14 | import { ClickCounterHandlers } from "./clickCounterHandlers"; 15 | import { ClickCounterModel } from "./clickCounterModel"; 16 | import { ClickCounterModelBinder } from "./clickCounterModelBinder"; 17 | import { ClickCounterViewModelBinder } from "./clickCounterViewModelBinder"; 18 | 19 | export class ClickCounterDesignModule implements IInjectorModule { 20 | public register(injector: IInjector): void { 21 | injector.bind("clickCounter", ClickCounter); 22 | injector.bind("clickCounterEditor", ClickCounterEditor); 23 | injector.bindSingleton("clickCounterModelBinder", ClickCounterModelBinder); 24 | injector.bindSingleton("clickCounterViewModelBinder", ClickCounterViewModelBinder); 25 | injector.bindSingleton("clickCounterHandlers", ClickCounterHandlers); 26 | 27 | const widgetService = injector.resolve("widgetService"); 28 | 29 | widgetService.registerWidget("click-counter", { 30 | modelDefinition: ClickCounterModel, 31 | componentBinder: KnockoutComponentBinder, 32 | componentDefinition: ClickCounter, 33 | modelBinder: ClickCounterModelBinder, 34 | viewModelBinder: ClickCounterViewModelBinder 35 | }); 36 | 37 | widgetService.registerWidgetEditor("click-counter", { 38 | displayName: "Click couter", 39 | iconClass: "widget-icon widget-icon-component", 40 | componentBinder: KnockoutComponentBinder, 41 | componentDefinition: ClickCounterEditor, 42 | handlerComponent: ClickCounterHandlers, 43 | requires: ["html", "js"] 44 | }); 45 | } 46 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounter.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | This is an example widget that is yet to be implemented. You can use it as a scaffold for your own widget. 4 |

5 |

6 | Please refer to documentation to learn about 7 | widget anatomy. 8 |

9 | 10 | 11 |
-------------------------------------------------------------------------------- /src/components/click-counter/clickCounter.publish.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 10 | import { IWidgetService } from "@paperbits/common/widgets"; 11 | import { KnockoutComponentBinder } from "@paperbits/core/ko/knockoutComponentBinder"; 12 | import { ClickCounter } from "./clickCounter"; 13 | import { ClickCounterModel } from "./clickCounterModel"; 14 | import { ClickCounterModelBinder } from "./clickCounterModelBinder"; 15 | import { ClickCounterViewModelBinder } from "./clickCounterViewModelBinder"; 16 | 17 | export class ClickCounterModule implements IInjectorModule { 18 | public register(injector: IInjector): void { 19 | injector.bind("clickCounter", ClickCounter); 20 | injector.bindSingleton("clickCounterModelBinder", ClickCounterModelBinder); 21 | injector.bindSingleton("clickCounterViewModelBinder", ClickCounterViewModelBinder); 22 | 23 | const widgetService = injector.resolve("widgetService"); 24 | 25 | widgetService.registerWidget("click-counter", { 26 | modelDefinition: ClickCounterModel, 27 | componentBinder: KnockoutComponentBinder, 28 | componentDefinition: ClickCounter, 29 | modelBinder: ClickCounterModelBinder, 30 | viewModelBinder: ClickCounterViewModelBinder 31 | }); 32 | } 33 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounter.runtime.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 10 | import { ClickCounterRuntime } from "./click-counter-runtime"; 11 | 12 | export class ClickCounterRuntimeModule implements IInjectorModule { 13 | public register(injector: IInjector): void { 14 | injector.bind("clickCounterRuntime", ClickCounterRuntime); 15 | } 16 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as ko from "knockout"; 10 | import template from "./clickCounter.html"; 11 | import { Component } from "@paperbits/common/ko/decorators"; 12 | import { StyleModel } from "@paperbits/common/styles"; 13 | 14 | @Component({ 15 | selector: "click-counter", 16 | template: template 17 | }) 18 | export class ClickCounter { 19 | public readonly runtimeConfig: ko.Observable; 20 | public readonly styles: ko.Observable; 21 | 22 | constructor() { 23 | this.runtimeConfig = ko.observable(); 24 | this.styles = ko.observable(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterContract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { Contract } from "@paperbits/common"; 10 | import { LocalStyles } from "@paperbits/common/styles"; 11 | 12 | export interface ClickCounterContract extends Contract { 13 | /** 14 | * Initial count. 15 | */ 16 | initialCount: number; 17 | 18 | /** 19 | * Widget local styles. 20 | */ 21 | styles?: LocalStyles; 22 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterEditor.html: -------------------------------------------------------------------------------- 1 |
2 |

This is your widget editor that is yet to be implemented.

3 | 4 |
5 | 6 | Basic 7 | 8 | 9 |
10 | 15 | 16 |
17 |
18 | 19 |
20 | 21 | Background 22 | 23 | 24 |
25 |
-------------------------------------------------------------------------------- /src/components/click-counter/clickCounterEditor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as ko from "knockout"; 10 | import template from "./clickCounterEditor.html"; 11 | import { ClickCounterModel } from "./clickCounterModel"; 12 | import { Component, OnMounted, Param, Event } from "@paperbits/common/ko/decorators"; 13 | import { WidgetEditor } from "@paperbits/common/widgets"; 14 | import { ChangeRateLimit } from "@paperbits/common/ko/consts"; 15 | import { BackgroundStylePluginConfig } from "@paperbits/styles/plugins"; 16 | import { StyleHelper } from "@paperbits/styles"; 17 | 18 | @Component({ 19 | selector: "click-counter-editor", 20 | template: template 21 | }) 22 | export class ClickCounterEditor implements WidgetEditor { 23 | public readonly initialCount: ko.Observable; 24 | public readonly background: ko.Observable; 25 | 26 | constructor() { 27 | this.initialCount = ko.observable(0); 28 | this.background = ko.observable(); 29 | } 30 | 31 | @Param() 32 | public model: ClickCounterModel; 33 | 34 | @Event() 35 | public onChange: (model: ClickCounterModel) => void; 36 | 37 | @OnMounted() 38 | public async initialize(): Promise { 39 | /* 40 | This method is called after component created. At this moment all the parameters, 41 | includinig "model", are available. 42 | */ 43 | 44 | this.initialCount(this.model.initialCount); 45 | this.initialCount 46 | .extend(ChangeRateLimit) 47 | .subscribe(this.applyChanges); 48 | 49 | const backgroundStyleConfig = StyleHelper.getPluginConfigForLocalStyles(this.model.styles, "background"); 50 | this.background(backgroundStyleConfig); 51 | } 52 | 53 | private applyChanges(): void { 54 | this.model.initialCount = this.initialCount(); 55 | this.onChange(this.model); 56 | } 57 | 58 | public onBackgroundChange(pluginConfig: BackgroundStylePluginConfig): void { 59 | StyleHelper.setPluginConfigForLocalStyles(this.model.styles, "background", pluginConfig); 60 | this.onChange(this.model); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterHandlers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IWidgetHandler } from "@paperbits/common/editing"; 10 | import { ClickCounterModel } from "./clickCounterModel"; 11 | 12 | 13 | export class ClickCounterHandlers implements IWidgetHandler { 14 | public async getWidgetModel(): Promise { 15 | return new ClickCounterModel(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterModel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { LocalStyles } from "@paperbits/common/styles"; 10 | 11 | export class ClickCounterModel { 12 | /** 13 | * Inital count. 14 | */ 15 | public initialCount: number; 16 | 17 | /** 18 | * Widget local styles. 19 | */ 20 | public styles: LocalStyles; 21 | 22 | constructor() { 23 | this.initialCount = 0; 24 | this.styles = { 25 | appearance: "components/card/default" 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterModelBinder.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IModelBinder } from "@paperbits/common/editing"; 10 | import { ClickCounterModel } from "./clickCounterModel"; 11 | import { Contract } from "@paperbits/common"; 12 | import { ClickCounterContract } from "./clickCounterContract"; 13 | 14 | export class ClickCounterModelBinder implements IModelBinder { 15 | public canHandleContract(contract: Contract): boolean { 16 | return contract.type === "click-counter"; 17 | } 18 | 19 | public canHandleModel(model: ClickCounterModel): boolean { 20 | return model instanceof ClickCounterModel; 21 | } 22 | 23 | public async contractToModel(contract: ClickCounterContract): Promise { 24 | const model = new ClickCounterModel(); 25 | model.initialCount = contract.initialCount; 26 | model.styles = contract.styles; 27 | return model; 28 | } 29 | 30 | public modelToContract(model: ClickCounterModel): Contract { 31 | const contract: ClickCounterContract = { 32 | type: "click-counter", 33 | initialCount: model.initialCount, 34 | styles: model.styles 35 | }; 36 | 37 | return contract; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/click-counter/clickCounterViewModelBinder.ts: -------------------------------------------------------------------------------- 1 | import { ViewModelBinder, WidgetState } from "@paperbits/common/widgets"; 2 | import { ClickCounterModel } from "./clickCounterModel"; 3 | import { ClickCounter } from "./clickCounter"; 4 | import { StyleCompiler } from "@paperbits/common/styles"; 5 | 6 | export class ClickCounterViewModelBinder implements ViewModelBinder { 7 | constructor(private readonly styleCompiler: StyleCompiler) { } 8 | 9 | public async modelToState(model: ClickCounterModel, state: WidgetState): Promise { 10 | if (model.styles) { 11 | state.styles = await this.styleCompiler.getStyleModelAsync(model.styles); 12 | } 13 | 14 | state.runtimeConfig = { initialCount: model.initialCount }; 15 | } 16 | 17 | public stateToInstance(state: WidgetState, componentInstance: ClickCounter): void { 18 | componentInstance.styles(state.styles); 19 | componentInstance.runtimeConfig(JSON.stringify(state.runtimeConfig)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/components/click-counter/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./clickCounterHandlers"; 2 | export * from "./clickCounterModel"; 3 | export * from "./clickCounterModelBinder"; 4 | -------------------------------------------------------------------------------- /src/config.design.json: -------------------------------------------------------------------------------- 1 | { 2 | "firebase": { 3 | "apiKey": "< your API key >", 4 | "authDomain": "< your domain >.firebaseapp.com", 5 | "databaseURL": "https://< your database URL >.firebaseio.com", 6 | "projectId": "< your project ID >", 7 | "storageBucket": "< your storage bucket >.appspot.com", 8 | "databaseRootKey": "/", 9 | "storageBasePath": "/", 10 | "auth": { 11 | "basic": { 12 | "email": "< user email >", 13 | "password": "< user password >" 14 | } 15 | } 16 | }, 17 | "features": { 18 | "seo": true, 19 | "localization": true, 20 | "preview": true 21 | }, 22 | "resources": { 23 | "iconsFonts": "/data/icon-fonts.json", 24 | "gridSnippets": "/data/grid-snippets.json", 25 | "blockSnippets": "/data/block-snippets.json" 26 | }, 27 | "environment": "design" 28 | } -------------------------------------------------------------------------------- /src/config.publish.json: -------------------------------------------------------------------------------- 1 | { 2 | "firebase": { 3 | "apiKey": "< your API key >", 4 | "authDomain": "< your domain >.firebaseapp.com", 5 | "databaseURL": "https://< your database URL >.firebaseio.com", 6 | "projectId": "< your project ID >", 7 | "storageBucket": "< your storage bucket >.appspot.com", 8 | "auth": { 9 | "serviceAccount": { 10 | "type": "service_account", 11 | "project_id": "...", 12 | "private_key_id": "...", 13 | "private_key": "...", 14 | "client_email": "...", 15 | "client_id": "...", 16 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 17 | "token_uri": "https://oauth2.googleapis.com/token", 18 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 19 | "client_x509_cert_url": "..." 20 | } 21 | }, 22 | "databaseRootKey": "/", 23 | "storageBasePath": "/" 24 | }, 25 | "emailTemplates": { 26 | "permalinkBaseUrl": "https://paperbits.io", 27 | "mediaBaseUrl": "https://cdn.paperbits.io" 28 | }, 29 | "googleMaps": { 30 | "apiKey": "< your API key >" 31 | }, 32 | "googleFonts": { 33 | "apiKey": "< your API key >" 34 | }, 35 | "intercom": { 36 | "appId": "< your App ID >" 37 | }, 38 | "googleTagManager": { 39 | "containerId": "< your container ID >" 40 | }, 41 | "environment": "publish" 42 | } 43 | -------------------------------------------------------------------------------- /src/config.runtime.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": "runtime" 3 | } -------------------------------------------------------------------------------- /src/configuration/staticSettingsProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { ISettingsProvider } from "@paperbits/common/configuration"; 10 | import * as Objects from "@paperbits/common/objects"; 11 | import * as Utils from "../utils"; 12 | 13 | 14 | export class StaticSettingsProvider implements ISettingsProvider { 15 | private configuration: Object; 16 | private loadingPromise: Promise; 17 | 18 | constructor(private readonly settingsPath: string) { } 19 | 20 | public async getSetting(name: string): Promise { 21 | await this.getSettings(); 22 | return Objects.getObjectAt(name, this.configuration); 23 | } 24 | 25 | public async setSetting(name: string, value: T): Promise { 26 | await this.getSettings(); 27 | this.configuration[name] = value; 28 | } 29 | 30 | public getSettings(): Promise { 31 | if (!this.loadingPromise) { 32 | this.loadingPromise = this.loadSettings(); 33 | } 34 | 35 | return this.loadingPromise; 36 | } 37 | 38 | private async loadSettings(): Promise { 39 | const configFileContent = await Utils.loadFileAsString(this.settingsPath); 40 | this.configuration = JSON.parse(configFileContent); 41 | return this.configuration; 42 | } 43 | } -------------------------------------------------------------------------------- /src/data/icon-fonts.json: -------------------------------------------------------------------------------- 1 | { 2 | "fonts": [ 3 | { 4 | "displayName": "Font Awesome icons", 5 | "key": "fonts/default", 6 | "variants": [ 7 | { 8 | "file": "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/webfonts/fa-regular-400.ttf", 9 | "style": "normal", 10 | "weight": "400" 11 | } 12 | ] 13 | }, 14 | { 15 | "displayName": "Material Design icons", 16 | "variants": [ 17 | { 18 | "file": "https://cdnjs.cloudflare.com/ajax/libs/material-design-icons/3.0.2/iconfont/MaterialIcons-Regular.ttf", 19 | "style": "normal", 20 | "weight": "400" 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /src/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.html" { 2 | const content: string; 3 | export default content; 4 | } 5 | 6 | declare module "*.raw" { 7 | const content: string; 8 | export default content; 9 | } -------------------------------------------------------------------------------- /src/modules/demo.design.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | 10 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 11 | import { AnchorRouteHandler, HistoryRouteHandler } from "@paperbits/common/routing"; 12 | import { SearchDesignModule } from "@paperbits/core/search/search.design.module"; 13 | import { RoleBasedSecurityDesignModule } from "@paperbits/core/security/roleBasedSecurity.design.module"; 14 | import { App } from "../components/app/app"; 15 | import { ClickCounterDesignModule } from "../components/click-counter/clickCounter.design.module"; 16 | import { HttpDataProvider } from "../persistence/httpDataProvider"; 17 | import { MemoryBlobStorage } from "../persistence/memoryBlobStorage"; 18 | import { MemoryObjectStorage } from "../persistence/memoryObjectStorage"; 19 | import { StaticRoleService } from "../user/staticRoleService"; 20 | 21 | 22 | export class DemoDesignModule implements IInjectorModule { 23 | public register(injector: IInjector): void { 24 | injector.bindSingleton("app", App); 25 | injector.bindSingleton("dataProvider", HttpDataProvider); 26 | injector.bindSingleton("blobStorage", MemoryBlobStorage); 27 | injector.bindSingleton("objectStorage", MemoryObjectStorage); 28 | injector.bindSingleton("roleService", StaticRoleService); 29 | injector.bindToCollection("autostart", HistoryRouteHandler); 30 | injector.bindToCollection("autostart", AnchorRouteHandler); 31 | injector.bindModule(new SearchDesignModule()); 32 | injector.bindModule(new ClickCounterDesignModule()); 33 | injector.bindModule(new RoleBasedSecurityDesignModule()); 34 | } 35 | } -------------------------------------------------------------------------------- /src/modules/demo.publish.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as path from "path"; 10 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 11 | import { MemoryBlobStorage } from "../persistence/memoryBlobStorage"; 12 | import { StaticUserService } from "../user/staticUserService"; 13 | import { FileSystemObjectStorage } from "../persistence/fileSystemObjectStorage"; 14 | import { FileSystemBlobStorage } from "../persistence/fileSystemBlobStorage"; 15 | import { StaticSettingsProvider } from "../configuration/staticSettingsProvider"; 16 | import { StaticRouter } from "../routing/staticRouter"; 17 | import { StaticRoleService } from "../user/staticRoleService"; 18 | import { SearchPublishModule } from "@paperbits/core/search/search.publish.module"; 19 | import { ClickCounterDesignModule } from "../components/click-counter/clickCounter.design.module"; 20 | import { FileSystemDataProvider } from "../persistence/fileSystemDataProvider"; 21 | import { RoleBasedSecurityPublishModule } from "@paperbits/core/security/roleBasedSecurity.publish.module"; 22 | import { IntercomPublishModule } from "@paperbits/intercom/intercom.publish.module"; 23 | import { GoogleTagManagerPublishModule } from "@paperbits/gtm/gtm.publish.module"; 24 | 25 | 26 | export class DemoPublishModule implements IInjectorModule { 27 | constructor( 28 | private readonly dataPath: string, 29 | private readonly settingsPath: string, 30 | private readonly outputBasePath: string 31 | ) { } 32 | 33 | public register(injector: IInjector): void { 34 | injector.bindSingleton("userService", StaticUserService); 35 | injector.bindSingleton("roleService", StaticRoleService); 36 | injector.bindSingleton("router", StaticRouter); 37 | injector.bindSingleton("blobStorage", MemoryBlobStorage); 38 | injector.bindInstance("dataProvider", new FileSystemDataProvider(path.resolve(this.dataPath))); 39 | injector.bindInstance("objectStorage", new FileSystemObjectStorage(path.resolve(this.dataPath))); 40 | injector.bindInstance("outputBlobStorage", new FileSystemBlobStorage(path.resolve(this.outputBasePath))); 41 | injector.bindInstance("settingsProvider", new StaticSettingsProvider(path.resolve(this.settingsPath))); 42 | injector.bindModule(new SearchPublishModule()); 43 | injector.bindModule(new ClickCounterDesignModule()); 44 | injector.bindModule(new IntercomPublishModule()); 45 | injector.bindModule(new GoogleTagManagerPublishModule()); 46 | injector.bindModule(new RoleBasedSecurityPublishModule()); 47 | } 48 | } -------------------------------------------------------------------------------- /src/modules/demo.runtime.module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { IInjector, IInjectorModule } from "@paperbits/common/injection"; 10 | import "@paperbits/core/ko/bindingHandlers/bindingHandlers.component"; 11 | import { RoleBasedSecurityRuntimeModule } from "@paperbits/core/security/roleBasedSecurity.runtime.module"; 12 | import { ClickCounterRuntimeModule } from "../components/click-counter/clickCounter.runtime.module"; 13 | import { StaticRoleService } from "../user/staticRoleService"; 14 | import { StaticUserService } from "../user/staticUserService"; 15 | 16 | export class DemoRuntimeModule implements IInjectorModule { 17 | public register(injector: IInjector): void { 18 | injector.bindModule(new ClickCounterRuntimeModule()); 19 | injector.bindSingleton("userService", StaticUserService); 20 | injector.bindSingleton("roleService", StaticRoleService); 21 | injector.bindModule(new RoleBasedSecurityRuntimeModule()); 22 | } 23 | } -------------------------------------------------------------------------------- /src/persistence/fileSystemBlobStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as fs from "fs"; 10 | import * as path from "path"; 11 | import { IBlobStorage } from "@paperbits/common/persistence"; 12 | 13 | export class FileSystemBlobStorage implements IBlobStorage { 14 | private basePath: string; 15 | 16 | constructor(basePath: string) { 17 | this.basePath = basePath; 18 | } 19 | 20 | public async uploadBlob(blobPath: string, content: Uint8Array): Promise { 21 | const fullpath = `${this.basePath}/${blobPath}`.replace("//", "/"); 22 | 23 | try { 24 | await fs.promises.mkdir(path.dirname(fullpath), { recursive: true }); 25 | await fs.promises.writeFile(fullpath, Buffer.from(content.buffer)); 26 | } 27 | catch (error) { 28 | console.error(error); 29 | } 30 | } 31 | 32 | public downloadBlob(blobPath: string): Promise { 33 | return new Promise((resolve, reject) => { 34 | const fullpath = `${this.basePath}/${blobPath}`.replace("//", "/"); 35 | 36 | fs.readFile(fullpath, (error, buffer: Buffer) => { 37 | if (error) { 38 | reject(error); 39 | return; 40 | } 41 | 42 | const arrayBuffer = new ArrayBuffer(buffer.length); 43 | const unit8Array = new Uint8Array(arrayBuffer); 44 | 45 | for (let i = 0; i < buffer.length; ++i) { 46 | unit8Array[i] = buffer[i]; 47 | } 48 | 49 | resolve(unit8Array); 50 | }); 51 | }); 52 | } 53 | 54 | public async listBlobs(): Promise { 55 | const files = this.listAllFilesInDirectory(this.basePath); 56 | if (files.length > 0) { 57 | return files.map(file => file.split(this.basePath).pop()); 58 | } 59 | return []; 60 | } 61 | 62 | private listAllFilesInDirectory(dir: string): string[] { 63 | const results = []; 64 | 65 | fs.readdirSync(dir).forEach((file) => { 66 | file = dir + "/" + file; 67 | const stat = fs.statSync(file); 68 | 69 | if (stat && stat.isDirectory()) { 70 | results.push(...this.listAllFilesInDirectory(file)); 71 | } else { 72 | results.push(file); 73 | } 74 | 75 | }); 76 | 77 | return results; 78 | } 79 | 80 | public getDownloadUrl(): Promise { 81 | throw new Error("Not supported"); 82 | } 83 | 84 | public async deleteBlob(): Promise { 85 | return null; 86 | } 87 | } -------------------------------------------------------------------------------- /src/persistence/fileSystemDataProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as Utils from "../utils"; 10 | 11 | export class FileSystemDataProvider { 12 | private storageDataObject: Object; 13 | 14 | constructor(private readonly dataPath: string) { } 15 | 16 | protected async getDataObject(): Promise { 17 | if (!this.storageDataObject) { 18 | this.storageDataObject = JSON.parse(await Utils.loadFileAsString(this.dataPath)); 19 | } 20 | return this.storageDataObject; 21 | } 22 | } -------------------------------------------------------------------------------- /src/persistence/fileSystemObjectStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as Utils from "../utils"; 10 | import * as Objects from "@paperbits/common/objects"; 11 | import { MemoryObjectStorage } from "./memoryObjectStorage"; 12 | 13 | export class FileSystemObjectStorage extends MemoryObjectStorage { 14 | private storageDataObject: Object; 15 | 16 | constructor(private readonly dataPath: string) { 17 | super(null); 18 | } 19 | 20 | protected async getDataObject(): Promise { 21 | if (!this.storageDataObject) { 22 | this.storageDataObject = JSON.parse(await Utils.loadFileAsString(this.dataPath)); 23 | } 24 | 25 | return this.storageDataObject; 26 | } 27 | } -------------------------------------------------------------------------------- /src/persistence/httpDataProvider.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { HttpClient } from "@paperbits/common/http"; 10 | 11 | export class HttpDataProvider { 12 | private dataObject: Object; 13 | private dataObjectPromise: Promise; 14 | 15 | constructor(private readonly httpClient: HttpClient) { } 16 | 17 | private async loadData(): Promise { 18 | const response = await this.httpClient.send({ 19 | url: "/data/demo.json", 20 | method: "GET" 21 | }); 22 | 23 | this.dataObject = response.toObject(); 24 | 25 | return this.dataObject; 26 | } 27 | 28 | public async getDataObject(): Promise { 29 | if (this.dataObjectPromise) { 30 | return this.dataObjectPromise; 31 | } 32 | 33 | this.dataObjectPromise = this.loadData(); 34 | 35 | return this.dataObjectPromise; 36 | } 37 | 38 | public async setDataObject(dataObject: Object): Promise { 39 | this.dataObject = dataObject; 40 | } 41 | } -------------------------------------------------------------------------------- /src/persistence/memoryBlobStorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as Utils from "@paperbits/common/utils"; 10 | import { IBlobStorage } from "@paperbits/common/persistence"; 11 | 12 | 13 | /** 14 | * Static blob storage for demo purposes. It stores all the uploaded blobs in memory. 15 | */ 16 | export class MemoryBlobStorage implements IBlobStorage { 17 | private initPromise: Promise; 18 | 19 | constructor(private readonly dataProvider: any) { } 20 | 21 | private async loadData(): Promise { 22 | const dataObject = await this.dataProvider.getDataObject(); 23 | const blobsDataObject = dataObject["blobs"] || {}; 24 | dataObject["blobs"] = blobsDataObject; 25 | 26 | return dataObject; 27 | } 28 | 29 | protected async getDataObject(): Promise { 30 | if (this.initPromise) { 31 | return this.initPromise; 32 | } 33 | 34 | this.initPromise = this.loadData(); 35 | 36 | return this.initPromise; 37 | } 38 | 39 | /** 40 | * Uploads specified content into browser memory and stores it as base64 string. 41 | * @param blobKey 42 | * @param content 43 | * @param contentType 44 | */ 45 | public async uploadBlob(blobKey: string, content: Uint8Array, contentType?: string): Promise { 46 | const dataObject = await this.getDataObject(); 47 | 48 | dataObject[blobKey] = { 49 | contentType: contentType, 50 | content: `data:${contentType};base64,${Utils.arrayBufferToBase64(content)}` 51 | }; 52 | } 53 | 54 | /** 55 | * Returns download URL of uploaded blob. 56 | * @param blobKey 57 | */ 58 | public async getDownloadUrl(blobKey: string): Promise { 59 | const dataObject = await this.getDataObject(); 60 | const blobRecord = dataObject[blobKey]; 61 | 62 | if (!blobRecord) { 63 | return null; 64 | } 65 | 66 | return blobRecord.content; 67 | } 68 | 69 | /** 70 | * Removes specified blob from memory. 71 | * @param blobKey 72 | */ 73 | public async deleteBlob(blobKey: string): Promise { 74 | const dataObject = await this.getDataObject(); 75 | delete dataObject[blobKey]; 76 | } 77 | 78 | public async downloadBlob?(blobKey: string): Promise { 79 | const dataObject = await this.getDataObject(); 80 | const blobRecord = dataObject[blobKey]; 81 | 82 | if (blobRecord) { 83 | const base64 = blobRecord.content.replace("data:font/ttf;base64,", ""); 84 | return Utils.base64ToArrayBuffer(base64); 85 | } 86 | else { 87 | return null; 88 | } 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import "@webcomponents/custom-elements"; 2 | import "core-js/es/array"; 3 | import "core-js/es/object"; 4 | import "core-js/es/promise"; 5 | import "core-js/es/reflect"; 6 | import "core-js/es/symbol"; 7 | import "core-js/web/immediate"; -------------------------------------------------------------------------------- /src/routing/staticRouter.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { Router, Route } from "@paperbits/common/routing"; 10 | 11 | export class StaticRouter implements Router { 12 | private currentUrl: string; 13 | private metadata: Object; 14 | private callbacks: any[]; 15 | 16 | constructor() { 17 | this.currentUrl = "/"; 18 | this.navigateTo = this.navigateTo.bind(this); 19 | this.getCurrentUrl = this.getCurrentUrl.bind(this); 20 | this.getCurrentUrlMetadata = this.getCurrentUrlMetadata.bind(this); 21 | 22 | this.callbacks = []; 23 | } 24 | 25 | public addRouteChangeListener(): void { 26 | // Do nothing; 27 | } 28 | 29 | public removeRouteChangeListener(): void { 30 | // Do nothing; 31 | } 32 | 33 | public addHistoryUpdateListener(): void { 34 | // Do nothing 35 | } 36 | 37 | public removeHistoryUpdateListener(): void { 38 | // Do nothing 39 | } 40 | 41 | public updateHistory(): void { 42 | // Do nothing 43 | } 44 | 45 | public async navigateTo(url: string): Promise { 46 | this.currentUrl = url; 47 | 48 | this.callbacks.forEach(callback => { 49 | callback(); 50 | }); 51 | } 52 | 53 | public getCurrentUrl(): string { 54 | return this.currentUrl; 55 | } 56 | 57 | public getPath(): string { 58 | return this.currentUrl; 59 | } 60 | 61 | public getHash(): string { 62 | return ""; 63 | } 64 | 65 | public getCurrentUrlMetadata(): Object { 66 | return this.metadata; 67 | } 68 | 69 | public getCurrentRoute(): Route { 70 | return { path: this.currentUrl }; 71 | } 72 | } -------------------------------------------------------------------------------- /src/startup.design.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import "./polyfills"; 10 | import * as ko from "knockout"; 11 | import { InversifyInjector } from "@paperbits/common/injection"; 12 | import { CoreDesignModule } from "@paperbits/core/core.design.module"; 13 | import { FormsDesignModule } from "@paperbits/forms/forms.design.module"; 14 | import { EmailsDesignModule } from "@paperbits/emails/emails.design.module"; 15 | import { StylesDesignModule } from "@paperbits/styles/styles.design.module"; 16 | import { OfflineModule } from "@paperbits/common/persistence/offline.module"; 17 | import { DemoDesignModule } from "./modules/demo.design.module"; 18 | 19 | /* Uncomment to enable Firebase module */ 20 | // import { FirebaseModule } from "@paperbits/firebase/firebase.module"; 21 | 22 | /* Initializing dependency injection */ 23 | const injector = new InversifyInjector(); 24 | injector.bindModule(new CoreDesignModule()); 25 | injector.bindModule(new FormsDesignModule()); 26 | injector.bindModule(new EmailsDesignModule()); 27 | injector.bindModule(new StylesDesignModule()); 28 | injector.bindModule(new DemoDesignModule()); 29 | 30 | /* Uncomment to enable Firebase module */ 31 | // injector.bindModule(new FirebaseModule()); 32 | 33 | injector.bindModule(new OfflineModule({ autosave: false })); 34 | injector.resolve("autostart"); 35 | 36 | document.addEventListener("DOMContentLoaded", () => { 37 | setImmediate(() => ko.applyBindings(undefined, document.body)); 38 | }); -------------------------------------------------------------------------------- /src/startup.publish.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { InversifyInjector } from "@paperbits/common/injection"; 10 | import { IPublisher } from "@paperbits/common/publishing"; 11 | import { CacheStorageModule } from "@paperbits/common/publishing/cacheStorageModule"; 12 | import { FormsModule } from "@paperbits/forms/forms.module"; 13 | import { CorePublishModule } from "@paperbits/core/core.publish.module"; 14 | import { EmailsModule } from "@paperbits/emails/emails.module"; 15 | import { EmailsPublishModule } from "@paperbits/emails/emails.publish.module"; 16 | import { StylePublishModule } from "@paperbits/styles/styles.publish.module"; 17 | import { DemoPublishModule } from "./modules/demo.publish.module"; 18 | 19 | /* Uncomment to enable Firebase module */ 20 | // import { FirebaseModule } from "@paperbits/firebase/firebase.admin.module"; 21 | 22 | /* Initializing dependency injection */ 23 | const injector = new InversifyInjector(); 24 | injector.bindModule(new CorePublishModule()); 25 | injector.bindModule(new FormsModule()); 26 | injector.bindModule(new EmailsModule()); 27 | injector.bindModule(new EmailsPublishModule()); 28 | injector.bindModule(new StylePublishModule()); 29 | 30 | /* Initializing Demo module */ 31 | const outputBasePath = "./dist/website"; 32 | const settingsPath = "./dist/publisher/config.json"; 33 | const dataPath = "./dist/publisher/data/demo.json"; 34 | injector.bindModule(new DemoPublishModule(dataPath, settingsPath, outputBasePath)); 35 | 36 | /* Uncomment to enable Firebase module */ 37 | // injector.bindModule(new FirebaseModule()); 38 | 39 | injector.bindModule(new CacheStorageModule()); 40 | injector.resolve("autostart"); 41 | 42 | const publisher = injector.resolve("sitePublisher"); 43 | 44 | /* Running actual publishing */ 45 | publisher.publish() 46 | .then(() => { 47 | console.log("DONE."); 48 | process.exit(); 49 | }) 50 | .catch((error) => { 51 | console.log(error); 52 | process.exit(); 53 | }); -------------------------------------------------------------------------------- /src/startup.runtime.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import "./polyfills"; 10 | import { InversifyInjector } from "@paperbits/common/injection"; 11 | import { CoreRuntimeModule } from "@paperbits/core/core.runtime.module"; 12 | import { StyleRuntimeModule } from "@paperbits/styles/styles.runtime.module"; 13 | import { DemoRuntimeModule } from "./modules/demo.runtime.module"; 14 | 15 | 16 | document.addEventListener("DOMContentLoaded", () => { 17 | /* Initializing dependency injection */ 18 | const injector = new InversifyInjector(); 19 | injector.bindModule(new CoreRuntimeModule()); 20 | injector.bindModule(new StyleRuntimeModule()); 21 | injector.bindModule(new DemoRuntimeModule()); 22 | injector.resolve("autostart"); 23 | }); 24 | -------------------------------------------------------------------------------- /src/themes/designer/assets/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Paperbits 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/themes/designer/styles/animation.scss: -------------------------------------------------------------------------------- 1 | @mixin animation-flyin { 2 | transition: 1s ease-in-out left; 3 | } 4 | 5 | @mixin animation-resize { 6 | transition: height 1s ease; 7 | } 8 | 9 | @keyframes fade-in { 10 | from { 11 | opacity: 0; 12 | } 13 | to { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | @mixin animation-fadein($timeframe: .5s) { 19 | opacity: 0; 20 | animation: fade-in $timeframe linear forwards; 21 | } 22 | 23 | @keyframes shake { 24 | 0% { 25 | -webkit-transform: translate(2px, 1px) rotate(0deg); 26 | } 27 | 10% { 28 | -webkit-transform: translate(-1px, -2px) rotate(-1deg); 29 | } 30 | 20% { 31 | -webkit-transform: translate(-3px, 0px) rotate(1deg); 32 | } 33 | 30% { 34 | -webkit-transform: translate(0px, 2px) rotate(0deg); 35 | } 36 | 40% { 37 | -webkit-transform: translate(1px, -1px) rotate(1deg); 38 | } 39 | 50% { 40 | -webkit-transform: translate(-1px, 2px) rotate(-1deg); 41 | } 42 | 60% { 43 | -webkit-transform: translate(-3px, 1px) rotate(0deg); 44 | } 45 | 70% { 46 | -webkit-transform: translate(2px, 1px) rotate(-1deg); 47 | } 48 | 80% { 49 | -webkit-transform: translate(-1px, -1px) rotate(1deg); 50 | } 51 | 90% { 52 | -webkit-transform: translate(2px, 2px) rotate(0deg); 53 | } 54 | 100% { 55 | -webkit-transform: translate(1px, -2px) rotate(-1deg); 56 | } 57 | } 58 | 59 | @mixin animation-shake { 60 | animation-name: shake; 61 | animation-duration: 0.8s; 62 | transform-origin: 50% 50%; 63 | animation-iteration-count: infinite; 64 | animation-timing-function: linear; 65 | } 66 | 67 | @mixin animation-slideleft { 68 | animation-name: slideleft; 69 | animation-duration: .5s; 70 | animation-timing-function: ease-in-out; 71 | visibility: visible !important; 72 | } 73 | 74 | @keyframes slideleft { 75 | 0% { 76 | transform: translateX(150%); 77 | } 78 | 50% { 79 | transform: translateX(-8%); 80 | } 81 | 65% { 82 | transform: translateX(4%); 83 | } 84 | 80% { 85 | transform: translateX(-4%); 86 | } 87 | 95% { 88 | transform: translateX(2%); 89 | } 90 | 100% { 91 | transform: translateX(0%); 92 | } 93 | } 94 | 95 | .animation-slideright { 96 | animation-name: slideright; 97 | animation-duration: 1s; 98 | animation-timing-function: ease-in-out; 99 | visibility: visible !important; 100 | } 101 | 102 | @keyframes slideright { 103 | 0% { 104 | transform: translateX(-150%); 105 | } 106 | 50% { 107 | transform: translateX(8%); 108 | } 109 | 65% { 110 | transform: translateX(-4%); 111 | } 112 | 80% { 113 | transform: translateX(4%); 114 | } 115 | 95% { 116 | transform: translateX(-2%); 117 | } 118 | 100% { 119 | transform: translateX(0%); 120 | } 121 | } 122 | 123 | .animation-popin { 124 | animation: pop-in .2s ease; 125 | } 126 | 127 | @keyframes pop-in { 128 | 0% { 129 | opacity: 0; 130 | transform: scale(0.5); 131 | } 132 | 100% { 133 | opacity: 1; 134 | transform: scale(1); 135 | } 136 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/arrows.scss: -------------------------------------------------------------------------------- 1 | @import "animation.scss"; 2 | @import "variables.scss"; 3 | @media (min-width: $breakpoint-lg) { 4 | .arrow-self { 5 | background-image: data-uri("../svgs/arrow.svg"); 6 | background-size: auto 80%; 7 | background-repeat: no-repeat; 8 | display: block; 9 | content: ''; 10 | width: 150px; 11 | height: 150px; 12 | } 13 | .arrow { 14 | @include no-pointer-events; 15 | @include animation-shake; 16 | position: absolute; 17 | font-family: Segoe Script; 18 | font-size: 20px; 19 | z-index: 9999; 20 | max-width: 150px; 21 | text-shadow: 0px 0px 10px rgba(255, 255, 255, 1); 22 | .step-number { 23 | display: inline-block; 24 | width: 1.4em; 25 | height: 1.4em; 26 | background: #D2E28B; 27 | border-radius: 50%; 28 | position: absolute; 29 | left: -1.6em; 30 | text-align: center; 31 | color: white; 32 | } 33 | &.arrow-left { 34 | &:after { 35 | transform: rotate(135deg); 36 | } 37 | &:before { 38 | transform: rotate(45deg) scale(-1, 1);; 39 | } 40 | } 41 | &.arrow-right { 42 | &:after { 43 | transform: rotate(-135deg) scale(-1, 1); 44 | } 45 | } 46 | &.arrow-top { 47 | &:after { 48 | @extend .arrow-self; 49 | } 50 | } 51 | &.arrow-bottom { 52 | &:before { 53 | @extend .arrow-self; 54 | } 55 | } 56 | &.arrow-fixed { 57 | position: fixed; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/balloons.scss: -------------------------------------------------------------------------------- 1 | @import "animation.scss"; 2 | @import "variables.scss"; 3 | @import "mixins.scss"; 4 | 5 | $tip-width: 10px; 6 | 7 | .balloon { 8 | @include surface(); 9 | @include animation-fadein(0.3s); 10 | position: fixed; 11 | background-color: $balloon-background; 12 | z-index: $z-index-balloon; 13 | box-shadow: $balloon-shadow; 14 | border-radius: $toolbox-border-radius; 15 | padding: 7px; 16 | display: none; 17 | 18 | &.balloon-is-active { 19 | display: flex; 20 | } 21 | } 22 | 23 | .balloon-tip { 24 | position: fixed; 25 | background-color: $balloon-background; 26 | width: $tip-width; 27 | height: $tip-width; 28 | z-index: $z-index-balloon; 29 | box-shadow: -2px -2px 2px rgba(0, 0, 0, 0.15); 30 | @include animation-fadein(0.3s); 31 | 32 | &.balloon-top { 33 | transform: rotate(45deg); 34 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); 35 | } 36 | 37 | &.balloon-bottom { 38 | transform: rotate(45deg); 39 | box-shadow: -2px -2px 2px rgba(0, 0, 0, 0.15); 40 | } 41 | 42 | &.balloon-left { 43 | transform: rotate(-45deg); 44 | box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); 45 | } 46 | 47 | &.balloon-right { 48 | transform: rotate(-45deg); 49 | box-shadow: -2px -2px 2px rgba(0, 0, 0, 0.15); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/themes/designer/styles/blocks.scss: -------------------------------------------------------------------------------- 1 | 2 | .add-block-container { 3 | width: 100%; 4 | max-height: 600px; 5 | .remove-block { 6 | background-color: rgb(43, 135, 218); 7 | background-repeat: no-repeat; 8 | position: absolute; 9 | top: 10px; 10 | right: 10px; 11 | .paperbits-icon { 12 | padding: 0; 13 | } 14 | } 15 | } 16 | 17 | .block-item { 18 | width: 100%; 19 | height: 120px; 20 | overflow: hidden; 21 | margin: 8px 2px 2px 2px; 22 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 23 | } 24 | 25 | .block-item-container { 26 | width: 1300px; 27 | height: 400px; 28 | transform: scale(0.28); 29 | transform-origin: 0% 0%; 30 | transform-origin: top left; 31 | pointer-events: none; 32 | border: none; 33 | overflow: hidden; 34 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/buttons.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "mixins.scss"; 3 | 4 | .btn { 5 | border: none; 6 | background: none; 7 | color: $color-base; 8 | font-family: $font-family-base; 9 | font-size: $font-size-base; 10 | line-height: $workshop-button-height; 11 | min-width: $workshop-button-height; 12 | text-decoration: none; 13 | text-align: center; 14 | vertical-align: middle; 15 | @include no-select; 16 | 17 | &:disabled { 18 | opacity: 0.2; 19 | } 20 | } 21 | 22 | .btn-default { 23 | padding: 2px 12px; 24 | 25 | &:focus, 26 | &:hover { 27 | @include selection(5px); 28 | outline: none; 29 | color: $color-base; 30 | cursor: pointer; 31 | } 32 | } 33 | 34 | .btn-round { 35 | border: 1px dashed #ccc; 36 | border-radius: 20px; 37 | padding: 2px 12px; 38 | 39 | &:focus { 40 | @include selection(5px, -2px); 41 | outline: none; 42 | } 43 | 44 | &:hover { 45 | background: rgb(238, 238, 238); 46 | outline: none; 47 | color: $color-base; 48 | cursor: pointer; 49 | } 50 | } 51 | 52 | .btn-link { 53 | color: $button-link-color; 54 | text-decoration: none; 55 | } 56 | 57 | .btn-danger, 58 | .btn-danger:hover, 59 | .btn-danger:active { 60 | background: $color-danger; 61 | color: $button-danger-text-color; 62 | } 63 | 64 | .btn-group { 65 | .btn-default { 66 | margin-top: 15px; 67 | margin-right: 5px; 68 | } 69 | } 70 | 71 | .btn-info { 72 | min-width: initial; 73 | border: none; 74 | padding: 0; 75 | position: absolute; 76 | top: 1px; 77 | right: 0; 78 | color: $collapse-button-color; 79 | cursor: pointer; 80 | z-index: 99999; 81 | @extend .paperbits-icon; 82 | @extend .paperbits-alert-circle-i; 83 | 84 | &:active, 85 | &:focus, 86 | &:hover { 87 | @include selection(7px, -5px); 88 | outline: none; 89 | } 90 | } 91 | 92 | .btn-dismiss { 93 | position: absolute; 94 | right: 5px; 95 | top: 5px; 96 | 97 | &:focus, 98 | &:hover { 99 | @include selection(5px); 100 | outline: none; 101 | color: $color-base; 102 | cursor: pointer; 103 | } 104 | } 105 | 106 | .btn-help { 107 | font-size: 0.9em; 108 | border: none; 109 | background: none; 110 | cursor: pointer; 111 | vertical-align: middle; 112 | color: $color-info; 113 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/confirmation.scss: -------------------------------------------------------------------------------- 1 | .confirmation { 2 | max-width: 300px; 3 | padding: 15px; 4 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/draggables.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | 3 | .dragtarget { 4 | width: 100px; 5 | height: 100px; 6 | border: 1px dashed #ccc; 7 | } 8 | 9 | .dragsource { 10 | border: 1px solid #ccc; 11 | position: fixed; 12 | background: #fff; 13 | } 14 | 15 | .dragged { 16 | @include no-select(); 17 | @include no-drag(); 18 | @include no-pointer-events(); 19 | z-index: $z-index-dragging; 20 | cursor: grab; 21 | position: fixed; 22 | background: #fff; 23 | } 24 | 25 | .placeholder { 26 | border: 2px dashed #ccc; 27 | border: 2px dashed darken($toolbox-background, 20%); 28 | transition: height .5s ease-in-out; 29 | position: relative; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/themes/designer/styles/dropbucket.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "toolboxes.scss"; 3 | @import "animation.scss"; 4 | @import "buttons.scss"; 5 | 6 | dropbucket { 7 | right: 20px; 8 | bottom: 20px; 9 | position: fixed; 10 | z-index: $z-index-base; 11 | 12 | .btn-dismiss { 13 | padding: 0; 14 | margin: 0; 15 | top: 0; 16 | right: 0; 17 | } 18 | 19 | .droppedcontent { 20 | @include surface(); 21 | @include animation-fadein(); 22 | position: relative; 23 | border-radius: $toolbox-border-radius; 24 | display: flex; 25 | flex-direction: column; 26 | } 27 | 28 | .droppedcontent-details { 29 | @include text-overflow(); 30 | } 31 | 32 | .droppedcontent-preview { 33 | width: 280px; 34 | height: 150px; 35 | background-image: data-uri("../svgs/icons-unknown.svg"); 36 | background-repeat: no-repeat; 37 | background-position: center; 38 | background-size: contain; 39 | display: block; 40 | margin: 10px 0; 41 | } 42 | 43 | .list-item { 44 | position: relative; 45 | } 46 | 47 | .btn { 48 | height: 100%; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/themes/designer/styles/dropzones.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "mixins.scss"; 3 | 4 | [alignment] { 5 | position: absolute; 6 | top: initial; 7 | left: initial; 8 | right: initial; 9 | bottom: initial; 10 | width: 14px; 11 | height: 14px; 12 | display: block; 13 | cursor: pointer; 14 | 15 | polygon { 16 | fill: transparent; 17 | stroke: $color-base; 18 | stroke-width: 1; 19 | @include tabable(); 20 | } 21 | 22 | &.active { 23 | polygon { 24 | fill: $arrow-active-color; 25 | stroke: $arrow-active-color; 26 | } 27 | } 28 | 29 | &[alignment="top left"] { 30 | transform: rotate(270deg); 31 | top: 0; 32 | left: 0; 33 | } 34 | 35 | &[alignment="top center"] { 36 | top: 0; 37 | left: 50%; 38 | bottom: initial; 39 | transform: rotate(317deg); 40 | } 41 | 42 | &[alignment="top right"] { 43 | top: 0; 44 | right: 0; 45 | } 46 | 47 | &[alignment="center left"] { 48 | top: 50%; 49 | left: 0; 50 | transform: rotate(225deg); 51 | } 52 | 53 | &[alignment="center right"] { 54 | right: 0; 55 | top: 50%; 56 | transform: rotate(45deg); 57 | } 58 | 59 | &[alignment="bottom left"] { 60 | bottom: 0; 61 | left: 0; 62 | transform: rotate(180deg); 63 | } 64 | 65 | &[alignment="bottom center"] { 66 | bottom: 0; 67 | transform: rotate(135deg); 68 | left: 50%; 69 | } 70 | 71 | &[alignment="bottom right"] { 72 | bottom: 0; 73 | right: 0; 74 | transform: rotate(90deg); 75 | } 76 | } 77 | 78 | .dropzone { 79 | margin: 10px 0; 80 | 81 | .dropzone-controls { 82 | padding: 12px; 83 | } 84 | 85 | .dropzone-showbox { 86 | width: 100%; 87 | height: 150px; 88 | position: relative; 89 | display: block; 90 | border: 1px solid $form-control-border; 91 | @include tabable(); 92 | @include pseudo-transparent-bckg(); 93 | } 94 | 95 | .dropzone-footer { 96 | display: block; 97 | height: 1.5em; 98 | } 99 | } 100 | 101 | .dropzone-square { 102 | width: 60px; 103 | height: 60px; 104 | outline: 1px solid $form-control-border; 105 | outline-offset: 4px; 106 | margin: 6px; 107 | } 108 | -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/boxEditor.scss: -------------------------------------------------------------------------------- 1 | @use "sass:math"; 2 | 3 | $box-model-spacing: 30px; 4 | $box-model-spacing-from-border: 1px; 5 | $box-model-border-width: 4px; 6 | 7 | .box-model { 8 | font-family: monospace; 9 | 10 | .box-model-control { 11 | border: none; 12 | position: absolute; 13 | width: $box-model-spacing - 2px; 14 | text-align: center; 15 | background: transparent; 16 | font-family: monospace; 17 | border: 1px dashed #ccc; 18 | 19 | &:focus { 20 | outline: 1px solid #ccc; 21 | } 22 | } 23 | 24 | .box-model-control-top { 25 | top: $box-model-spacing-from-border; 26 | left: 50%; 27 | transform: translateX(-50%); 28 | } 29 | 30 | .box-model-control-left { 31 | left: $box-model-spacing-from-border; 32 | top: 50%; 33 | transform: translateY(-50%); 34 | } 35 | 36 | .box-model-control-right { 37 | right: $box-model-spacing-from-border; 38 | top: 50%; 39 | transform: translateY(-50%); 40 | } 41 | 42 | .box-model-control-bottom { 43 | bottom: $box-model-spacing-from-border; 44 | left: 50%; 45 | transform: translateX(-50%); 46 | } 47 | 48 | .box-model-control-top-left { 49 | top: math.div(-$box-model-border-width, 2); 50 | left: math.div(-$box-model-border-width, 2); 51 | } 52 | 53 | .box-model-control-top-right { 54 | top: math.div(-$box-model-border-width, 2); 55 | right: math.div(-$box-model-border-width, 2); 56 | } 57 | 58 | .box-model-control-bottom-left { 59 | bottom: math.div(-$box-model-border-width, 2); 60 | left: math.div(-$box-model-border-width, 2); 61 | } 62 | 63 | .box-model-control-bottom-right { 64 | bottom: math.div(-$box-model-border-width, 2); 65 | right: math.div(-$box-model-border-width, 2); 66 | } 67 | 68 | .box-model-group { 69 | @include fit; 70 | display: none; 71 | } 72 | 73 | .box-model-enabled { 74 | > .box-model-group { 75 | display: block; 76 | } 77 | } 78 | 79 | .box-model-margin { 80 | &.box-model-enabled { 81 | padding: 20px 30px; 82 | } 83 | 84 | > .box-model-group { 85 | border: 1px dashed #d8e60a; 86 | } 87 | } 88 | 89 | .box-model-padding { 90 | &.box-model-enabled { 91 | padding: $box-model-spacing; 92 | margin: 5px; 93 | 94 | > .box-model-group { 95 | border: 1px dotted #ccc; 96 | } 97 | } 98 | } 99 | 100 | .box-model-content { 101 | border: 1px dashed #54a9ff; 102 | min-width: 80px; 103 | height: 60px; 104 | text-align: center; 105 | line-height: 60px; 106 | } 107 | 108 | .box-model-label { 109 | position: absolute; 110 | top: 0; 111 | left: 0; 112 | line-height: $box-model-spacing; 113 | padding: 0 5px; 114 | } 115 | 116 | .box-model-border { 117 | &.box-model-enabled { 118 | padding: 20px; 119 | margin: 20px; 120 | 121 | > .box-model-group { 122 | border: $box-model-border-width solid #737373; 123 | position: absolute; 124 | top: -4px; 125 | bottom: -4px; 126 | left: -4px; 127 | right: -4px; 128 | 129 | & > .box-model-control { 130 | background: #737373; 131 | border-radius: 4px; 132 | color: #fff; 133 | 134 | &.box-model-control-top { 135 | top: math.div(-$box-model-border-width, 2); 136 | transform: translate(-50%, -50%); 137 | } 138 | 139 | &.box-model-control-left { 140 | left: math.div(-$box-model-border-width, 2); 141 | transform: translate(-50%, -50%); 142 | } 143 | 144 | &.box-model-control-right { 145 | right: math.div(-$box-model-border-width, 2); 146 | transform: translate(50%, -50%); 147 | } 148 | 149 | &.box-model-control-bottom { 150 | bottom: math.div(-$box-model-border-width, 2); 151 | transform: translate(-50%, 50%); 152 | } 153 | 154 | &.box-model-control-top-left { 155 | transform: translate(-50%, -50%); 156 | } 157 | 158 | &.box-model-control-top-right { 159 | transform: translate(50%, -50%); 160 | } 161 | 162 | &.box-model-control-bottom-left { 163 | transform: translate(-50%, 50%); 164 | } 165 | 166 | &.box-model-control-bottom-right { 167 | transform: translate(50%, 50%); 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/colorSelector.scss: -------------------------------------------------------------------------------- 1 | 2 | .pseudo-transparent-bckg { 3 | position: relative; 4 | overflow: hidden; 5 | @include pseudo-transparent-bckg(); 6 | } 7 | 8 | .palette { 9 | list-style: none; 10 | padding: 0; 11 | max-width: 250px; 12 | 13 | .color-box { 14 | float: left; 15 | position: relative; 16 | cursor: pointer; 17 | 18 | &.selected:before { 19 | position: absolute; 20 | content: ""; 21 | border: 2px solid #4c5866; 22 | border-radius: 50%; 23 | width: 36px; 24 | height: 36px; 25 | pointer-events: none; 26 | top: 50%; 27 | left: 50%; 28 | transform: translate(-50%, -50%); 29 | } 30 | 31 | &:focus, 32 | &:hover { 33 | outline: none; 34 | @include selection(10px); 35 | } 36 | 37 | .color-box-wrapper { 38 | position: relative; 39 | display: block; 40 | border: 0.2px solid #ccc; 41 | border-radius: 50%; 42 | box-shadow: inset 1px 1px 1px 0 rgba(0, 0, 0, 0.3); 43 | width: 30px; 44 | height: 30px; 45 | margin: 10px; 46 | padding: 0; 47 | line-height: 30px; 48 | overflow: hidden; 49 | text-align: center; 50 | 51 | &.pseudo-transparent-bckg { 52 | @include pseudo-transparent-bckg(); 53 | } 54 | } 55 | 56 | .color { 57 | @include fit(); 58 | display: block; 59 | text-align: center; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/fontEditor.scss: -------------------------------------------------------------------------------- 1 | .font-variant-item { 2 | border-bottom: 1px solid #ccc; 3 | padding: 10px; 4 | 5 | &:last-child { 6 | border-bottom: none; 7 | } 8 | 9 | .font-variant-display { 10 | text-decoration: none; 11 | color: $color-base; 12 | font-size: 40px; 13 | overflow: hidden; 14 | } 15 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/fontSelector.scss: -------------------------------------------------------------------------------- 1 | .fonts { 2 | list-style: none; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | .font { 8 | font-size: 20px; 9 | text-decoration: none; 10 | color: $color-base; 11 | padding: 5px 10px; 12 | 13 | &.font-italic { 14 | font-style: italic; 15 | } 16 | 17 | &.font-thin { 18 | font-weight: 100; 19 | } 20 | 21 | &.font-extra-light { 22 | font-weight: 200; 23 | } 24 | 25 | &.font-light { 26 | font-weight: 300; 27 | } 28 | 29 | &.font-regular { 30 | font-weight: 400; 31 | } 32 | 33 | &.font-medium { 34 | font-weight: 500; 35 | } 36 | 37 | &.font-semi-bold { 38 | font-weight: 600; 39 | } 40 | 41 | &.font-bold { 42 | font-weight: 700; 43 | } 44 | 45 | &.font-extra-bold { 46 | font-weight: 800; 47 | } 48 | 49 | &.font-black { 50 | font-weight: 900; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/rowLayoutSelector.scss: -------------------------------------------------------------------------------- 1 | .row-layout-selector { 2 | width: 400px; 3 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/editors/videoEditor.scss: -------------------------------------------------------------------------------- 1 | .video-player-editor { 2 | position: fixed; 3 | top: 20px; 4 | left: 100px; 5 | 6 | video { 7 | width: 100%; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/themes/designer/styles/flex.scss: -------------------------------------------------------------------------------- 1 | .flex { 2 | display: flex; 3 | justify-content: flex-start; 4 | align-content: flex-start; 5 | min-height: 0; // fixes issue with oveflow in Edge, FF and other browsers 6 | 7 | &.flex-row { 8 | flex-direction: row; 9 | } 10 | 11 | &.flex-column { 12 | flex-direction: column; 13 | } 14 | 15 | .flex-item { 16 | flex: 0 1 auto; 17 | align-self: auto; 18 | 19 | &.flex-item-grow { 20 | flex: 1 1 auto; 21 | } 22 | } 23 | 24 | .flex-wrap { 25 | flex-wrap: wrap; 26 | } 27 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-Light.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/paperbits.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/paperbits.eot -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/paperbits.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/paperbits.ttf -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/paperbits.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/paperbits.woff -------------------------------------------------------------------------------- /src/themes/designer/styles/fonts/paperbits.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/designer/styles/fonts/paperbits.woff2 -------------------------------------------------------------------------------- /src/themes/designer/styles/grid.scss: -------------------------------------------------------------------------------- 1 | .snippet-grid { 2 | display: grid; 3 | grid-template-columns: repeat(12, 10px); 4 | grid-template-rows: repeat(12, 10px); 5 | row-gap: 0 !important; 6 | column-gap: 0 !important; 7 | margin: 5px !important; 8 | width: 80px !important; 9 | height: 50px !important; 10 | float: left; 11 | padding: 2px; 12 | 13 | &:hover, 14 | &:focus { 15 | @include selection(15px, -5px); 16 | cursor: pointer; 17 | } 18 | } 19 | 20 | .snippet-grid-cell { 21 | background: #d9d9d9; 22 | outline: 1px solid gray; 23 | min-width: 10px; 24 | min-height: 10px; 25 | margin: 2px; 26 | 27 | &[future-role="article"] { 28 | background: #bfbfbf; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/themes/designer/styles/host.scss: -------------------------------------------------------------------------------- 1 | .host { 2 | height: 100%; 3 | width: 100%; 4 | border: 0; 5 | padding: 0; 6 | margin: 0; 7 | // box-shadow: 0 5px 10px #c6c6c6; 8 | } 9 | 10 | .viewport { 11 | position: fixed; 12 | top: 0; 13 | left: 0; 14 | right: 0; 15 | bottom: 0; 16 | padding: 0; 17 | background: #ececec; 18 | 19 | &.viewport-xl { 20 | padding-left: 0; 21 | padding-right: 0; 22 | } 23 | 24 | &.viewport-lg { 25 | padding-left: calc((100% - #{$breakpoint-xl - 1})/2); 26 | padding-right: calc((100% - #{$breakpoint-xl - 1})/2); 27 | } 28 | 29 | &.viewport-md { 30 | padding-left: calc((100% - #{$breakpoint-lg - 1})/2); 31 | padding-right: calc((100% - #{$breakpoint-lg - 1})/2); 32 | } 33 | 34 | &.viewport-sm { 35 | padding-left: calc((100% - #{$breakpoint-md - 1})/2); 36 | padding-right: calc((100% - #{$breakpoint-md - 1})/2); 37 | } 38 | 39 | &.viewport-xs { 40 | padding-left: calc((100% - #{$breakpoint-sm - 1})/2); 41 | padding-right: calc((100% - #{$breakpoint-sm - 1})/2); 42 | } 43 | 44 | &.viewport-zoomout .host { 45 | transform: scale(0.5); 46 | transform-origin: center top 0; 47 | height: 200%; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons-toolboxes.scss: -------------------------------------------------------------------------------- 1 | .paperbits-icon { 2 | display: inline-block; 3 | font: normal normal normal 1em/1 "paperbits"; 4 | font-size: 1.1em; 5 | line-height: inherit; 6 | text-transform: none; 7 | flex-shrink: 0; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | 12 | /* size examples - relative units */ 13 | .paperbits-sm { 14 | font-size: 0.8em; 15 | } 16 | 17 | .paperbits-lg { 18 | font-size: 1.2em; 19 | } 20 | 21 | /* size examples - absolute units */ 22 | .paperbits-16 { 23 | font-size: 16px; 24 | } 25 | 26 | .paperbits-32 { 27 | font-size: 32px; 28 | } 29 | 30 | /* rotate the icon infinitely */ 31 | .paperbits-is-spinning { 32 | animation: paperbits-spin 1s infinite linear; 33 | } 34 | 35 | @keyframes paperbits-spin { 36 | 0% { 37 | transform: rotate(0deg); 38 | } 39 | 100% { 40 | transform: rotate(360deg); 41 | } 42 | } 43 | 44 | /* transform */ 45 | .paperbits-rotate-90 { 46 | transform: rotate(90deg); 47 | } 48 | 49 | .paperbits-rotate-180 { 50 | transform: rotate(180deg); 51 | } 52 | 53 | .paperbits-rotate-270 { 54 | transform: rotate(270deg); 55 | } 56 | 57 | .paperbits-flip-y { 58 | transform: scaleY(-1); 59 | } 60 | 61 | .paperbits-flip-x { 62 | transform: scaleX(-1); 63 | } 64 | 65 | /* icons */ 66 | $icons: ( 67 | "layout-11": "ea03", 68 | "align-center": "ea09", 69 | "align-justify": "ea0a", 70 | "align-left-2": "ea0b", 71 | "align-right-2": "ea0c", 72 | "size": "ea0d", 73 | "edit-2": "ea0e", 74 | "code": "ea0f", 75 | "list-bullet": "ea10", 76 | "list-numbers": "ea11", 77 | "bold": "ea12", 78 | "alert-circle-i": "ea17", 79 | "chat-45-2": "ea18", 80 | "drop": "ea1a", 81 | "m-location": "ea1d", 82 | "mobile": "ea1f", 83 | "desktop-screen": "ea20", 84 | "mobile-landscape": "ea22", 85 | "tablet-2": "ea24", 86 | "wifi-off": "ea25", 87 | "wifi": "ea26", 88 | "ban": "ea27", 89 | "single-content-03": "ea28", 90 | "single-copy-04": "ea29", 91 | "mirror-2": "ea2b", 92 | "image-2": "ea31", 93 | "paint-bucket-40": "ea32", 94 | "palette": "ea33", 95 | "menu-34": "ea36", 96 | "check-2": "ea38", 97 | "preferences-circle": "ea39", 98 | "underline": "ea3a", 99 | "link-69-2": "ea3d", 100 | "color": "ea3f", 101 | "anchor": "ea40", 102 | "italic": "ea41", 103 | "square-upload": "ea42", 104 | "square-download": "ea43", 105 | "simple-add": "ea44", 106 | "simple-remove": "ea45", 107 | "circle-delete": "ea47", 108 | "circle-add": "ea48", 109 | "enlarge-circle": "ea4a", 110 | "align-bottom": "ea4c", 111 | "align-right": "ea4d", 112 | "align-top": "ea4e", 113 | "align-center-horizontal": "ea4f", 114 | "align-center-vertical": "ea50", 115 | "align-left": "ea51", 116 | "disperse": "ea52", 117 | "zoom-out": "ea53", 118 | "zoom-in": "ea54", 119 | "trash-simple": "ea55", 120 | "edit-72": "ea56", 121 | "form": "ea57", 122 | "zoom-99": "ea58", 123 | "crop": "ea59", 124 | "enlarge-vertical": "ea5a", 125 | "enlarge-horizontal": "ea5b", 126 | "rotate-right-2": "ea5c", 127 | "rotate-left-2": "ea5d", 128 | "margin-right": "ea5e", 129 | "margin-left": "ea5f", 130 | "alert": "ea60", 131 | "at-sign": "ea63", 132 | "gradient": "ea64", 133 | "polaroid": "ea65", 134 | "marker-2": "ea67", 135 | "undo-25": "ea68", 136 | "redo-26": "ea69", 137 | "floppy-disk": "ea6a", 138 | "upload": "ea6b", 139 | "world": "ea6e", 140 | "wireframe": "ea6f", 141 | "send": "ea70", 142 | "c-question": "ea71", 143 | "layout-11-2": "ea73", 144 | "clapperboard": "ea75", 145 | "single-02": "ea76", 146 | "lightning": "ea77", 147 | "l-search": "ea78", 148 | "small-down": "ea79", 149 | "small-up": "ea7a", 150 | "menu-4": "ea7b", 151 | "tablet-2-2": "ea7c", 152 | "strikethrough": "ea7d", 153 | "divider": "ea7e", 154 | "s-add": "ea7f", 155 | "curved-arrow-left": "ea80", 156 | "puzzle-10": "ea81", 157 | "a-security": "ea82", 158 | "arrow-up": "ea83", 159 | "arrow-down": "ea84", 160 | ); 161 | 162 | @function unicode($str) { 163 | @return unquote('"\\#{$str}"'); 164 | } 165 | 166 | @each $name, $code in $icons { 167 | .paperbits-#{"" + $name}::before { 168 | content: unicode($code); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons-widgets.scss: -------------------------------------------------------------------------------- 1 | .widget-icon { 2 | width: 34px; 3 | height: 21px; 4 | background-repeat: no-repeat; 5 | background-position: center; 6 | display: inline-block; 7 | margin-bottom: 7px; 8 | } 9 | 10 | .widget-icon-accordion { 11 | background-image: url("./icons/icon-accordion.svg"); 12 | } 13 | 14 | .widget-icon-button { 15 | background-image: url("./icons/icon-button.svg"); 16 | } 17 | 18 | .widget-icon-card { 19 | background-image: url("./icons/icon-card.svg"); 20 | } 21 | 22 | .widget-icon-carousel { 23 | background-image: url("./icons/icon-carousel.svg"); 24 | } 25 | 26 | .widget-icon-checkbox { 27 | background-image: url("./icons/icon-checkbox.svg"); 28 | } 29 | 30 | .widget-icon-collapsible-panel { 31 | background-image: url("./icons/icon-collapsible-panel.svg"); 32 | } 33 | 34 | .widget-icon-component { 35 | background-image: url("./icons/icon-component.svg"); 36 | } 37 | 38 | .widget-icon-date-input { 39 | background-image: url("./icons/icon-date-input.svg"); 40 | } 41 | 42 | .widget-icon-email-input { 43 | background-image: url("./icons/icon-email-input.svg"); 44 | } 45 | 46 | .widget-icon-form { 47 | background-image: url("./icons/icon-form.svg"); 48 | } 49 | 50 | .widget-icon-map { 51 | background-image: url("./icons/icon-map.svg"); 52 | } 53 | 54 | .widget-icon-menu { 55 | background-image: url("./icons/icon-menu.svg"); 56 | } 57 | 58 | .widget-icon-multi-line-input { 59 | background-image: url("./icons/icon-multi-line-input.svg"); 60 | } 61 | 62 | .widget-icon-number-input { 63 | background-image: url("./icons/icon-number-input.svg"); 64 | } 65 | 66 | .widget-icon-password-input { 67 | background-image: url("./icons/icon-password-input.svg"); 68 | } 69 | 70 | .widget-icon-picture-gallery { 71 | background-image: url("./icons/icon-picture-gallery.svg"); 72 | } 73 | 74 | .widget-icon-picture { 75 | background-image: url("./icons/icon-picture.svg"); 76 | } 77 | 78 | .widget-icon-range-input { 79 | background-image: url("./icons/icon-range-input.svg"); 80 | } 81 | 82 | .widget-icon-search-box { 83 | background-image: url("./icons/icon-search-box.svg"); 84 | } 85 | 86 | .widget-icon-select-input { 87 | background-image: url("./icons/icon-select-input.svg"); 88 | } 89 | 90 | .widget-icon-splitter { 91 | background-image: url("./icons/icon-splitter.svg"); 92 | } 93 | 94 | .widget-icon-submit-form-button { 95 | background-image: url("./icons/icon-submit-form-button.svg"); 96 | } 97 | 98 | .widget-icon-tab-panel { 99 | background-image: url("./icons/icon-tab-panel.svg"); 100 | } 101 | 102 | .widget-icon-table { 103 | background-image: url("./icons/icon-table.svg"); 104 | } 105 | 106 | .widget-icon-testimonials { 107 | background-image: url("./icons/icon-testimonials.svg"); 108 | } 109 | 110 | .widget-icon-text-block { 111 | background-image: url("./icons/icon-text-block.svg"); 112 | } 113 | 114 | .widget-icon-text-input { 115 | background-image: url("./icons/icon-text-input.svg"); 116 | } 117 | 118 | .widget-icon-time-input { 119 | background-image: url("./icons/icon-time-input.svg"); 120 | } 121 | 122 | .widget-icon-url-input { 123 | background-image: url("./icons/icon-url-input.svg"); 124 | } 125 | 126 | .widget-icon-video-player { 127 | background-image: url("./icons/icon-video-player.svg"); 128 | } 129 | 130 | .widget-icon-youtube-player { 131 | background-image: url("./icons/icon-youtube-player.svg"); 132 | } 133 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-accordion.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-carousel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-checkbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-collapsible-panel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-component.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-date-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-email-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-form.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-map.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-multi-line-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-number-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-password-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-picture-gallery.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-picture.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-range-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-search-box.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-select-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-splitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-submit-form-button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-tab-panel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-table.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-testimonials.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-text-block.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-text-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-time-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-url-input.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-video-player.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/themes/designer/styles/icons/icon-youtube-player.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/themes/designer/styles/layouts.scss: -------------------------------------------------------------------------------- 1 | @use "sass:math"; 2 | @import "mixins.scss"; 3 | @import "variables.scss"; 4 | 5 | 6 | paperbits-code, 7 | paperbits-googlemaps, 8 | paperbits-picture, 9 | paperbits-text, 10 | widget { 11 | display: block; 12 | 13 | &.dragged { 14 | box-shadow: 2px 2px 2px 2px rgba(128, 128, 128, 0.4); 15 | transform: scale(1.05); 16 | transition: transform 0.5s ease-in-out, height 0.5s ease-in-out, width 0.5s ease-in-out; 17 | background: #fff; 18 | opacity: 0.98; 19 | position: fixed; 20 | } 21 | 22 | &:hover { 23 | @include selection(10px); 24 | } 25 | } 26 | 27 | .layout-editor-selection { 28 | position: fixed; 29 | pointer-events: none; 30 | z-index: $z-index-selection; 31 | 32 | &:before { 33 | content: attr(title); 34 | background: $layout-editor-selection-color; 35 | color: #fff; 36 | padding: 5px; 37 | left: -5px; 38 | top: -30px; 39 | position: absolute; 40 | font-size: 8px; 41 | text-transform: uppercase; 42 | font-family: $font-family-base; 43 | opacity: 0.7; 44 | } 45 | 46 | &:after { 47 | @include fit; 48 | content: ""; 49 | display: block; 50 | position: absolute; 51 | } 52 | } 53 | 54 | .layout-editor-splitter { 55 | position: fixed; 56 | pointer-events: none; 57 | z-index: $z-index-selection; 58 | border-color: $layout-editor-splitter-color; 59 | border-style: dashed; 60 | border-width: 0; 61 | opacity: 0.5; 62 | } 63 | 64 | .empty-item { 65 | margin: auto; 66 | width: 50%; 67 | text-align: center; 68 | } 69 | 70 | .fixed { 71 | position: fixed; 72 | z-index: $z-index-base; 73 | } 74 | 75 | .btn-standalone { 76 | color: #ececec; 77 | display: inline-flex; 78 | align-items: center; 79 | justify-content: center; 80 | box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.15); 81 | border: none; 82 | border-radius: 50%; 83 | width: 30px; 84 | height: 30px; 85 | cursor: pointer; 86 | z-index: $z-index-selection + 1; 87 | line-height: 1em; 88 | 89 | .paperbits-icon { 90 | font-size: 0.9em; 91 | } 92 | 93 | &:hover { 94 | transform: scale(1.2); 95 | transition-duration: 0.3s; 96 | z-index: $z-index-selection + 2; 97 | } 98 | } 99 | 100 | .row-cfg { 101 | border-radius: 7px; 102 | border-spacing: 2px; 103 | display: inline-flex; 104 | flex-wrap: nowrap; 105 | justify-content: flex-start; 106 | align-content: stretch; 107 | padding: 0; 108 | line-height: 0; 109 | width: 80px; 110 | white-space: nowrap; 111 | margin: 5px; 112 | 113 | &:hover { 114 | @include selection(15px, -5px); 115 | cursor: pointer; 116 | } 117 | } 118 | 119 | .col-cfg { 120 | height: 30px; 121 | width: 70px; 122 | background: #ccc; 123 | outline: 1px solid gray; 124 | flex: 0 1 auto; 125 | margin: 2px; 126 | } 127 | 128 | .col-cfg-3 { 129 | width: math.div(3 * 100%, 12) 130 | } 131 | 132 | .col-cfg-4 { 133 | width: math.div(4 * 100%, 12) 134 | } 135 | 136 | .col-cfg-6 { 137 | width: math.div(6 * 100%, 12) 138 | } 139 | 140 | .col-cfg-8 { 141 | width: math.div(8 * 100, 12); 142 | } 143 | 144 | .col-cfg-9 { 145 | width: math.div(9 * 100%, 12); 146 | } 147 | 148 | .col-cfg-12 { 149 | width: math.div(12 * 100%, 12) 150 | } 151 | 152 | .section-cfg { 153 | padding: 0; 154 | margin: 5px; 155 | display: inline-block; 156 | border-radius: 7px; 157 | border: 3px solid $color-base; 158 | line-height: 0; 159 | width: 120px; 160 | cursor: pointer; 161 | 162 | &:hover { 163 | @include selection(15px, -10px); 164 | } 165 | } 166 | 167 | .section-container-cfg { 168 | margin: 0 15%; 169 | background: #ece7e7; 170 | display: block; 171 | height: 50px; 172 | border-left: 2px solid $color-base; 173 | border-right: 2px solid $color-base; 174 | } 175 | 176 | .section-fluid-cfg { 177 | margin: 0; 178 | background: #ece7e7; 179 | display: block; 180 | height: 50px; 181 | } 182 | 183 | .pull-right { 184 | float: right; 185 | } 186 | 187 | .pull-left { 188 | float: left; 189 | } 190 | -------------------------------------------------------------------------------- /src/themes/designer/styles/lightbox.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | 3 | // Vars ---------------------------------------------------------------- // 4 | $basicLightbox__background: rgba(0, 0, 0, .8) !default; 5 | $basicLightbox__zIndex: 100000 !default; 6 | $basicLightbox__duration: .4s !default; 7 | $basicLightbox__timing: ease !default; 8 | 9 | // basicLightbox ------------------------------------------------------- // 10 | .basicLightbox { 11 | font-family: $font-family-base; 12 | font-size: $font-size-base; 13 | position: fixed; 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 100vh; 21 | background: $basicLightbox__background; 22 | opacity: .01; // Start with .01 to avoid the repaint that happens from 0 to .01 23 | transition: opacity $basicLightbox__duration $basicLightbox__timing; 24 | z-index: $basicLightbox__zIndex; 25 | will-change: opacity; 26 | 27 | .lightbox-title { 28 | position: sticky; 29 | top: 0px; 30 | left: 0px; 31 | font-size: 20px; 32 | color: white; 33 | text-align: center; 34 | padding: 10px; 35 | } 36 | 37 | .lightbox-img { 38 | 39 | @include pseudo-transparent-bckg(); 40 | max-height: 80vh; 41 | width: auto; 42 | } 43 | 44 | &--visible { 45 | opacity: 1; 46 | } 47 | 48 | &__placeholder { 49 | max-width: 100%; 50 | transform: scale(.9); 51 | transition: transform $basicLightbox__duration $basicLightbox__timing; 52 | z-index: 1; 53 | will-change: transform; 54 | 55 | > img:first-child:last-child, 56 | > video:first-child:last-child, 57 | > iframe:first-child:last-child { 58 | display: block; 59 | position: absolute; 60 | top: 0; 61 | right: 0; 62 | bottom: 0; 63 | left: 0; 64 | margin: auto; 65 | max-width: 95%; 66 | max-height: 95%; 67 | } 68 | 69 | > video:first-child:last-child, 70 | > iframe:first-child:last-child { 71 | pointer-events: auto; 72 | } 73 | 74 | > img:first-child:last-child, 75 | > video:first-child:last-child { 76 | width: auto; 77 | height: auto; 78 | } 79 | } 80 | 81 | &--img &__placeholder, 82 | &--video &__placeholder, 83 | &--iframe &__placeholder { 84 | width: 100%; 85 | height: 100%; 86 | pointer-events: none; 87 | } 88 | 89 | &--visible &__placeholder { 90 | transform: scale(1); 91 | } 92 | 93 | } 94 | 95 | .lightbox-image { 96 | width: 100%; 97 | height: auto; 98 | max-height: 100%; 99 | cursor: pointer; 100 | object-fit: cover; 101 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/lists.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "mixins.scss"; 3 | @import "icons-toolboxes.scss"; 4 | 5 | .list-item-group { 6 | width: 100%; 7 | 8 | .list-item { 9 | flex-basis: unset; 10 | } 11 | 12 | &.dragged { 13 | pointer-events: none; 14 | box-shadow: 0 0 2px 2px rgba(128, 128, 128, 0.4); 15 | transform: scale(1.07); 16 | transition: transform 0.1s ease-in-out; 17 | min-width: 200px; 18 | position: fixed; 19 | } 20 | 21 | &.list-item-group-collapsible { 22 | padding-left: 18px; 23 | } 24 | } 25 | 26 | .list-item-collapsible { 27 | display: flex; 28 | align-items: center; 29 | 30 | .list-item-collapse { 31 | display: block; 32 | cursor: pointer; 33 | background: none; 34 | border: none; 35 | margin: 0; 36 | padding: 0; 37 | width: 16px; 38 | height: 16px; 39 | left: -16px; 40 | position: absolute; 41 | 42 | &:focus, 43 | &:hover { 44 | @include selection(8px, -3px); 45 | outline: none; 46 | cursor: pointer; 47 | } 48 | 49 | .paperbits-icon { 50 | color: $collapse-button-color; 51 | } 52 | } 53 | } 54 | 55 | .list { 56 | width: 300px; 57 | height: 400px; 58 | } 59 | 60 | .list-item { 61 | line-height: 2.2em; 62 | position: relative; 63 | cursor: pointer; 64 | vertical-align: middle; 65 | display: flex; 66 | text-decoration: none; 67 | color: $color-base; 68 | padding: 0 5px; 69 | 70 | .paperbits-icon { 71 | padding: 0 5px; 72 | } 73 | 74 | .list-item-label { 75 | padding: 0 5px; 76 | flex: 1; 77 | text-decoration: none; 78 | color: $color-base; 79 | @include text-overflow(); 80 | } 81 | 82 | &:focus, 83 | &:hover { 84 | outline: none; 85 | } 86 | 87 | &:hover { 88 | @include selection(); 89 | } 90 | 91 | &:focus { 92 | @include selection(); 93 | } 94 | 95 | &.selected { 96 | font-weight: bold; 97 | } 98 | 99 | .list-item-handle { 100 | cursor: pointer; 101 | cursor: grab; 102 | } 103 | 104 | .list-item-thumbnail { 105 | width: 30px; 106 | height: 30px; 107 | display: block; 108 | border-radius: 50%; 109 | background-color: $list-item-thumbnail-bg; 110 | float: right; 111 | box-shadow: inset -1px 1px 1px 0 rgba(0, 0, 0, 0.1); 112 | } 113 | 114 | &.list-item-image { 115 | @include pseudo-transparent-bckg(); 116 | margin: 3px; 117 | padding: 0; 118 | flex-basis: auto; 119 | } 120 | 121 | &.list-item-icon { 122 | float: left; 123 | padding: 5px; 124 | display: block; 125 | text-align: center; 126 | line-height: 1em; 127 | width: 90px; 128 | height: 90px; 129 | 130 | .icon { 131 | font-size: 18px; 132 | padding: 20px; 133 | } 134 | } 135 | 136 | .list-item-widget-icon { 137 | margin: 7px; 138 | } 139 | 140 | &.list-item-large { 141 | display: inline-block; 142 | width: 140px; 143 | text-align: center; 144 | padding: 15px 5px; 145 | line-height: 1.5em; 146 | margin-bottom: 15px; 147 | flex-basis: auto; 148 | vertical-align: top; 149 | 150 | a { 151 | display: inline-block; 152 | } 153 | 154 | .paperbits-icon { 155 | display: block; 156 | padding: 0; 157 | height: 1.5em; 158 | } 159 | } 160 | } 161 | 162 | .list-edge { 163 | display: block; 164 | flex-basis: 100%; 165 | min-height: 10px; 166 | min-width: 10px; 167 | } 168 | 169 | .list-checkboxes { 170 | .list-item { 171 | padding-left: 25px; 172 | 173 | &.list-item-checked::before { 174 | @extend .paperbits-icon; 175 | @extend .paperbits-check-2; 176 | position: absolute; 177 | left: 5px; 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/themes/designer/styles/scaffolding.scss: -------------------------------------------------------------------------------- 1 | @import "mixins.scss"; 2 | html, 3 | body { 4 | width: 100%; 5 | height: 100%; 6 | padding: 0; 7 | margin: 0; 8 | overflow: hidden; 9 | } 10 | 11 | * { 12 | @include box-sizing(border-box); 13 | position: relative; 14 | } 15 | 16 | *:before, 17 | *:after { 18 | @include box-sizing(border-box); 19 | } 20 | 21 | // Reset fonts for relevant elements 22 | input, 23 | button, 24 | select, 25 | textarea { 26 | font-family: inherit; 27 | font-size: inherit; 28 | } 29 | 30 | figure { 31 | margin: 0; 32 | } 33 | 34 | img { 35 | vertical-align: middle; 36 | } 37 | 38 | [role="button"] { 39 | cursor: pointer; 40 | } 41 | -------------------------------------------------------------------------------- /src/themes/designer/styles/scrollbars.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Container style 3 | */ 4 | .ps { 5 | overflow: hidden !important; 6 | overflow-anchor: none; 7 | touch-action: auto; 8 | } 9 | 10 | /* 11 | * Scrollbar rail styles 12 | */ 13 | .ps__rail-x { 14 | display: none; 15 | opacity: .2; 16 | transition: background-color .2s linear, opacity .2s linear; 17 | height: 15px; 18 | /* there must be 'bottom' or 'top' for ps__rail-x */ 19 | bottom: 0; 20 | /* please don't change 'position' */ 21 | position: absolute; 22 | border-radius: 20px; 23 | } 24 | 25 | .ps__rail-y { 26 | display: none; 27 | opacity: .2; 28 | transition: background-color .2s linear, opacity .2s linear; 29 | width: 15px; 30 | /* there must be 'right' or 'left' for ps__rail-y */ 31 | right: 0; 32 | /* please don't change 'position' */ 33 | position: absolute; 34 | border-radius: 20px; 35 | } 36 | 37 | .ps--active-x > .ps__rail-x, 38 | .ps--active-y > .ps__rail-y { 39 | display: block; 40 | background-color: transparent; 41 | } 42 | 43 | .ps--focus > .ps__rail-x, 44 | .ps--focus > .ps__rail-y, 45 | .ps--scrolling-x > .ps__rail-x, 46 | .ps--scrolling-y > .ps__rail-y, 47 | .ps:hover > .ps__rail-x, 48 | .ps:hover > .ps__rail-y { 49 | opacity: .6; 50 | } 51 | 52 | .ps__rail-x:focus, 53 | .ps__rail-x:hover, 54 | .ps__rail-y:focus, 55 | .ps__rail-y:hover { 56 | background-color: $scrollbar-rail-active-bg; 57 | opacity: .9; 58 | } 59 | 60 | /* 61 | * Scrollbar thumb styles 62 | */ 63 | .ps__thumb-x { 64 | background-color: $scrollbar-thumb-bg; 65 | border-radius: 6px; 66 | transition: background-color .2s linear, height .2s ease-in-out; 67 | height: 6px; 68 | /* there must be 'bottom' for ps__thumb-x */ 69 | bottom: 2px; 70 | /* please don't change 'position' */ 71 | position: absolute; 72 | } 73 | 74 | .ps__thumb-y { 75 | background-color: $scrollbar-thumb-bg; 76 | border-radius: 6px; 77 | transition: background-color .2s linear, width .2s ease-in-out; 78 | width: 6px; 79 | /* there must be 'right' for ps__thumb-y */ 80 | right: 2px; 81 | /* please don't change 'position' */ 82 | position: absolute; 83 | } 84 | 85 | .ps__rail-x:focus > .ps__thumb-x, 86 | .ps__rail-x:hover > .ps__thumb-x { 87 | background-color: $scrollbar-thumb-active-bg; 88 | height: 11px; 89 | } 90 | 91 | .ps__rail-y:focus > .ps__thumb-y, 92 | .ps__rail-y:hover > .ps__thumb-y { 93 | background-color: $scrollbar-thumb-active-bg; 94 | width: 11px; 95 | } 96 | 97 | /* MS supports */ 98 | @supports (-ms-overflow-style: none) { 99 | .ps { 100 | overflow: auto !important; 101 | } 102 | } 103 | @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { 104 | .ps { 105 | overflow: auto !important; 106 | } 107 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/slider.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | 3 | .slider-display { 4 | width: 100%; 5 | height: 25px; 6 | margin-top: 15px; 7 | } 8 | 9 | .slider { 10 | width: 100%; 11 | height: 15px; 12 | 13 | .slider-thumb { 14 | @include slider-thumb(); 15 | position: absolute; 16 | transform: translate(-$form-control-slider-thumb-size / 2, -$form-control-slider-thumb-size / 2); 17 | } 18 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/spinners.scss: -------------------------------------------------------------------------------- 1 | .shutter { 2 | opacity: 0; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | display: block; 8 | position: fixed; 9 | background: $spinner-shutter-bg; 10 | pointer-events: none; 11 | transition: opacity .5s ease-in-out; 12 | 13 | spinner { 14 | display: none; 15 | } 16 | 17 | &.active { 18 | opacity: 1; 19 | 20 | spinner { 21 | display: flex; 22 | } 23 | } 24 | } 25 | 26 | spinner { 27 | display: flex; 28 | text-align: center; 29 | align-items: center; 30 | flex-basis: 100%; 31 | justify-content: center; 32 | 33 | & > div { 34 | width: 10px; 35 | height: 10px; 36 | margin: 5px; 37 | border-radius: 100%; 38 | display: inline-block; 39 | animation: sk-bouncedelay 1.4s infinite ease-in-out both; 40 | border: 1px solid $spinner-dot-border-color; 41 | 42 | &:nth-child(1) { 43 | animation-delay: -0.32s; 44 | } 45 | 46 | &:nth-child(2) { 47 | animation-delay: -0.16s; 48 | } 49 | } 50 | 51 | &.inverted > div { 52 | border: 1px solid $spinner-inverted-dot-border-color; 53 | background: $spinner-inverted-bg; 54 | } 55 | } 56 | @keyframes sk-bouncedelay { 57 | 0%, 58 | 100%, 59 | 80% { 60 | transform: scale(0); 61 | } 62 | 63 | 40% { 64 | transform: scale(1); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/themes/designer/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "typography.scss"; 3 | @import "flex.scss"; 4 | @import "balloons.scss"; 5 | @import "buttons.scss"; 6 | @import "icons-toolboxes.scss"; 7 | @import "icons-widgets.scss"; 8 | @import "lists.scss"; 9 | @import "mixins.scss"; 10 | @import "scaffolding.scss"; 11 | @import "toolbox.paragraph.scss"; 12 | @import "toolboxes.scss"; 13 | @import "workshops.scss"; 14 | @import "dropbucket.scss"; 15 | @import "toasts.scss"; 16 | @import "layouts.scss"; 17 | @import "animation.scss"; 18 | @import "draggables.scss"; 19 | @import "forms.scss"; 20 | @import "tabs.scss"; 21 | @import "widgets.scss"; 22 | @import "arrows.scss"; 23 | @import "spinners.scss"; 24 | @import "host.scss"; 25 | @import "scrollbars.scss"; 26 | @import "thumbnails.scss"; 27 | @import "dropzones.scss"; 28 | @import "utils.scss"; 29 | @import "tooltips.scss"; 30 | @import "confirmation.scss"; 31 | @import "cropper.scss"; 32 | @import "lightbox.scss"; 33 | @import "blocks.scss"; 34 | 35 | 36 | 37 | @import "editors/boxEditor.scss"; 38 | @import "editors/fontSelector.scss"; 39 | @import "editors/colorPicker.scss"; 40 | 41 | 42 | @import "grid.scss"; -------------------------------------------------------------------------------- /src/themes/designer/styles/tabs.scss: -------------------------------------------------------------------------------- 1 | 2 | .tabs { 3 | border-bottom: 1px solid $form-control-border; 4 | margin-bottom: 12px; 5 | display: flex; 6 | width: 100%; 7 | 8 | .tab { 9 | & > a { 10 | min-width: 60px; 11 | display: inline-block; 12 | vertical-align: middle; 13 | text-align: center; 14 | line-height: 35px; 15 | color: $color-base; 16 | padding: 0 10px; 17 | text-decoration: none; 18 | font-weight: bold; 19 | 20 | &:active, 21 | &:focus, 22 | &:hover { 23 | outline: none; 24 | @include selection(); 25 | } 26 | } 27 | 28 | &.tab-is-active { 29 | border-bottom: 2px solid $tab-selected-color; 30 | } 31 | } 32 | } 33 | 34 | .tab-panel { 35 | min-height: 200px; 36 | max-height: 350px; 37 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/thumbnails.scss: -------------------------------------------------------------------------------- 1 | .thumbnail { 2 | width: 120px; 3 | height: 100px; 4 | background-size: contain; 5 | background-repeat: no-repeat; 6 | background-color: transparent; 7 | background-position: center; 8 | position: relative; 9 | margin: 5px; 10 | float: left; 11 | text-align: center; 12 | 13 | & div { 14 | width: 50px; 15 | height: 50px; 16 | top: 25%; 17 | left: 25%; 18 | } 19 | 20 | &:before { 21 | opacity: 0; 22 | transition: opacity .6s; 23 | top: 0; 24 | left: 0; 25 | right: 0; 26 | bottom: 0; 27 | content: attr(title); 28 | position: absolute; 29 | background-color: #000; 30 | color: #fff; 31 | line-height: normal; 32 | white-space: nowrap; 33 | overflow: hidden; 34 | text-overflow: ellipsis; 35 | padding: 5px; 36 | } 37 | 38 | &:hover { 39 | &:before { 40 | opacity: .7; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/themes/designer/styles/toasts.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "mixins.scss"; 3 | @import "animation.scss"; 4 | 5 | .toasts { 6 | position: fixed; 7 | right: 40px; 8 | bottom: 50px; 9 | z-index: $z-index-popup; 10 | 11 | .toast { 12 | @include surface(); 13 | @include animation-fadein(); 14 | display: flex; 15 | 16 | align-items: center; 17 | justify-content: center; 18 | width: 350px; 19 | 20 | .toast-indicator { 21 | width: 30px; 22 | 23 | .progress { 24 | @extend spinner; 25 | 26 | &:after { 27 | content: ""; 28 | width: 10px; 29 | height: 10px; 30 | border: 2px solid $color-base; 31 | border-radius: 50%; 32 | float: left; 33 | transform: scale(0); 34 | animation: hollow-dots-spinner-animation 1000ms ease infinite 0ms; 35 | } 36 | } 37 | 38 | .info { 39 | @extend .paperbits-icon; 40 | @extend .paperbits-alert-circle-i; 41 | } 42 | 43 | .success { 44 | @extend .paperbits-icon; 45 | @extend .paperbits-check-2; 46 | } 47 | 48 | .error { 49 | @extend .paperbits-icon; 50 | @extend .paperbits-alert; 51 | } 52 | } 53 | 54 | .toast-container { 55 | flex-basis: 100%; 56 | line-height: 1.5em; 57 | padding: 17px; 58 | } 59 | 60 | .toast-header { 61 | font-weight: bold; 62 | } 63 | 64 | .toast-content { 65 | p { 66 | margin: 0; 67 | margin-top: 5px; 68 | } 69 | } 70 | 71 | & > div { 72 | padding: 10px; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/themes/designer/styles/toolbox.paragraph.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "buttons.scss"; 3 | 4 | 5 | .tools-paragraph-style { 6 | @include tabable(); 7 | display: block; 8 | padding: 5px 10px; 9 | text-decoration: none; 10 | color: $color-base; 11 | } 12 | 13 | .tools-paragraph-style { 14 | &:last-child { 15 | border: none; 16 | } 17 | } 18 | 19 | .tools-paragraph-style-normal { 20 | @extend .tools-paragraph-style; 21 | } 22 | 23 | .tools-paragraph-style-h1 { 24 | @extend .tools-paragraph-style; 25 | font-size: 2.6em; 26 | } 27 | 28 | .tools-paragraph-style-h2 { 29 | @extend .tools-paragraph-style; 30 | font-size: 2.3em; 31 | } 32 | 33 | .tools-paragraph-style-h3 { 34 | @extend .tools-paragraph-style; 35 | font-size: 1.7em; 36 | } 37 | 38 | .tools-paragraph-style-h4 { 39 | @extend .tools-paragraph-style; 40 | font-size: 1.4em; 41 | } 42 | 43 | .tools-paragraph-style-h5 { 44 | @extend .tools-paragraph-style; 45 | font-size: 1em; 46 | } 47 | 48 | .tools-paragraph-style-h6 { 49 | @extend .tools-paragraph-style; 50 | font-size: .85em; 51 | } 52 | -------------------------------------------------------------------------------- /src/themes/designer/styles/tooltips.scss: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | padding: 10px; 3 | max-width: 300px; 4 | display: block; 5 | line-height: 1.5em; 6 | p { 7 | margin: 0; 8 | } 9 | 10 | h1 { 11 | margin-bottom: 5px; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/themes/designer/styles/typography.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'OpenSans'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: url('fonts/OpenSans-Light.ttf') format('truetype'); 6 | } 7 | @font-face { 8 | font-family: 'OpenSans'; 9 | font-style: italic; 10 | font-weight: 400; 11 | src: url('fonts/OpenSans-LightItalic.ttf') format('truetype'); 12 | } 13 | @font-face { 14 | font-family: 'OpenSans'; 15 | font-style: normal; 16 | font-weight: 600; 17 | src: url('fonts/OpenSans-Regular.ttf') format('truetype'); 18 | } 19 | @font-face { 20 | font-family: 'OpenSans'; 21 | font-style: italic; 22 | font-weight: 600; 23 | src: url('fonts/OpenSans-Italic.ttf') format('truetype'); 24 | } 25 | @font-face { 26 | font-family: 'OpenSans'; 27 | font-style: normal; 28 | font-weight: 900; 29 | src: url('fonts/OpenSans-Bold.ttf') format('truetype'); 30 | } 31 | @font-face { 32 | font-family: 'OpenSans'; 33 | font-style: italic; 34 | font-weight: 900; 35 | src: url('fonts/OpenSans-BoldItalic.ttf') format('truetype'); 36 | } 37 | @font-face { 38 | font-family: 'paperbits'; 39 | src: url('fonts/paperbits.eot'); 40 | src: url('fonts/paperbits.eot') format('embedded-opentype'), url('fonts/paperbits.woff2') format('woff2'), url('fonts/paperbits.woff') format('woff'), url('fonts/paperbits.ttf') format('truetype'), url('fonts/paperbits.svg') format('svg'); 41 | font-weight: normal; 42 | font-style: normal; 43 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/utils.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | 3 | .text-center { 4 | text-align: center; 5 | } 6 | 7 | .text-hide { 8 | overflow: hidden; 9 | position: fixed; 10 | top: -1000px; 11 | left: -1000px; 12 | width: 0; 13 | height: 0; 14 | } 15 | 16 | .text-overflow { 17 | white-space: nowrap; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | } 21 | 22 | .no-overflow { 23 | overflow: hidden; 24 | } 25 | 26 | .no-pointer-events { 27 | pointer-events: none; 28 | } 29 | 30 | .d-none { 31 | display: none !important; 32 | } 33 | 34 | .d-xl-inline-block { 35 | @media (min-width: $breakpoint-xl) { 36 | display: inline-block !important; 37 | } 38 | } 39 | 40 | .d-lg-inline-block { 41 | @media (min-width: $breakpoint-lg) { 42 | display: inline-block !important; 43 | } 44 | } 45 | .d-md-inline-block { 46 | @media (min-width: $breakpoint-md) { 47 | display: inline-block !important; 48 | } 49 | } 50 | 51 | .d-sm-inline-block { 52 | @media (min-width: $breakpoint-sm) { 53 | display: inline-block !important; 54 | } 55 | } 56 | 57 | .mt-0 { 58 | margin-top: 0; 59 | } 60 | 61 | .ml-5 { 62 | margin-left: 5px; 63 | } 64 | 65 | .max-w300 { 66 | max-width: 300px; 67 | } 68 | -------------------------------------------------------------------------------- /src/themes/designer/styles/variables.scss: -------------------------------------------------------------------------------- 1 | /* Breakpoints */ 2 | $breakpoint-xs: 0; 3 | $breakpoint-sm: 576px; 4 | $breakpoint-md: 768px; 5 | $breakpoint-lg: 992px; 6 | $breakpoint-xl: 1200px; 7 | 8 | $grid-breakpoints: ( 9 | xs: 0, 10 | sm: 576px, 11 | md: 768px, 12 | lg: 992px, 13 | xl: 1200px 14 | ) !default; 15 | 16 | /* Typography */ 17 | $font-family-base: 'OpenSans', sans-serif; 18 | $font-size-base: 13px; 19 | $font-weight-base: 500; 20 | $font-weight-headers: 600; 21 | $lineheight: 2em; 22 | 23 | /* Toolboxes */ 24 | $toolbox-background: #fff; 25 | $toolbox-btn-size: 45px; 26 | $toolbox-btn-padding: 5px; 27 | $toolbox-btn-active-bg: #F9FBFC; 28 | $toolbox-btn-active-border: #96989A; 29 | $toolbox-border-radius: 10px; 30 | $toolbox-padding: 8px; 31 | 32 | /* Workshops */ 33 | $workshop-button-height: 2em; 34 | $workshop-help-button-size: 24px; 35 | $workshop-max-height: 300px; 36 | $workshop-subtitle-color: #AFBD77; 37 | $workshop-padding: 20px; 38 | $workshop-journey-step-border-color: #eceaea; 39 | 40 | /* Buttons */ 41 | $button-danger-text-color: #fff; 42 | $button-link-color: #99c248; 43 | 44 | /* Balloons */ 45 | $balloon-background: $toolbox-background; 46 | $balloon-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.15); 47 | 48 | /* Palette */ 49 | $tab-selected-color: #A8CF45; 50 | $collapse-button-color: #91D8F7; 51 | $color-base: #5b5f61; 52 | $color-danger: #e46969; 53 | $color-info: #91D8F7; 54 | $color-watermark: #CCC; 55 | $link-color: #00AFEF; 56 | $arrow-active-color: #F58634; 57 | $icon-base-color: #57585a; 58 | 59 | /* Forms */ 60 | $form-control-font-size: 1em; 61 | $form-control-border: #e6e7e8; 62 | $form-control-border-focused: #61b2e8; 63 | $form-control-bg: #ffffff; 64 | $form-control-bg-readonly: #fff; 65 | $form-control-bg-disabled: #e6e7e8; 66 | $form-control-padding: 7px; 67 | $form-control-focus-color: #61b2e8; 68 | $form-control-slider-thumb-bg: #fff; 69 | $form-control-slider-thumb-size: 17px; 70 | $form-collapsible-section-gutter-color: #ddd; 71 | 72 | /* Lists */ 73 | $list-item-thumbnail-bg: #f8f8f8; 74 | 75 | /* Spinners */ 76 | $spinner-shutter-bg: #fff; 77 | $spinner-dot-border-color: #003255; 78 | $spinner-inverted-bg: #fff; 79 | $spinner-inverted-dot-border-color: #fff; 80 | 81 | /* Scrollbars */ 82 | $scrollbar-rail-active-bg: #eee; 83 | $scrollbar-thumb-bg: #aaa; 84 | $scrollbar-thumb-active-bg: #999; 85 | 86 | /* Layout editor */ 87 | $layout-editor-selection-color: #3c3c3c; 88 | $layout-editor-splitter-color: #ccc; 89 | 90 | /* Others */ 91 | $z-index-base: 9100; 92 | $z-index-popup: 9110; 93 | $z-index-balloon: 9120; 94 | $z-index-dragging: 9130; 95 | $z-index-arrows: 9140; 96 | $z-index-selection: 9000; 97 | $list-item-size: 2.5em; 98 | $dropdown-option-selected-color: #98e2ff; -------------------------------------------------------------------------------- /src/themes/designer/styles/widgets.scss: -------------------------------------------------------------------------------- 1 | @import "widgets/map.scss"; 2 | @import "widgets/video.scss"; 3 | @import "editors/videoEditor.scss"; 4 | @import "editors/rowLayoutSelector.scss"; 5 | @import "editors/colorSelector.scss"; 6 | @import "editors/fontEditor.scss"; -------------------------------------------------------------------------------- /src/themes/designer/styles/widgets/map.scss: -------------------------------------------------------------------------------- 1 | .paperbits-googlemaps { 2 | width: 100%; 3 | height: 100%; 4 | height: 200px; 5 | border: 0; 6 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/widgets/video.scss: -------------------------------------------------------------------------------- 1 | video { 2 | width: 100%; 3 | display: block; 4 | } -------------------------------------------------------------------------------- /src/themes/designer/styles/workshops.scss: -------------------------------------------------------------------------------- 1 | @import "variables.scss"; 2 | @import "icons-toolboxes.scss"; 3 | @import "mixins.scss"; 4 | @import "animation.scss"; 5 | @import "flex.scss"; 6 | 7 | 8 | .workshop { 9 | .workshop-journey-step { 10 | min-width: 300px; 11 | padding: 0 20px; 12 | 13 | &:first-child { 14 | padding-left: 0; 15 | } 16 | 17 | &:last-child { 18 | padding-right: 0; 19 | } 20 | 21 | &:not(:last-child) { 22 | position: relative; 23 | 24 | &:after { 25 | content: ""; 26 | position: absolute; 27 | top: 40px; 28 | bottom: 40px; 29 | right: 0; 30 | width: 1px; 31 | border: 0; 32 | background: $workshop-journey-step-border-color; 33 | background: radial-gradient($workshop-journey-step-border-color, white); 34 | } 35 | } 36 | 37 | &:first-child { 38 | @extend .flex-item-grow; 39 | } 40 | 41 | h1 { 42 | padding-left: 5px; 43 | line-height: 25px; 44 | } 45 | 46 | .btn-dismiss { 47 | top: 0; 48 | right: 0; 49 | } 50 | } 51 | } 52 | 53 | .toolbox.workshops-container { 54 | z-index: $z-index-base; 55 | min-height: 370px; 56 | 57 | .journey-container { 58 | padding: 10px 20px; 59 | } 60 | } 61 | 62 | .workshop { 63 | padding: 10px; 64 | @media (min-width: $breakpoint-lg) { 65 | width: 80%; 66 | } 67 | } 68 | 69 | workshops { 70 | height: 100%; 71 | display: flex; 72 | flex-direction: column; 73 | justify-items: center; 74 | align-items: flex-start; 75 | justify-content: center; 76 | } 77 | 78 | .workshop:first-child { 79 | margin-left: 0; 80 | } 81 | 82 | .workshop:last-child { 83 | margin-right: 0; 84 | } 85 | -------------------------------------------------------------------------------- /src/themes/designer/svgs/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/themes/designer/svgs/drag-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Drag 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/themes/website/assets/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/themes/website/styles/animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes bounce { 2 | 20%, 3 | 53%, 4 | 80%, 5 | from, 6 | to { 7 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); 8 | transform: translate3d(0, 0, 0); 9 | } 10 | 11 | 40%, 12 | 43% { 13 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); 14 | transform: translate3d(0, -30px, 0); 15 | } 16 | 17 | 70% { 18 | animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); 19 | transform: translate3d(0, -15px, 0); 20 | } 21 | 22 | 90% { 23 | transform: translate3d(0, -4px, 0); 24 | } 25 | } 26 | 27 | .bounce { 28 | animation-name: bounce; 29 | transform-origin: center bottom; 30 | } 31 | 32 | @keyframes fadeInLeft { 33 | from { 34 | opacity: 0; 35 | transform: translate3d(-100%, 0, 0); 36 | } 37 | 38 | to { 39 | opacity: 1; 40 | transform: translate3d(0, 0, 0); 41 | } 42 | } 43 | 44 | .fadeInLeft { 45 | animation-name: fadeInLeft; 46 | } 47 | 48 | @keyframes fadeInRight { 49 | from { 50 | opacity: 0; 51 | transform: translate3d(100%, 0, 0); 52 | } 53 | 54 | to { 55 | opacity: 1; 56 | transform: translate3d(0, 0, 0); 57 | } 58 | } 59 | 60 | .fadeInRight { 61 | animation-name: fadeInRight; 62 | } -------------------------------------------------------------------------------- /src/themes/website/styles/emails/emails.scss: -------------------------------------------------------------------------------- 1 | @import "../variables.scss"; 2 | @import "layouts.scss"; 3 | 4 | .email-layout { 5 | max-width: 600px; 6 | } 7 | 8 | .email-layout-section, 9 | .email-layout-row { 10 | width: 100%; 11 | table-layout: fixed; 12 | } 13 | 14 | .email-layout-column { 15 | overflow: hidden; 16 | } 17 | .email-layout-column-12 { 18 | width: 100%; 19 | } 20 | 21 | .email-layout-column-9 { 22 | width: 75%; 23 | } 24 | 25 | .email-layout-column-8 { 26 | width: 66.666667%; 27 | } 28 | 29 | .email-layout-column-6 { 30 | width: 50%; 31 | } 32 | 33 | .email-layout-column-4 { 34 | width: 33.333333%s; 35 | } 36 | 37 | .email-layout-column-3 { 38 | width: 25%; 39 | } 40 | 41 | $grid-padding: 10px; 42 | 43 | table { 44 | vertical-align: top; 45 | line-height: inherit; 46 | border: none; 47 | 48 | table { 49 | tr { 50 | td { 51 | padding: $grid-padding; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/themes/website/styles/emails/layouts.scss: -------------------------------------------------------------------------------- 1 | @mixin fit($spacing: 0) { 2 | top: $spacing; 3 | left: $spacing; 4 | right: $spacing; 5 | bottom: $spacing; 6 | position: absolute; 7 | } 8 | 9 | .placeholder { 10 | position: relative; 11 | min-height: 80px; 12 | font-size: .9vw; 13 | font-family: monospace; 14 | text-transform: uppercase; 15 | height: 100%; 16 | pointer-events: none; 17 | } 18 | 19 | .empty-item { 20 | @include fit(); 21 | background: repeating-linear-gradient(45deg, rgba(0, 0, 0, 0.0), rgba(0, 0, 0, 0.0) 10px, rgba(0, 0, 0, 0.05) 10px, rgba(0, 0, 0, 0.05) 20px); 22 | 23 | &:after { 24 | content: attr(placeholder); 25 | position: absolute; 26 | top: 50%; 27 | transform: translateY(-50%); 28 | width: 100%; 29 | text-align: center; 30 | } 31 | } 32 | @function breakpoint-min($name, $breakpoints: $grid-breakpoints) { 33 | $min: map-get($breakpoints, $name); 34 | @return if($min != 0, $min, null); 35 | } 36 | @function breakpoint-infix($name, $breakpoints: $grid-breakpoints) { 37 | @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); 38 | } 39 | @each $breakpoint in map-keys($grid-breakpoints) { 40 | $infix: breakpoint-infix($breakpoint, $grid-breakpoints); 41 | 42 | .align-horizontally#{$infix}-left { 43 | text-align: left; 44 | } 45 | 46 | .align-horizontally#{$infix}-center { 47 | text-align: center; 48 | } 49 | 50 | .align-horizontally#{$infix}-right { 51 | text-align: right; 52 | } 53 | 54 | .align-vertically#{$infix}-top { 55 | vertical-align: top; 56 | } 57 | 58 | .align-vertically#{$infix}-center { 59 | vertical-align: middle; 60 | } 61 | 62 | .align-vertically#{$infix}-bottom { 63 | vertical-align: bottom; 64 | } 65 | } -------------------------------------------------------------------------------- /src/themes/website/styles/flex.scss: -------------------------------------------------------------------------------- 1 | .flex { 2 | display: flex; 3 | min-height: 0; // fixes issue with oveflow in Edge, FF and other browsers 4 | } 5 | 6 | .flex-row { 7 | flex-direction: row; 8 | } 9 | 10 | .flex-column { 11 | flex-direction: column; 12 | } 13 | 14 | .flex-grow { 15 | flex: 1 1 auto; 16 | } 17 | 18 | .flex-wrap { 19 | flex-wrap: wrap; 20 | } 21 | -------------------------------------------------------------------------------- /src/themes/website/styles/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/website/styles/fonts/icons.eot -------------------------------------------------------------------------------- /src/themes/website/styles/fonts/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/themes/website/styles/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/website/styles/fonts/icons.ttf -------------------------------------------------------------------------------- /src/themes/website/styles/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/website/styles/fonts/icons.woff -------------------------------------------------------------------------------- /src/themes/website/styles/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paperbits/paperbits-demo/4ad97b8dda9f6268900c61d55cd0a48bfd8e8aaf/src/themes/website/styles/fonts/icons.woff2 -------------------------------------------------------------------------------- /src/themes/website/styles/forms.scss: -------------------------------------------------------------------------------- 1 | form { 2 | padding: 10px; 3 | flex-grow: 1; 4 | } 5 | 6 | .form-control { 7 | width: 100%; 8 | } 9 | 10 | .form-inline { 11 | display: flex; 12 | flex-flow: row wrap; 13 | align-items: center; 14 | } 15 | 16 | .form-inline .form-check { 17 | width: 100%; 18 | } 19 | 20 | .invalid-feedback { 21 | margin-top: 0.25rem; 22 | font-size: 0.875em; 23 | color: $form-invalid-color; 24 | display: none; 25 | } 26 | 27 | :invalid~.invalid-feedback, 28 | .invalid~.invalid-feedback { 29 | display: block; 30 | } 31 | 32 | 33 | form.needs-validation:invalid { 34 | [type="submit"] { 35 | pointer-events: none; 36 | } 37 | } 38 | 39 | input[type="range"] { 40 | width: 100%; 41 | } 42 | 43 | @media (min-width: 576px) { 44 | .form-inline label { 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | margin-bottom: 0; 49 | } 50 | .form-inline .form-group { 51 | display: flex; 52 | flex: 0 0 auto; 53 | flex-flow: row wrap; 54 | align-items: center; 55 | margin-bottom: 0; 56 | } 57 | .form-inline .form-control { 58 | display: inline-block; 59 | width: auto; 60 | vertical-align: middle; 61 | } 62 | .form-inline .form-control-plaintext { 63 | display: inline-block; 64 | } 65 | .form-inline .input-group, 66 | .form-inline .custom-select { 67 | width: auto; 68 | } 69 | .form-inline .form-check { 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | width: auto; 74 | padding-left: 0; 75 | } 76 | .form-inline .form-check-input { 77 | position: relative; 78 | flex-shrink: 0; 79 | margin-top: 0; 80 | margin-right: 0.25rem; 81 | margin-left: 0; 82 | } 83 | } 84 | 85 | .input-group { 86 | padding: 0; 87 | display: flex; 88 | flex-wrap: wrap; 89 | 90 | & > :not(:first-child):not(.dropdown):not(.invalid-feedback) { 91 | border-top-left-radius: 0; 92 | border-bottom-left-radius: 0; 93 | margin-left: -1px; 94 | } 95 | 96 | &.has-validation > :nth-last-child(n + 3) { 97 | border-top-right-radius: 0; 98 | border-bottom-right-radius: 0; 99 | margin-left: -1px; 100 | } 101 | 102 | &:not(.has-validation) > :not(:last-child):not(.dropdown) { 103 | border-top-right-radius: 0; 104 | border-bottom-right-radius: 0; 105 | } 106 | 107 | .form-control { 108 | flex-grow: 1; 109 | } 110 | 111 | .input-group-addon { 112 | line-height: normal; 113 | background-color: #fff; 114 | color: #000; 115 | border: 1px solid #000; 116 | } 117 | 118 | .dropdown { 119 | background: #fff; 120 | box-shadow: 1px 1px 3px 0 rgba(0, 0, 0, 0.5); 121 | padding: 20px; 122 | width: 100%; 123 | top: 100%; 124 | left: 0; 125 | right: 0; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/themes/website/styles/icons.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Icons Embedded"; 3 | src: url("/styles/fonts/icons.eot"); 4 | src: url("/styles/fonts/icons.eot") format("embedded-opentype"), url("/styles/fonts/icons.woff2") format("woff2"), 5 | url("/styles/fonts/icons.woff") format("woff"), url("/styles/fonts/icons.ttf") format("truetype"), 6 | url("/styles/fonts/icons.svg") format("svg"); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | .icon-emb { 12 | display: inline-block; 13 | font-family: "Icons Embedded"; 14 | font-style: normal; 15 | font-weight: normal; 16 | font-size: 1em; 17 | vertical-align: middle; 18 | speak: none; 19 | text-transform: none; 20 | /* Better Font Rendering */ 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale; 23 | } 24 | 25 | .icon-emb.icon-emb-menu-8::before { 26 | content: "\ea05"; 27 | } 28 | 29 | .icon-emb.icon-emb-simple-remove::before { 30 | content: "\ea06"; 31 | } -------------------------------------------------------------------------------- /src/themes/website/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin fit($spacing: 0) { 2 | top: $spacing; 3 | left: $spacing; 4 | right: $spacing; 5 | bottom: $spacing; 6 | position: absolute; 7 | } -------------------------------------------------------------------------------- /src/themes/website/styles/navs.scss: -------------------------------------------------------------------------------- 1 | .nav { 2 | display: flex; 3 | flex-wrap: wrap; 4 | padding-left: 0; 5 | margin-bottom: 0; 6 | list-style: none; 7 | } 8 | 9 | .nav-link { 10 | display: block; 11 | padding: $default-navlink-padding; 12 | } 13 | 14 | .nav-link:hover, 15 | .nav-link:focus { 16 | text-decoration: none; 17 | } 18 | 19 | .nav-link.disabled { 20 | color: #6c757d; 21 | pointer-events: none; 22 | cursor: default; 23 | } 24 | 25 | .dropdown { 26 | display: none; 27 | position: absolute; 28 | z-index: 9000; 29 | 30 | &.show { 31 | display: block; 32 | } 33 | } 34 | 35 | .nav { 36 | display: flex; 37 | flex-wrap: wrap; 38 | 39 | .nav-item { 40 | padding: 0; 41 | margin: 0; 42 | } 43 | 44 | .nav-link { 45 | color: inherit; 46 | cursor: pointer; 47 | } 48 | 49 | .nav { 50 | margin-left: 20px; 51 | } 52 | 53 | .dropdown { 54 | .nav { 55 | margin: 0px; 56 | } 57 | 58 | .nav-item { 59 | white-space: nowrap; 60 | } 61 | } 62 | } 63 | 64 | .menu.menu-vertical { 65 | .nav { 66 | flex-direction: column; 67 | 68 | .nav { 69 | flex-direction: column; 70 | display: flex; 71 | } 72 | } 73 | 74 | .nav-item { 75 | flex: 100%; 76 | } 77 | } 78 | 79 | .menu.menu-horizontal { 80 | .nav-item.collapsible { 81 | > .nav-link::after { 82 | display: inline-block; 83 | margin-left: 0.255em; 84 | vertical-align: 0.255em; 85 | content: ""; 86 | border-top: 0.3em solid; 87 | border-right: 0.3em solid transparent; 88 | border-bottom: 0; 89 | border-left: 0.3em solid transparent; 90 | } 91 | 92 | &.show { 93 | > .nav-link::after { 94 | transform: rotate(180deg); 95 | } 96 | } 97 | 98 | .dropdown { 99 | z-index: 1; 100 | } 101 | } 102 | 103 | @media (max-width: $breakpoint-md) { 104 | .nav-item { 105 | flex-basis: 100%; 106 | } 107 | } 108 | } 109 | 110 | .menu.menu-full { 111 | flex-wrap: wrap; 112 | 113 | > .nav-item { 114 | display: flex; 115 | flex-direction: column; 116 | 117 | > .nav { 118 | display: flex; 119 | flex-direction: column; 120 | flex-wrap: wrap; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/themes/website/styles/styles.scss: -------------------------------------------------------------------------------- 1 | @import "reboot.scss"; 2 | @import "mixins.scss"; 3 | @import "layouts.scss"; 4 | @import "variables.scss"; 5 | @import "text.scss"; 6 | @import "icons.scss"; 7 | @import "animations.scss"; 8 | @import "navs.scss"; 9 | @import "utils.scss"; 10 | @import "forms.scss"; 11 | @import "widgets/widgets.scss"; 12 | @import "emails/emails.scss"; 13 | @import "utils.scss"; 14 | @import "flex.scss"; -------------------------------------------------------------------------------- /src/themes/website/styles/text.scss: -------------------------------------------------------------------------------- 1 | h1, 2 | h2, 3 | h3, 4 | h4, 5 | h5, 6 | h6 { 7 | font-weight: $default-heading-font-weight; 8 | line-height: $default-heading-line-height; 9 | color: $default-heading-color; 10 | } 11 | 12 | pre { 13 | white-space: pre-wrap; 14 | } 15 | -------------------------------------------------------------------------------- /src/themes/website/styles/utils.scss: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none !important; 3 | } 4 | 5 | .empty-media { 6 | width: 300px; 7 | height: 200px; 8 | display: flex; 9 | align-items: center; 10 | justify-content: center; 11 | background-color: #cccccc30; 12 | padding: 10px; 13 | } 14 | 15 | .preview { 16 | text-transform: uppercase; 17 | font-size: 10px; 18 | position: absolute; 19 | margin: 0 5px; 20 | } 21 | -------------------------------------------------------------------------------- /src/themes/website/styles/variables.scss: -------------------------------------------------------------------------------- 1 | /* Breakpoints */ 2 | $breakpoint-xs: 0; 3 | $breakpoint-sm: 576px; 4 | $breakpoint-md: 768px; 5 | $breakpoint-lg: 992px; 6 | $breakpoint-xl: 1200px; 7 | 8 | $grid-breakpoints: ( 9 | xs: 0, 10 | sm: 576px, 11 | md: 768px, 12 | lg: 992px, 13 | xl: 1200px 14 | ) !default; 15 | 16 | $font-size-base: calc(10px + 0.8vw); 17 | $font-size-h6: $font-size-base; 18 | $font-size-h5: calc(8px + 1vw); 19 | $font-size-h4: calc(10px + 1vw); 20 | $font-size-h3: calc(12px + 1vw); 21 | $font-size-h2: calc(14px + 1vw); 22 | $font-size-h1: calc(32px + 1vw); 23 | 24 | $link-color: #5cc6d0; 25 | $link-hover-color: #a2b747; 26 | $link-active-color: #a2b747; 27 | 28 | $font-family-base: Arial, Helvetica, sans-serif; 29 | $font-family-script: Tahoma, Geneva, Verdana, sans-serif; 30 | 31 | /* Defaults */ 32 | $default-heading-font-weight: 500; 33 | $default-heading-line-height: 1.2; 34 | $default-heading-color: inherit; 35 | $default-navlink-padding: 0.5rem 1rem; 36 | 37 | /* Forms */ 38 | $form-invalid-color: #f58634; 39 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/button.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | display: inline-block; 3 | 4 | .icon { 5 | padding-right: .5em; 6 | vertical-align: middle; 7 | } 8 | } -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/card.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | flex-direction: row; 3 | flex-wrap: wrap; 4 | } -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/carousel.scss: -------------------------------------------------------------------------------- 1 | $carousel-control-width: 20px; 2 | $carousel-control-color: #ccc; 3 | $carousel-control-opacity: 0.5; 4 | $carousel-control-icon-size: 50px; 5 | $carousel-indicator-size: 10px; 6 | $carousel-indicator-spacer: 20px; 7 | $carousel-indicator-active-bg: #fff; 8 | 9 | .carousel { 10 | display: flex; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | .carousel.pointer-event { 16 | touch-action: pan-y; 17 | } 18 | 19 | .carousel-items { 20 | display: flex; 21 | position: relative; 22 | transform: translateX(calc(-100% * var(--slide))); 23 | width: 100%; 24 | 25 | .carousel-item { 26 | display: flex; 27 | min-width: 100%; 28 | } 29 | } 30 | 31 | .carousel-item.active, 32 | .carousel-item-next, 33 | .carousel-item-prev { 34 | display: flex; 35 | flex: 1; 36 | } 37 | 38 | .carousel-item-next:not(.carousel-item-left), 39 | .active.carousel-item-right { 40 | transform: translateX(100%); 41 | } 42 | 43 | .carousel-item-prev:not(.carousel-item-right), 44 | .active.carousel-item-left { 45 | transform: translateX(-100%); 46 | } 47 | 48 | .carousel-control-prev, 49 | .carousel-control-next { 50 | position: absolute; 51 | top: 0; 52 | bottom: 0; 53 | z-index: 1; 54 | display: flex; 55 | align-items: center; 56 | justify-content: center; 57 | color: $carousel-control-color; 58 | text-align: center; 59 | opacity: $carousel-control-opacity; 60 | background: none; 61 | border: none; 62 | } 63 | .carousel-control-prev { 64 | left: 20px; 65 | } 66 | .carousel-control-next { 67 | right: 20px; 68 | } 69 | 70 | .carousel-control-prev-icon, 71 | .carousel-control-next-icon { 72 | display: block; 73 | } 74 | .carousel-control-prev-icon { 75 | font-size: $carousel-control-icon-size; 76 | } 77 | .carousel-control-next-icon { 78 | font-size: $carousel-control-icon-size; 79 | } 80 | 81 | .carousel-indicators { 82 | position: absolute; 83 | right: 0; 84 | bottom: 0; 85 | left: 0; 86 | z-index: 15; 87 | display: flex; 88 | justify-content: center; 89 | padding-left: 0; 90 | margin-right: $carousel-control-width; 91 | margin-left: $carousel-control-width; 92 | list-style: none; 93 | pointer-events: none; 94 | 95 | .carousel-indicator { 96 | box-sizing: content-box; 97 | flex: 0 1 auto; 98 | width: $carousel-indicator-size; 99 | height: $carousel-indicator-size; 100 | margin-right: $carousel-indicator-spacer; 101 | margin-left: $carousel-indicator-spacer; 102 | text-indent: -999px; 103 | cursor: pointer; 104 | background-color: none; 105 | background-clip: padding-box; 106 | opacity: 0.5; 107 | border: 2px solid #cccccc85; 108 | border-radius: $carousel-indicator-size; 109 | 110 | &.active { 111 | opacity: 1; 112 | background-color: #cccccc85; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/collapsibles.scss: -------------------------------------------------------------------------------- 1 | @import "../animations.scss"; 2 | 3 | .collapsible-container { 4 | flex-wrap: wrap; 5 | } 6 | 7 | .collapsible-content { 8 | display: none; 9 | flex-basis: 100%; 10 | flex-wrap: wrap; 11 | align-items: center; 12 | } 13 | 14 | .collapsible-panel { 15 | flex: 1; 16 | // min-width: 1px; 17 | 18 | .collapsible-panel-open { 19 | display: block; 20 | border: none; 21 | background: none; 22 | padding: 10px; 23 | border-radius: 10px; 24 | margin: 0 10px; 25 | cursor: pointer; 26 | margin-left: auto; 27 | } 28 | 29 | .collapsible-panel-close { 30 | display: none; 31 | border: none; 32 | background: none; 33 | padding: 10px; 34 | margin: 0 10px; 35 | cursor: pointer; 36 | position: absolute; 37 | right: 0; 38 | } 39 | } 40 | 41 | .collapsible-backdrop { 42 | opacity: 0.5; 43 | position: fixed; 44 | top: 0; 45 | left: 0; 46 | right: 0; 47 | bottom: 0; 48 | background: #000000c9; 49 | z-index: 8500; 50 | display: none; 51 | animation: fadeIn 0.2s linear forwards; 52 | } 53 | 54 | @media (min-width: $breakpoint-md) { 55 | .collapsible-panel { 56 | & > .collapsible-content { 57 | display: flex; 58 | } 59 | 60 | .collapsible-panel-open { 61 | display: none; 62 | } 63 | } 64 | } 65 | 66 | .collapsible-panel.show { 67 | & > .collapsible-content { 68 | display: flex; 69 | position: absolute; 70 | } 71 | } 72 | 73 | .collapsible-panel.show { 74 | & > .collapsible-content { 75 | top: 0; 76 | right: 0; 77 | bottom: 0; 78 | z-index: 9000; 79 | position: fixed; 80 | animation: slideInRight 0.2s linear forwards; 81 | 82 | & > .collapsible-panel-close { 83 | display: block; 84 | top: 0; 85 | right: 0; 86 | } 87 | } 88 | 89 | .collapsible-backdrop { 90 | display: block; 91 | } 92 | } 93 | 94 | @media (max-width: $breakpoint-md) { 95 | .collapsible-panel { 96 | .collapsible-panel-open { 97 | display: block; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/map.scss: -------------------------------------------------------------------------------- 1 | map, 2 | map-runtime { 3 | display: block; 4 | } 5 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/picture.scss: -------------------------------------------------------------------------------- 1 | .picture { 2 | display: inline-block; 3 | flex-basis: auto; 4 | max-width: 100%; 5 | } -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/popups.scss: -------------------------------------------------------------------------------- 1 | .popup { 2 | display: none; 3 | 4 | &.show { 5 | display: block; 6 | } 7 | 8 | .popup-backdrop { 9 | position: fixed; 10 | top: 0; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | z-index: 1000; 15 | } 16 | 17 | .popup-container { 18 | z-index: 1001; 19 | align-items: center; 20 | } 21 | 22 | .popup-close { 23 | position: absolute; 24 | top: 5px; 25 | right: 5px; 26 | border: none; 27 | background: none; 28 | z-index: 1002; 29 | } 30 | } 31 | 32 | .popup-host { 33 | display: block; 34 | overflow: visible; 35 | height: 0; 36 | width: 100%; 37 | } 38 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/tables.scss: -------------------------------------------------------------------------------- 1 | .table { 2 | overflow: auto; 3 | display: block; 4 | flex-basis: 100%; 5 | max-width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/tabs.scss: -------------------------------------------------------------------------------- 1 | .tab-panel { 2 | display: flex; 3 | flex-direction: column; 4 | position: relative; 5 | overflow: hidden; 6 | 7 | .tab-navs { 8 | display: flex; 9 | } 10 | 11 | .tab-content { 12 | display: none; 13 | flex-grow: 1; 14 | 15 | &.tab-content-active { 16 | display: flex; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/textblock.scss: -------------------------------------------------------------------------------- 1 | [contenteditable]:focus { 2 | outline: none; 3 | } 4 | 5 | p:empty { 6 | &:before { 7 | content: " "; 8 | } 9 | } 10 | 11 | .ProseMirror { 12 | display: block; 13 | flex-basis: 100%; 14 | max-width: 100%; 15 | position: relative; 16 | word-wrap: break-word; 17 | white-space: pre-wrap; 18 | white-space: break-spaces; 19 | -webkit-font-variant-ligatures: none; 20 | font-variant-ligatures: none; 21 | font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */ 22 | 23 | pre { 24 | white-space: pre-wrap; 25 | } 26 | 27 | li { 28 | position: relative; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/themes/website/styles/widgets/widgets.scss: -------------------------------------------------------------------------------- 1 | @import "button.scss"; 2 | @import "card.scss"; 3 | @import "map.scss"; 4 | @import "textblock.scss"; 5 | @import "collapsibles.scss"; 6 | @import "carousel.scss"; 7 | @import "tabs.scss"; 8 | @import "popups.scss"; 9 | @import "tables.scss"; 10 | @import "picture.scss"; -------------------------------------------------------------------------------- /src/user/staticRoleService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import { BuiltInRoles, RoleModel, RoleService } from "@paperbits/common/user"; 10 | 11 | /** 12 | * Static role service for demo purposes. 13 | */ 14 | export class StaticRoleService implements RoleService { 15 | public async getRoles(): Promise { 16 | return [BuiltInRoles.everyone, BuiltInRoles.anonymous, BuiltInRoles.authenticated]; 17 | } 18 | } -------------------------------------------------------------------------------- /src/user/staticUserService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | 10 | import { UserService, BuiltInRoles } from "@paperbits/common/user"; 11 | 12 | /** 13 | * Static user service for demo purposes. 14 | */ 15 | export class StaticUserService implements UserService { 16 | public async getUserName(): Promise { 17 | return "Jane Doe"; 18 | } 19 | 20 | public async getUserPhotoUrl(): Promise { 21 | return "https://cdn.paperbits.io/images/portrait.svg"; 22 | } 23 | 24 | public async getUserRoles(): Promise { 25 | return [BuiltInRoles.anonymous.key]; 26 | } 27 | 28 | public async setUserRoles(): Promise { 29 | // Not supported. 30 | } 31 | } -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright Paperbits. All Rights Reserved. 4 | * 5 | * Use of this source code is governed by an MIT-style license that can be 6 | * found in the LICENSE file and at https://paperbits.io/license/mit. 7 | */ 8 | 9 | import * as fs from "fs"; 10 | 11 | export async function loadFileAsString(filepath: string): Promise { 12 | return new Promise((resolve, reject) => { 13 | fs.readFile(filepath, "utf8", (error, content) => { 14 | if (error) { 15 | reject(error); 16 | return; 17 | } 18 | 19 | resolve(content); 20 | }); 21 | }); 22 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": [ 5 | "es2018", 6 | "dom" 7 | ], 8 | "module": "commonjs", 9 | "moduleResolution": "node", 10 | "declaration": false, 11 | "noImplicitAny": false, 12 | "removeComments": true, 13 | "noLib": false, 14 | "skipLibCheck": true, 15 | "experimentalDecorators": true, 16 | "emitDecoratorMetadata": true, 17 | "allowUnreachableCode": true, 18 | "listFiles": false, 19 | "sourceMap": false, 20 | "allowSyntheticDefaultImports": true, 21 | "incremental": true, 22 | "jsx": "react" 23 | }, 24 | "include": [ 25 | "./src/**/*", 26 | "./node_modules/@paperbits/**/*" 27 | ], 28 | "exclude": [ 29 | "./node_modules" 30 | ] 31 | } -------------------------------------------------------------------------------- /webpack.build.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const TerserPlugin = require("terser-webpack-plugin"); 3 | const { designerConfig, designerRuntimeConfig } = require("./webpack.designer.js"); 4 | const { publisherConfig, publisherRuntimeConfig } = require("./webpack.publisher.js"); 5 | 6 | 7 | const productionConfig = { 8 | mode: "production", 9 | optimization: { 10 | minimizer: [ 11 | new TerserPlugin({ 12 | terserOptions: { 13 | mangle: false, 14 | output: { 15 | comments: false 16 | } 17 | } 18 | }) 19 | ] 20 | } 21 | } 22 | 23 | module.exports = [ 24 | designerConfig, 25 | designerRuntimeConfig, 26 | publisherConfig, 27 | publisherRuntimeConfig 28 | ].map(x => merge(x, productionConfig)); -------------------------------------------------------------------------------- /webpack.designer.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const { merge } = require("webpack-merge"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const runtimeConfig = require("./webpack.runtime"); 7 | 8 | 9 | const designerConfig = { 10 | mode: "development", 11 | target: "web", 12 | entry: { 13 | "editors/scripts/paperbits": ["./src/startup.design.ts"], 14 | "editors/styles/paperbits": [`./src/themes/designer/styles/styles.scss`], 15 | }, 16 | output: { 17 | filename: "./[name].js", 18 | path: path.resolve(__dirname, "./dist/designer") 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.scss$/, 24 | use: [ 25 | MiniCssExtractPlugin.loader, 26 | { 27 | loader: "css-loader", 28 | options: { url: { filter: (url) => /\/icon-.*\.svg$/.test(url) } } 29 | }, 30 | { 31 | loader: "postcss-loader", 32 | options: { 33 | postcssOptions: { plugins: [["autoprefixer", { sourceMap: true, minimize: true }]] } 34 | } 35 | }, 36 | { loader: "sass-loader" } 37 | ] 38 | }, 39 | { 40 | test: /\.tsx?$/, 41 | loader: "ts-loader", 42 | options: { 43 | allowTsInNodeModules: true 44 | } 45 | }, 46 | { 47 | test: /\.html$/, 48 | loader: "html-loader", 49 | options: { 50 | esModule: true, 51 | sources: false, 52 | minimize: { 53 | removeComments: false, 54 | collapseWhitespace: false 55 | } 56 | } 57 | }, 58 | { 59 | test: /\.(svg)$/i, 60 | type: "asset/inline" 61 | }, 62 | { 63 | test: /\.(raw|liquid)$/, 64 | loader: "raw-loader" 65 | } 66 | ] 67 | }, 68 | plugins: [ 69 | new MiniCssExtractPlugin({ 70 | filename: "[name].css", 71 | chunkFilename: "[id].css" 72 | }), 73 | new CopyWebpackPlugin({ 74 | patterns: [ 75 | { from: `./src/data`, to: `./data` }, 76 | { from: `./src/config.design.json`, to: `./config.json` }, 77 | { from: `./src/themes/designer/assets/index.html`, to: "index.html" }, 78 | { from: `./src/themes/designer/styles/fonts`, to: "editors/styles/fonts" }, 79 | ] 80 | }), 81 | new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }) 82 | ], 83 | resolve: { 84 | extensions: [".ts", ".tsx", ".js", ".jsx", ".html", ".scss"], 85 | fallback: { 86 | buffer: require.resolve("buffer"), 87 | stream: require.resolve("stream-browserify"), 88 | crypto: false 89 | } 90 | } 91 | }; 92 | 93 | const designerRuntimeConfig = merge(runtimeConfig, { 94 | entry: { "styles/theme": `./src/themes/website/styles/styles.design.scss` }, 95 | output: { "path": path.resolve(__dirname, "dist/designer") } 96 | }); 97 | 98 | module.exports = { 99 | default: [designerConfig, designerRuntimeConfig], 100 | designerRuntimeConfig, 101 | designerConfig 102 | }; -------------------------------------------------------------------------------- /webpack.develop.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 3 | const { designerConfig, designerRuntimeConfig } = require("./webpack.designer.js"); 4 | 5 | const developmentConfig = { 6 | mode: "development", 7 | devtool: "inline-source-map", 8 | devServer: { 9 | hot: true, 10 | historyApiFallback: true 11 | }, 12 | plugins: [ 13 | new CopyWebpackPlugin({ 14 | patterns: [ 15 | { from: `./src/config.design.json`, to: `./config.json` }, 16 | ] 17 | }) 18 | ] 19 | } 20 | 21 | module.exports = [ 22 | merge(designerConfig, developmentConfig), 23 | designerRuntimeConfig 24 | ] -------------------------------------------------------------------------------- /webpack.publisher.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require('webpack'); 3 | const { merge } = require("webpack-merge"); 4 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 6 | const runtimeConfig = require("./webpack.runtime"); 7 | 8 | 9 | const publisherConfig = { 10 | mode: "development", 11 | target: "node", 12 | node: { 13 | __dirname: false, 14 | __filename: false 15 | }, 16 | entry: { 17 | "index": ["./src/startup.publish.ts"] 18 | }, 19 | output: { 20 | filename: "./[name].js", 21 | path: path.resolve(__dirname, "dist/publisher") 22 | }, 23 | externals: { 24 | "firebase-admin": "firebase-admin" 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.scss$/, 30 | use: [ 31 | MiniCssExtractPlugin.loader, 32 | { loader: "css-loader", options: { url: false } }, 33 | { loader: "postcss-loader" }, 34 | { loader: "sass-loader" } 35 | ] 36 | }, 37 | { 38 | test: /\.tsx?$/, 39 | loader: "ts-loader", 40 | options: { 41 | allowTsInNodeModules: true 42 | } 43 | }, 44 | { 45 | test: /\.html$/, 46 | loader: "html-loader", 47 | options: { 48 | esModule: true, 49 | sources: false, 50 | minimize: { 51 | removeComments: false, 52 | collapseWhitespace: false 53 | } 54 | } 55 | }, 56 | { 57 | test: /\.(png|woff|woff2|eot|ttf|svg)$/, 58 | loader: "url-loader", 59 | options: { 60 | limit: 10000 61 | } 62 | }, 63 | { 64 | test: /\.(raw|liquid)$/, 65 | loader: "raw-loader" 66 | } 67 | ] 68 | }, 69 | plugins: [ 70 | new webpack.IgnorePlugin({ resourceRegExp: /canvas/ }, { resourceRegExp: /jsdom$/ }), 71 | new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }), 72 | new CopyWebpackPlugin({ 73 | patterns: [ 74 | { from: `./src/data/demo.json`, to: `./data/demo.json` }, 75 | { from: `./src/config.publish.json`, to: `config.json` }, 76 | { from: `./src/config.runtime.json`, to: `assets/config.json` } 77 | ] 78 | }) 79 | ], 80 | resolve: { 81 | extensions: [".ts", ".tsx", ".js", ".jsx", ".html", ".scss"] 82 | } 83 | }; 84 | 85 | const publisherRuntimeConfig = merge(runtimeConfig, { 86 | entry: { "styles/theme": `./src/themes/website/styles/styles.scss` }, 87 | output: { "path": path.resolve(__dirname, "dist/publisher/assets") } 88 | }); 89 | 90 | module.exports = { 91 | default: [publisherConfig, publisherRuntimeConfig], 92 | publisherConfig, 93 | publisherRuntimeConfig 94 | } -------------------------------------------------------------------------------- /webpack.runtime.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 4 | 5 | 6 | const runtimeConfig = { 7 | mode: "development", 8 | target: "web", 9 | entry: { 10 | "scripts/theme": ["./src/startup.runtime.ts"], 11 | }, 12 | output: { 13 | filename: "./[name].js", 14 | path: path.resolve(__dirname, "dist/runtime"), 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.scss$/, 20 | use: [ 21 | MiniCssExtractPlugin.loader, 22 | { loader: "css-loader", options: { url: false } }, 23 | { loader: "postcss-loader" }, 24 | { loader: "sass-loader" } 25 | ] 26 | }, 27 | { 28 | test: /\.tsx?$/, 29 | loader: "ts-loader", 30 | options: { 31 | allowTsInNodeModules: true 32 | } 33 | }, 34 | { 35 | test: /\.html$/, 36 | loader: "html-loader", 37 | options: { 38 | esModule: true, 39 | sources: false, 40 | minimize: { 41 | removeComments: false, 42 | collapseWhitespace: false 43 | } 44 | } 45 | }, 46 | { 47 | test: /\.(svg)$/i, 48 | type: "asset/inline" 49 | }, 50 | { 51 | test: /\.liquid$/, 52 | loader: "raw-loader" 53 | } 54 | ] 55 | }, 56 | plugins: [ 57 | new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" }), 58 | new CopyWebpackPlugin({ 59 | patterns: [ 60 | { from: `./src/themes/website/styles/fonts`, to: "styles/fonts" }, 61 | { from: `./src/themes/website/assets` } 62 | ] 63 | }) 64 | ], 65 | resolve: { 66 | extensions: [".js", ".ts", ".jsx", ".tsx", ".html", ".scss"], 67 | fallback: { 68 | "buffer": false, 69 | "stream": require.resolve("stream-browserify") 70 | } 71 | } 72 | } 73 | 74 | module.exports = runtimeConfig; --------------------------------------------------------------------------------