├── .editorconfig ├── .env.sample ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .prettierignore ├── license.md ├── package-lock.json ├── package.json ├── readme.md ├── src ├── components.d.ts ├── components │ ├── ezp-auth │ │ ├── ezp-auth.scss │ │ ├── ezp-auth.tsx │ │ └── readme.md │ ├── ezp-backdrop │ │ ├── ezp-backdrop.scss │ │ ├── ezp-backdrop.tsx │ │ └── readme.md │ ├── ezp-dialog │ │ ├── ezp-dialog.scss │ │ ├── ezp-dialog.tsx │ │ └── readme.md │ ├── ezp-icon-button │ │ ├── ezp-icon-button.scss │ │ ├── ezp-icon-button.tsx │ │ └── readme.md │ ├── ezp-icon │ │ ├── assets │ │ │ ├── glyph-account.svg │ │ │ ├── glyph-checkmark-alt.svg │ │ │ ├── glyph-checkmark.svg │ │ │ ├── glyph-close.svg │ │ │ ├── glyph-color.svg │ │ │ ├── glyph-copies.svg │ │ │ ├── glyph-dark.svg │ │ │ ├── glyph-drag-drop.svg │ │ │ ├── glyph-duplex.svg │ │ │ ├── glyph-exclamation-mark.svg │ │ │ ├── glyph-expand.svg │ │ │ ├── glyph-height.svg │ │ │ ├── glyph-help.svg │ │ │ ├── glyph-light.svg │ │ │ ├── glyph-logo.svg │ │ │ ├── glyph-logout.svg │ │ │ ├── glyph-menu.svg │ │ │ ├── glyph-minus.svg │ │ │ ├── glyph-orientation.svg │ │ │ ├── glyph-paper_range.svg │ │ │ ├── glyph-plus.svg │ │ │ ├── glyph-printer.svg │ │ │ ├── glyph-quality.svg │ │ │ ├── glyph-question-mark.svg │ │ │ ├── glyph-size.svg │ │ │ ├── glyph-system.svg │ │ │ ├── glyph-trays.svg │ │ │ └── glyph-width.svg │ │ ├── ezp-icon.scss │ │ ├── ezp-icon.tsx │ │ └── readme.md │ ├── ezp-input │ │ ├── ezp-input.scss │ │ ├── ezp-input.tsx │ │ └── readme.md │ ├── ezp-label │ │ ├── ezp-label.scss │ │ ├── ezp-label.tsx │ │ └── readme.md │ ├── ezp-printer-selection │ │ ├── ezp-printer-selection.scss │ │ ├── ezp-printer-selection.tsx │ │ └── readme.md │ ├── ezp-printing │ │ ├── ezp-printing.scss │ │ ├── ezp-printing.tsx │ │ └── readme.md │ ├── ezp-select │ │ ├── ezp-select.scss │ │ ├── ezp-select.tsx │ │ └── readme.md │ ├── ezp-status │ │ ├── ezp-status.scss │ │ ├── ezp-status.tsx │ │ └── readme.md │ ├── ezp-stepper │ │ ├── ezp-stepper.scss │ │ ├── ezp-stepper.tsx │ │ └── readme.md │ ├── ezp-text-button │ │ ├── ezp-text-button.scss │ │ ├── ezp-text-button.tsx │ │ └── readme.md │ ├── ezp-upload │ │ ├── ezp-upload.scss │ │ ├── ezp-upload.tsx │ │ └── readme.md │ └── ezp-user-menu │ │ ├── ezp-user-menu.scss │ │ ├── ezp-user-menu.tsx │ │ └── readme.md ├── data │ ├── file-types.json │ ├── locales │ │ ├── de.json │ │ └── en.json │ ├── options.json │ └── user.json ├── index.html ├── index.ts ├── services │ ├── auth.ts │ ├── print.ts │ └── user.ts ├── shared │ ├── config.json │ ├── global.scss │ ├── global.ts │ └── types.d.ts └── utils │ └── utils.ts ├── stencil.config.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [src/data/locales/*.json] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | DEV_SERVER_ADDRESS=https://localhost.test 2 | DEV_SERVER_PORT=0000 3 | DEV_SERVER_HTTPS_CERT=cert.pem 4 | DEV_SERVER_HTTPS_KEY=key.pem 5 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 2 | 3 | name: Node.js Build 4 | 5 | env: 6 | MODULE_VERSION: '1.0.${{ github.run_number }}' # update MAJOR and MINOR version here 7 | DIST_PATH: 'dist/ezeep' # deployment path 8 | ARTIFACT_NAME: 'ezeep-js.zip' # artifact name 9 | 10 | DIST_PATH_NG: 'dist/directives' # angular directives deployment path 11 | ARTIFACT_NAME_NG: 'ng-directives.zip' # angular directives artifact name 12 | 13 | NPM_REGISTRY: 'https://registry.npmjs.org' # npm registry url 14 | 15 | PRERELEASE: false # true if marked as pre-release, false if production-ready 16 | 17 | on: 18 | push: 19 | branches: [ main ] 20 | 21 | jobs: 22 | 23 | build-npm: 24 | environment: Production 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | node-version: [18.x] 29 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 30 | steps: 31 | 32 | - name: Checkout main branch 33 | uses: actions/checkout@v4 34 | 35 | - name: Setup Node.js ${{ matrix.node-version }} 36 | uses: actions/setup-node@v4 37 | with: 38 | node-version: ${{ matrix.node-version }} 39 | registry-url: "${{ env.NPM_REGISTRY }}" 40 | cache: 'npm' 41 | 42 | - name: Bump package.json version of npm package 43 | uses: reedyuk/npm-version@1.1.1 44 | with: 45 | version: "${{ env.MODULE_VERSION }}" 46 | 47 | - name: Build 48 | run: | 49 | npm ci 50 | npm run build 51 | 52 | - name: Publish npm package to npm registry 53 | run: npm publish --access public 54 | env: 55 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 56 | 57 | - name: Upload npm artifact 58 | uses: actions/upload-artifact@v4 59 | with: 60 | path: ${{ env.DIST_PATH }} 61 | name: ${{ env.ARTIFACT_NAME }} 62 | 63 | - name: Upload ng directives artifacts 64 | uses: actions/upload-artifact@v4 65 | with: 66 | path: ${{ env.DIST_PATH_NG }} 67 | name: ${{ env.ARTIFACT_NAME_NG }} 68 | 69 | - name: Zip npm artifact for release 70 | uses: thedoctor0/zip-release@master 71 | with: 72 | type: 'zip' 73 | directory: ${{ env.DIST_PATH }} 74 | path: '.' 75 | filename: ${{ env.ARTIFACT_NAME }} 76 | 77 | - name: Create release with npm package 78 | uses: ncipollo/release-action@v1 79 | with: 80 | tag: "${{ env.MODULE_VERSION }}" 81 | prerelease: ${{ env.PRERELEASE }} 82 | artifacts: "${{ env.DIST_PATH }}/${{ env.ARTIFACT_NAME }}" 83 | token: ${{ secrets.GITHUB_TOKEN }} 84 | 85 | 86 | build-ng: 87 | environment: Production 88 | needs: build-npm 89 | runs-on: ubuntu-latest 90 | strategy: 91 | matrix: 92 | node-version: [18.x] 93 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 94 | 95 | steps: 96 | 97 | - name: Checkout angular branch 98 | uses: actions/checkout@v4 99 | with: 100 | ref: ngx-ezeep-js 101 | 102 | - name: Bump package.json version for angular component 103 | uses: reedyuk/npm-version@1.1.1 104 | with: 105 | package: 'ezeep-js-angular/projects/ngx-ezeep-js' 106 | version: "${{ env.MODULE_VERSION }}" 107 | 108 | - name: download angular build artifact 109 | uses: actions/download-artifact@v4 110 | with: 111 | name: ng-directives.zip 112 | path: ezeep-js-angular/projects/ngx-ezeep-js/src/lib 113 | 114 | - name: Setup Node.js ${{ matrix.node-version }} 115 | uses: actions/setup-node@v4 116 | with: 117 | node-version: ${{ matrix.node-version }} 118 | registry-url: "${{ env.NPM_REGISTRY }}" 119 | cache: 'npm' 120 | cache-dependency-path: 'ezeep-js-angular/projects/ngx-ezeep-js' 121 | 122 | - name: build angular library and publish 123 | run: | 124 | cd ezeep-js-angular 125 | npm install -g @angular/cli 126 | npm install 127 | cd projects/ngx-ezeep-js 128 | npm install 129 | cd ../.. 130 | ng build ngx-ezeep-js --prod 131 | cd dist/ngx-ezeep-js 132 | npm publish --access public 133 | env: 134 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 135 | 136 | 137 | cdn-upload-npm: 138 | environment: Production 139 | needs: build-npm 140 | runs-on: ubuntu-latest 141 | steps: 142 | 143 | - name: Download npm artifact 144 | uses: actions/download-artifact@v4 145 | with: 146 | name: ${{ env.ARTIFACT_NAME }} 147 | path: "artifacts/v${{ env.MODULE_VERSION }}" 148 | 149 | - name: Login to Azure CLI 150 | uses: azure/login@v1 151 | with: 152 | creds: ${{ secrets.AZURE_CREDENTIALS }} 153 | 154 | - name: Upload artifact to blob storage 155 | run: | 156 | azenv='${{ secrets.AZURE_ENVIRONMENT }}' 157 | account=$(jq -crM .storage.account <<< $azenv) 158 | container=$(jq -crM .storage.container <<< $azenv) 159 | 160 | az storage blob upload-batch \ 161 | --auth-mode login \ 162 | --account-name $account \ 163 | -d $container \ 164 | -s "artifacts" \ 165 | -o none 166 | 167 | - name: Update Url redirect 168 | run: | 169 | azenv='${{ secrets.AZURE_ENVIRONMENT }}' 170 | resourceGroup=$(jq -crM .resourceGroup <<< $azenv) 171 | profile=$(jq -crM .cdn.profileName <<< $azenv) 172 | endpoint=$(jq -crM .cdn.endpoint.name <<< $azenv) 173 | rules=$(jq -crM .cdn.endpoint.rules[].name <<< $azenv) 174 | container=$(jq -crM .storage.container <<< $azenv) 175 | 176 | for rule in $rules; do 177 | fileName=$(jq -crM --arg rule "$rule" \ 178 | '.cdn.endpoint.rules[] 179 | | select(.name == $rule) 180 | | .fileName' \ 181 | <<< $azenv) 182 | 183 | index=$(az cdn endpoint rule action show \ 184 | -g $resourceGroup \ 185 | --profile-name $profile \ 186 | -n $endpoint \ 187 | | jq --arg rule "$rule" '.deliveryPolicy.rules[] 188 | | select(.name == $rule) 189 | | .actions | to_entries | .[] 190 | | select(.value.name == "UrlRedirect") | .key' \ 191 | ) 192 | 193 | az cdn endpoint rule action remove \ 194 | -g $resourceGroup \ 195 | --profile-name $profile \ 196 | -n $endpoint \ 197 | --rule-name $rule \ 198 | --index $index \ 199 | -o none 200 | 201 | az cdn endpoint rule action add \ 202 | -g $resourceGroup \ 203 | --profile-name $profile \ 204 | -n $endpoint \ 205 | --rule-name $rule \ 206 | --action-name UrlRedirect \ 207 | --custom-path "/$container/v${{ env.MODULE_VERSION }}/$fileName" \ 208 | --redirect-type Found \ 209 | -o none 210 | done 211 | 212 | - name: Purge CDN endpoint 213 | run: | 214 | azenv='${{ secrets.AZURE_ENVIRONMENT }}' 215 | resourceGroup=$(jq -crM .resourceGroup <<< $azenv) 216 | profile=$(jq -crM .cdn.profileName <<< $azenv) 217 | endpoint=$(jq -crM .cdn.endpoint.name <<< $azenv) 218 | container=$(jq -crM .storage.container <<< $azenv) 219 | 220 | az cdn endpoint purge \ 221 | -g $resourceGroup \ 222 | --profile-name $profile \ 223 | -n $endpoint \ 224 | --content-paths "/$container/*" \ 225 | --no-wait \ 226 | -o none 227 | 228 | - name: Azure CLI logout 229 | run: | 230 | az logout 231 | az cache purge 232 | az account clear 233 | if: always() 234 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | www/ 3 | loader/ 4 | 5 | *~ 6 | *.sw[mnpcod] 7 | *.log 8 | *.lock 9 | *.tmp 10 | *.tmp.* 11 | log.txt 12 | *.sublime-project 13 | *.sublime-workspace 14 | 15 | .stencil/ 16 | .idea/ 17 | .vscode/ 18 | .sass-cache/ 19 | .versions/ 20 | node_modules/ 21 | $RECYCLE.BIN/ 22 | 23 | .DS_Store 24 | Thumbs.db 25 | UserInterfaceState.xcuserstate 26 | .env 27 | *.pem 28 | *.crt 29 | *.key 30 | stencil.config.ts 31 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | www/ 3 | loader/ 4 | 5 | src/components.d.ts 6 | 7 | package-lock.json 8 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Thinprint GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ezeep/ezeep-js", 3 | "version": "1.0.0-alpha.0", 4 | "description": "A JavaScript library to enable easy and fast printing on any web application uising ezeep Blue.", 5 | "author": "Thinprint GmbH", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/ezeep/ezeep-js.git" 10 | }, 11 | "main": "dist/index.cjs.js", 12 | "module": "dist/custom-elements/index.js", 13 | "es2015": "dist/esm/index.mjs", 14 | "es2017": "dist/esm/index.mjs", 15 | "types": "dist/custom-elements/index.d.ts", 16 | "collection": "dist/collection/collection-manifest.json", 17 | "collection:main": "dist/collection/index.js", 18 | "unpkg": "dist/ezeep-js/ezeep-js.esm.js", 19 | "files": [ 20 | "dist/", 21 | "loader/", 22 | "www/" 23 | ], 24 | "scripts": { 25 | "build": "stencil build --docs", 26 | "start": "stencil build --dev --watch --serve", 27 | "generate": "stencil generate", 28 | "stylelint": "stylelint \"**/*.scss\" --fix", 29 | "prettier": "prettier \"**/*.{json,ts,tsx,md,html}\" --write", 30 | "format": "npm run stylelint && npm run prettier" 31 | }, 32 | "dependencies": { 33 | "@azure/storage-blob": "^12.8.0", 34 | "@cortado-holding/colors": "^1.1.6", 35 | "@cortado-holding/utils": "^1.0.1", 36 | "@stencil/core": "^2.14.0", 37 | "fetch-intercept": "^2.4.0", 38 | "i18next": "21.2.4", 39 | "whatwg-fetch": "^3.6.2" 40 | }, 41 | "devDependencies": { 42 | "@rollup/plugin-replace": "3.0.0", 43 | "@stencil/angular-output-target": "0.2.0", 44 | "@stencil/react-output-target": "^0.1.0", 45 | "@stencil/sass": "^1.5.2", 46 | "@stencil/store": "^1.4.1", 47 | "@types/css-font-loading-module": "^0.0.6", 48 | "@types/node": "^15.14.9", 49 | "dotenv": "^10.0.0", 50 | "prettier": "^2.4.1", 51 | "rollup": "2.58.0", 52 | "rollup-plugin-node-polyfills": "^0.2.1", 53 | "stylelint": "^13.13.1", 54 | "stylelint-config-prettier": "^8.0.2", 55 | "stylelint-order": "^4.1.0", 56 | "stylelint-prettier": "^1.2.0" 57 | }, 58 | "prettier": { 59 | "printWidth": 100, 60 | "semi": false, 61 | "singleQuote": true, 62 | "htmlWhitespaceSensitivity": "strict", 63 | "overrides": [ 64 | { 65 | "files": "src/data/locales/*.json", 66 | "options": { 67 | "tabWidth": 4 68 | } 69 | } 70 | ] 71 | }, 72 | "stylelint": { 73 | "extends": [ 74 | "stylelint-config-prettier" 75 | ], 76 | "plugins": [ 77 | "stylelint-prettier", 78 | "stylelint-order" 79 | ], 80 | "rules": { 81 | "prettier/prettier": true, 82 | "order/order": [ 83 | "dollar-variables", 84 | "custom-properties", 85 | "declarations", 86 | "rules", 87 | "at-rules" 88 | ], 89 | "order/properties-alphabetical-order": true 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/ezp-auth/ezp-auth.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | --ezp-theme-solid: var(--ezp-accent-cyan-solid); 9 | --ezp-theme-translucent: var(--ezp-accent-cyan-translucent); 10 | 11 | display: block; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/ezp-auth/ezp-auth.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, h, Prop, State, Event, EventEmitter, Listen } from '@stencil/core' 2 | import { EzpAuthorizationService } from '../../services/auth' 3 | import authStore from '../../services/auth' 4 | import i18next from 'i18next' 5 | @Component({ 6 | tag: 'ezp-auth', 7 | styleUrl: 'ezp-auth.scss', 8 | shadow: true, 9 | }) 10 | export class EzpAuth { 11 | @Prop({ mutable: true }) clientID: string 12 | @Prop({ mutable: true }) redirectURI: string 13 | @Prop() hidelogin: boolean 14 | @Prop() trigger: string 15 | @Prop() code: string 16 | 17 | @State() auth: EzpAuthorizationService 18 | @State() authURI: string 19 | @State() accessToken: string 20 | 21 | @Event() authCancel: EventEmitter 22 | @Event() authSuccess: EventEmitter 23 | 24 | @Listen('dialogAction') 25 | listenDialogAction() { 26 | this.openSignInWindow(this.auth.authURI.toString(), 'ezeep Login') 27 | } 28 | 29 | @Listen('dialogClose') 30 | listenDialogClose() { 31 | this.authCancel.emit() 32 | } 33 | 34 | @Listen('statusCancel') 35 | listenStatusCancel() { 36 | this.authCancel.emit() 37 | } 38 | 39 | oauthPopupWindow: Window = null 40 | previousUrl = null 41 | 42 | openSignInWindow(url: string, name: string) { 43 | if (authStore.state.isAuthorized) { 44 | this.authCancel.emit() 45 | this.authSuccess.emit() 46 | return 47 | } 48 | 49 | // remove any existing event listeners 50 | window.removeEventListener('message', this.receiveMessage) 51 | 52 | // window features 53 | const windowFeatures = 'toolbar=no, menubar=no, width=600, height=7000, top=100, left=100' 54 | 55 | if (this.oauthPopupWindow === null || this.oauthPopupWindow.closed) { 56 | /* if the pointer to the window object in memory does not exist 57 | or if such pointer exists but the window was closed */ 58 | 59 | this.oauthPopupWindow = window.open(url, name, windowFeatures) 60 | } else if (this.previousUrl !== this.auth.authURI.toString()) { 61 | /* if the resource to load is different, 62 | then we load it in the already opened secondary window and then 63 | we bring such window back on top/in front of its parent window. */ 64 | 65 | this.oauthPopupWindow = window.open(url, name, windowFeatures) 66 | 67 | if ( 68 | !this.oauthPopupWindow || 69 | this.oauthPopupWindow.closed || 70 | typeof this.oauthPopupWindow.closed == 'undefined' 71 | ) { 72 | alert('popup blocked') 73 | } 74 | 75 | this.oauthPopupWindow.focus() 76 | } else { 77 | /* else the window reference must exist and the window 78 | is not closed; therefore, we can bring it back on top of any other 79 | window with the focus() method. There would be no need to re-create 80 | the window or to reload the referenced resource. */ 81 | 82 | this.oauthPopupWindow.focus() 83 | } 84 | 85 | // add the listener for receiving a message from the popup 86 | window.addEventListener('message', (event) => this.receiveMessage(event), false) 87 | 88 | this.previousUrl = this.auth.authURI 89 | } 90 | 91 | receiveMessage(event) { 92 | authStore.state.code = event.data 93 | this.auth.getAccessToken().then(() => { 94 | this.authCancel.emit() 95 | this.authSuccess.emit() 96 | }) 97 | } 98 | 99 | async componentWillLoad() { 100 | this.auth = new EzpAuthorizationService(this.redirectURI, this.clientID) 101 | 102 | if (this.code) { 103 | authStore.state.code = this.code 104 | await this.auth.getAccessToken() 105 | this.authCancel.emit() 106 | this.authSuccess.emit() 107 | } 108 | 109 | if (authStore.state.isAuthorized === false) { 110 | this.auth.generateCodeVerifier() 111 | await this.auth.generateCodeChallenge(authStore.state.codeVerifier) 112 | this.auth.buildAuthURI() 113 | } 114 | 115 | if (this.hidelogin && this.trigger === 'button') { 116 | this.openSignInWindow(this.auth.authURI.toString(), 'ezeep Login') 117 | } 118 | } 119 | 120 | render() { 121 | return ( 122 | 123 | {this.hidelogin && this.trigger === 'button' ? ( 124 | 125 | ) : this.hidelogin && this.trigger === 'file' ? ( 126 | this.openSignInWindow(this.auth.authURI.toString(), 'ezeep Login')} 130 | label={i18next.t('button_actions.select_printer')} 131 | /> 132 | ) : ( 133 | 142 | )} 143 | 144 | ) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/components/ezp-auth/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-auth 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------- | ---------------- | ----------- | --------- | ----------- | 9 | | `clientID` | `client-i-d` | | `string` | `undefined` | 10 | | `code` | `code` | | `string` | `undefined` | 11 | | `hidelogin` | `hidelogin` | | `boolean` | `undefined` | 12 | | `redirectURI` | `redirect-u-r-i` | | `string` | `undefined` | 13 | | `trigger` | `trigger` | | `string` | `undefined` | 14 | 15 | ## Events 16 | 17 | | Event | Description | Type | 18 | | ------------- | ----------- | ------------------------- | 19 | | `authCancel` | | `CustomEvent` | 20 | | `authSuccess` | | `CustomEvent` | 21 | 22 | ## Dependencies 23 | 24 | ### Used by 25 | 26 | - [ezp-printing](../ezp-printing) 27 | 28 | ### Depends on 29 | 30 | - [ezp-status](../ezp-status) 31 | - [ezp-text-button](../ezp-text-button) 32 | - [ezp-dialog](../ezp-dialog) 33 | 34 | ### Graph 35 | 36 | ```mermaid 37 | graph TD; 38 | ezp-auth --> ezp-status 39 | ezp-auth --> ezp-text-button 40 | ezp-auth --> ezp-dialog 41 | ezp-status --> ezp-icon 42 | ezp-status --> ezp-label 43 | ezp-status --> ezp-text-button 44 | ezp-text-button --> ezp-label 45 | ezp-dialog --> ezp-icon-button 46 | ezp-dialog --> ezp-icon 47 | ezp-dialog --> ezp-label 48 | ezp-dialog --> ezp-text-button 49 | ezp-icon-button --> ezp-icon 50 | ezp-printing --> ezp-auth 51 | style ezp-auth fill:#f9f,stroke:#333,stroke-width:4px 52 | ``` 53 | 54 | --- 55 | -------------------------------------------------------------------------------- /src/components/ezp-backdrop/ezp-backdrop.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | animation: var(--ezp-backdrop-animation-name, host-in) var(--ezp-duration-3) forwards 9 | var(--ezp-easing-out-quart); 10 | background: var(--ezp-core-backdrop); 11 | border-radius: var(--ezp-backdrop-border-radius, 6px); 12 | display: block; 13 | height: 100%; 14 | left: 0; 15 | opacity: 0; 16 | position: absolute; 17 | top: 0; 18 | width: 100%; 19 | z-index: 4; 20 | 21 | @at-root { 22 | #{&}(.hide) { 23 | --ezp-backdrop-animation-name: host-out; 24 | } 25 | } 26 | 27 | @keyframes host-in { 28 | 0% { 29 | opacity: 0; 30 | } 31 | 32 | 100% { 33 | opacity: 1; 34 | } 35 | } 36 | 37 | @keyframes host-out { 38 | 0% { 39 | opacity: 1; 40 | } 41 | 42 | 100% { 43 | opacity: 0; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/ezp-backdrop/ezp-backdrop.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Element, Host, Prop, Listen, Event, EventEmitter, h } from '@stencil/core' 2 | 3 | @Component({ 4 | tag: 'ezp-backdrop', 5 | styleUrl: 'ezp-backdrop.scss', 6 | shadow: true, 7 | }) 8 | export class EzpBackdrop { 9 | @Element() component!: HTMLEzpBackdropElement 10 | @Prop({ mutable: true }) visible: boolean = true 11 | @Event() backdropHideStart: EventEmitter 12 | @Event() backdropHideEnd: EventEmitter 13 | 14 | private handleClick() { 15 | this.visible = false 16 | this.backdropHideStart.emit() 17 | } 18 | 19 | @Listen('animationend') 20 | listenAnimationEnd() { 21 | if (!this.visible) { 22 | this.backdropHideEnd.emit() 23 | } 24 | } 25 | 26 | render() { 27 | return this.handleClick()} /> 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/ezp-backdrop/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-backdrop 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | --------- | --------- | ----------- | --------- | ------- | 9 | | `visible` | `visible` | | `boolean` | `true` | 10 | 11 | ## Events 12 | 13 | | Event | Description | Type | 14 | | ------------------- | ----------- | ------------------ | 15 | | `backdropHideEnd` | | `CustomEvent` | 16 | | `backdropHideStart` | | `CustomEvent` | 17 | 18 | ## Dependencies 19 | 20 | ### Used by 21 | 22 | - [ezp-select](../ezp-select) 23 | - [ezp-user-menu](../ezp-user-menu) 24 | 25 | ### Graph 26 | 27 | ```mermaid 28 | graph TD; 29 | ezp-select --> ezp-backdrop 30 | ezp-user-menu --> ezp-backdrop 31 | style ezp-backdrop fill:#f9f,stroke:#333,stroke-width:4px 32 | ``` 33 | 34 | --- 35 | -------------------------------------------------------------------------------- /src/components/ezp-dialog/ezp-dialog.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | --ezp-icon-fill: var(--ezp-theme-solid); 3 | 4 | align-content: center; 5 | background: var(--ezp-core-backdrop); 6 | display: grid; 7 | grid-template: auto / minmax(auto, 280px); 8 | height: 100%; 9 | justify-content: center; 10 | left: 0; 11 | position: fixed; 12 | top: 0; 13 | width: 100%; 14 | } 15 | 16 | #box { 17 | background: var(--ezp-core-surface-tertiary); 18 | border-radius: 6px; 19 | box-shadow: var(--ezp-dialog-box-boxShadow, 0 0 0 1px var(--ezp-black-10)); 20 | } 21 | 22 | #header { 23 | display: flex; 24 | justify-content: flex-end; 25 | padding: var(--ezp-spacing-2) var(--ezp-spacing-3); 26 | } 27 | 28 | #body { 29 | align-items: center; 30 | display: flex; 31 | flex-direction: column; 32 | gap: var(--ezp-spacing-5); 33 | padding: 0 var(--ezp-spacing-5); 34 | text-align: center; 35 | } 36 | 37 | #text { 38 | color: var(--ezp-core-foreground-primary); 39 | display: flex; 40 | flex-direction: column; 41 | gap: var(--ezp-spacing-4); 42 | } 43 | 44 | #footer { 45 | display: grid; 46 | grid-template: auto / minmax(144px, auto); 47 | justify-content: center; 48 | padding: var(--ezp-spacing-5); 49 | } 50 | -------------------------------------------------------------------------------- /src/components/ezp-dialog/ezp-dialog.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, Event, EventEmitter, h } from '@stencil/core' 2 | import i18next from 'i18next' 3 | import { IconNameTypes, IconSizeTypes } from '../../shared/types' 4 | 5 | @Component({ 6 | tag: 'ezp-dialog', 7 | styleUrl: 'ezp-dialog.scss', 8 | shadow: true, 9 | }) 10 | export class EzpDialog { 11 | /** 12 | * 13 | * Events 14 | * 15 | */ 16 | 17 | @Event() dialogClose: EventEmitter 18 | @Event() dialogAction: EventEmitter 19 | 20 | /** 21 | * 22 | * Properties 23 | * 24 | */ 25 | 26 | @Prop() heading: string 27 | @Prop() description: string 28 | @Prop() action: string = i18next.t('button_actions.close') 29 | @Prop() iconName?: IconNameTypes 30 | @Prop() iconSize: IconSizeTypes = 'large' 31 | @Prop() iconFramed: boolean = true 32 | @Prop() instance: string 33 | 34 | /** 35 | * 36 | * Lifecycle methods 37 | * 38 | */ 39 | 40 | componentWillLoad() {} 41 | 42 | /** 43 | * 44 | * Private methods 45 | * 46 | */ 47 | 48 | private handleClose = () => { 49 | this.dialogClose.emit(this.instance) 50 | } 51 | 52 | private handleAction = () => { 53 | this.dialogAction.emit(this.instance) 54 | } 55 | 56 | /** 57 | * 58 | * Render method 59 | * 60 | */ 61 | 62 | render() { 63 | return ( 64 | 65 |
66 | 74 |
75 | {this.iconName && ( 76 | 77 | )} 78 |
79 | 80 | 81 |
82 |
83 | 91 |
92 |
93 | ) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/components/ezp-dialog/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-dialog 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------- | ------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | 9 | | `action` | `action` | | `string` | `i18next.t('button_actions.close')` | 10 | | `description` | `description` | | `string` | `undefined` | 11 | | `heading` | `heading` | Properties | `string` | `undefined` | 12 | | `iconFramed` | `icon-framed` | | `boolean` | `true` | 13 | | `iconName` | `icon-name` | | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 14 | | `iconSize` | `icon-size` | | `"huge" \| "large" \| "normal"` | `'large'` | 15 | | `instance` | `instance` | | `string` | `undefined` | 16 | 17 | ## Events 18 | 19 | | Event | Description | Type | 20 | | -------------- | ----------- | ------------------ | 21 | | `dialogAction` | | `CustomEvent` | 22 | | `dialogClose` | Events | `CustomEvent` | 23 | 24 | ## Dependencies 25 | 26 | ### Used by 27 | 28 | - [ezp-auth](../ezp-auth) 29 | - [ezp-printing](../ezp-printing) 30 | 31 | ### Depends on 32 | 33 | - [ezp-icon-button](../ezp-icon-button) 34 | - [ezp-icon](../ezp-icon) 35 | - [ezp-label](../ezp-label) 36 | - [ezp-text-button](../ezp-text-button) 37 | 38 | ### Graph 39 | 40 | ```mermaid 41 | graph TD; 42 | ezp-dialog --> ezp-icon-button 43 | ezp-dialog --> ezp-icon 44 | ezp-dialog --> ezp-label 45 | ezp-dialog --> ezp-text-button 46 | ezp-icon-button --> ezp-icon 47 | ezp-text-button --> ezp-label 48 | ezp-auth --> ezp-dialog 49 | ezp-printing --> ezp-dialog 50 | style ezp-dialog fill:#f9f,stroke:#333,stroke-width:4px 51 | ``` 52 | 53 | --- 54 | -------------------------------------------------------------------------------- /src/components/ezp-icon-button/ezp-icon-button.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | display: block; 9 | 10 | @at-root { 11 | #{&}(.primary) { 12 | --ezp-iconButton-background-disabled: var(--ezp-core-shade-primary); 13 | --ezp-iconButton-background-init: var(--ezp-theme-solid); 14 | --ezp-iconButton-color-disabled: var(--ezp-core-foreground-tertiary); 15 | --ezp-iconButton-color-init: var(--ezp-white-100); 16 | --ezp-iconButton-opacity-active: 0.74; 17 | --ezp-iconButton-opacity-hover: 0.84; 18 | } 19 | 20 | #{&}(.secondary) { 21 | --ezp-iconButton-background-active: var(--ezp-core-shade-tertiary); 22 | --ezp-iconButton-background-disabled: var(--ezp-core-shade-primary); 23 | --ezp-iconButton-background-hover: var(--ezp-core-shade-secondary); 24 | --ezp-iconButton-background-init: var(--ezp-core-shade-primary); 25 | --ezp-iconButton-color-disabled: var(--ezp-core-foreground-tertiary); 26 | --ezp-iconButton-color-init: var(--ezp-theme-solid); 27 | } 28 | 29 | #{&}(.tertiary) { 30 | --ezp-iconButton-background-active: var(--ezp-core-shade-secondary); 31 | --ezp-iconButton-background-hover: var(--ezp-core-shade-primary); 32 | --ezp-iconButton-background-init: transparent; 33 | --ezp-iconButton-color-disabled: var(--ezp-core-foreground-tertiary); 34 | --ezp-iconButton-color-init: var(--ezp-theme-solid); 35 | } 36 | 37 | #{&}(.quaternary) { 38 | --ezp-iconButton-background-init: transparent; 39 | --ezp-iconButton-color-disabled: var(--ezp-core-foreground-tertiary); 40 | --ezp-iconButton-color-init: var(--ezp-theme-solid); 41 | --ezp-iconButton-opacity-active: 0.58; 42 | --ezp-iconButton-opacity-hover: 0.74; 43 | } 44 | } 45 | } 46 | 47 | #button { 48 | align-items: center; 49 | appearance: none; 50 | background: var(--ezp-iconButton-background, var(--ezp-iconButton-background-init)); 51 | border: 0; 52 | border-radius: 3px; 53 | box-shadow: none; 54 | color: var(--ezp-iconButton-color, var(--ezp-iconButton-color-init)); 55 | cursor: var(--ezp-iconButton-cursor, pointer); 56 | display: flex; 57 | justify-content: center; 58 | margin: 0; 59 | opacity: var(--ezp-iconButton-opacity, 1); 60 | outline: none; 61 | padding: var(--ezp-spacing-2); 62 | 63 | &:not(:disabled) { 64 | @media (hover: hover) { 65 | &:hover { 66 | --ezp-iconButton-background: var( 67 | --ezp-iconButton-background-hover, 68 | var(--ezp-iconButton-background-init) 69 | ); 70 | --ezp-iconButton-opacity: var(--ezp-iconButton-opacity-hover, 1); 71 | } 72 | 73 | &:active { 74 | --ezp-iconButton-background: var( 75 | --ezp-iconButton-background-active, 76 | var(--ezp-iconButton-background-init) 77 | ); 78 | --ezp-iconButton-opacity: var(--ezp-iconButton-opacity-active, 1); 79 | } 80 | } 81 | } 82 | 83 | &:disabled { 84 | --ezp-iconButton-background: var( 85 | --ezp-iconButton-background-disabled, 86 | var(--ezp-iconButton-background-init) 87 | ); 88 | --ezp-iconButton-color: var(--ezp-iconButton-color-disabled); 89 | --ezp-iconButton-cursor: default; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/components/ezp-icon-button/ezp-icon-button.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, h } from '@stencil/core' 2 | import { IconButtonLevelTypes, IconButtonTypeTypes, IconNameTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-icon-button', 6 | styleUrl: 'ezp-icon-button.scss', 7 | shadow: true, 8 | }) 9 | export class EzpIconButton { 10 | /** 11 | * 12 | * Properties 13 | * 14 | */ 15 | 16 | /** Description... */ 17 | @Prop() blank: boolean = false 18 | 19 | /** Description... */ 20 | @Prop() disabled: boolean = false 21 | 22 | /** Description... */ 23 | @Prop() href: string 24 | 25 | /** Description... */ 26 | @Prop() icon!: IconNameTypes 27 | 28 | /** Description... */ 29 | @Prop() level: IconButtonLevelTypes = 'primary' 30 | 31 | /** Description... */ 32 | @Prop() type: IconButtonTypeTypes 33 | 34 | /** 35 | * 36 | * Render method 37 | * 38 | */ 39 | 40 | render() { 41 | const TagType = this.type !== undefined ? 'button' : 'a' 42 | const attributes = 43 | this.type !== undefined 44 | ? { 45 | type: this.type, 46 | disabled: this.disabled, 47 | } 48 | : { 49 | href: this.href, 50 | target: this.blank ? '_blank' : '_self', 51 | } 52 | 53 | return ( 54 | 55 | 56 | 57 | 58 | 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/ezp-icon-button/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-icon-button 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------------- | ---------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | 9 | | `blank` | `blank` | Description... | `boolean` | `false` | 10 | | `disabled` | `disabled` | Description... | `boolean` | `false` | 11 | | `href` | `href` | Description... | `string` | `undefined` | 12 | | `icon` _(required)_ | `icon` | Description... | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 13 | | `level` | `level` | Description... | `"primary" \| "quaternary" \| "secondary" \| "tertiary"` | `'primary'` | 14 | | `type` | `type` | Description... | `"button"` | `undefined` | 15 | 16 | ## Dependencies 17 | 18 | ### Used by 19 | 20 | - [ezp-dialog](../ezp-dialog) 21 | - [ezp-printer-selection](../ezp-printer-selection) 22 | - [ezp-printing](../ezp-printing) 23 | - [ezp-user-menu](../ezp-user-menu) 24 | 25 | ### Depends on 26 | 27 | - [ezp-icon](../ezp-icon) 28 | 29 | ### Graph 30 | 31 | ```mermaid 32 | graph TD; 33 | ezp-icon-button --> ezp-icon 34 | ezp-dialog --> ezp-icon-button 35 | ezp-printer-selection --> ezp-icon-button 36 | ezp-printing --> ezp-icon-button 37 | ezp-user-menu --> ezp-icon-button 38 | style ezp-icon-button fill:#f9f,stroke:#333,stroke-width:4px 39 | ``` 40 | 41 | --- 42 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-account.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-checkmark-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-checkmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-copies.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-drag-drop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-duplex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-exclamation-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-expand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-height.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-logout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-orientation.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-paper_range.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-printer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-quality.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-question-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-size.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-system.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-trays.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/components/ezp-icon/assets/glyph-width.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/ezp-icon/ezp-icon.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | align-content: center; 9 | background: var(--ezp-icon-background, transparent); 10 | border-radius: var(--ezp-icon-borderRadius, 0); 11 | display: grid; 12 | grid-template: var(--ezp-icon-gridTemplate) / var(--ezp-icon-gridTemplate); 13 | height: var(--ezp-icon-height, var(--ezp-icon-size)); 14 | justify-content: center; 15 | padding: var(--ezp-icon-padding, 0); 16 | width: var(--ezp-icon-width, var(--ezp-icon-size)); 17 | 18 | @at-root { 19 | #{&}(.framed) { 20 | --ezp-icon-background: var(--ezp-core-shade-primary); 21 | --ezp-icon-borderRadius: 50%; 22 | --ezp-icon-padding: var(--ezp-icon-frame); 23 | } 24 | 25 | #{&}(.normal) { 26 | --ezp-icon-size: 18px; 27 | --ezp-icon-gridTemplate: 24px; 28 | --ezp-icon-frame: var(--ezp-spacing-3); 29 | } 30 | 31 | #{&}(.large) { 32 | --ezp-icon-size: 36px; 33 | --ezp-icon-gridTemplate: 48px; 34 | --ezp-icon-frame: var(--ezp-spacing-4); 35 | } 36 | 37 | #{&}(.huge) { 38 | --ezp-icon-size: 54px; 39 | --ezp-icon-gridTemplate: 72px; 40 | --ezp-icon-frame: var(--ezp-spacing-5); 41 | } 42 | } 43 | } 44 | 45 | svg { 46 | display: block; 47 | fill: var(--ezp-icon-fill, currentColor); 48 | height: 100%; 49 | width: 100%; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/ezp-icon/ezp-icon.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, getAssetPath, h } from '@stencil/core' 2 | import { IconNameTypes, IconSizeTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-icon', 6 | styleUrl: 'ezp-icon.scss', 7 | shadow: true, 8 | assetsDirs: ['assets'], 9 | }) 10 | export class EzpIcon { 11 | private glyph: string 12 | 13 | /** 14 | * 15 | * Properties 16 | * 17 | */ 18 | 19 | /** Description... */ 20 | @Prop() name!: IconNameTypes 21 | 22 | /** Description... */ 23 | @Prop() size: IconSizeTypes = 'normal' 24 | 25 | /** Description... */ 26 | @Prop() framed: boolean = false 27 | 28 | /** 29 | * 30 | * Lifecycle methods 31 | * 32 | */ 33 | 34 | async componentWillLoad() { 35 | await fetch(getAssetPath(`./assets/glyph-${this.name}.svg`)) 36 | .then((response) => response.text()) 37 | .then((result) => { 38 | this.glyph = result 39 | }) 40 | .catch((error) => console.log(error)) 41 | } 42 | 43 | /** 44 | * 45 | * Render method 46 | * 47 | */ 48 | 49 | render() { 50 | return ( 51 | 52 |
53 | 54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/components/ezp-icon/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-icon 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------------- | --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | 9 | | `framed` | `framed` | Description... | `boolean` | `false` | 10 | | `name` _(required)_ | `name` | Description... | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 11 | | `size` | `size` | Description... | `"huge" \| "large" \| "normal"` | `'normal'` | 12 | 13 | ## Dependencies 14 | 15 | ### Used by 16 | 17 | - [ezp-dialog](../ezp-dialog) 18 | - [ezp-icon-button](../ezp-icon-button) 19 | - [ezp-select](../ezp-select) 20 | - [ezp-status](../ezp-status) 21 | - [ezp-stepper](../ezp-stepper) 22 | - [ezp-upload](../ezp-upload) 23 | - [ezp-user-menu](../ezp-user-menu) 24 | - [ezp-input](../ezp-input) 25 | 26 | ### Graph 27 | 28 | ```mermaid 29 | graph TD; 30 | ezp-dialog --> ezp-icon 31 | ezp-icon-button --> ezp-icon 32 | ezp-select --> ezp-icon 33 | ezp-status --> ezp-icon 34 | ezp-stepper --> ezp-icon 35 | ezp-upload --> ezp-icon 36 | ezp-user-menu --> ezp-icon 37 | ezp-input --> ezp-icon 38 | style ezp-icon fill:#f9f,stroke:#333,stroke-width:4px 39 | ``` 40 | 41 | --- 42 | -------------------------------------------------------------------------------- /src/components/ezp-input/ezp-input.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use './../../shared/global'; 8 | 9 | /** 10 | * 11 | * Rules 12 | * 13 | */ 14 | 15 | :host { 16 | display: grid; 17 | grid-template: var(--ezp-input-grid-template, auto / auto auto 1fr); 18 | height: 30px; 19 | position: relative; 20 | gap: var(--ezp-spacing-3); 21 | background: var(--ezp-input-background, var(--ezp-core-surface-tertiary)); 22 | padding: 0 var(--ezp-spacing-3); 23 | z-index: var(--ezp-input-z-index, 3); 24 | border-radius: var(--ezp-input-border-radius, 0); 25 | box-shadow: var(--ezp-input-box-shadow-position-top, 0 -1px 0 0) var(--ezp-core-outline), 26 | var(--ezp-input-box-shadow-position-right, 1px 0 0 0) var(--ezp-core-outline), 27 | var(--ezp-input-box-shadow-position-bottom, 0 1px 0 0) var(--ezp-core-outline), 28 | var(--ezp-input-box-shadow-position-left, -1px 0 0 0) var(--ezp-core-outline); 29 | align-items: center; 30 | cursor: pointer; 31 | 32 | @at-root { 33 | #{&}([suffix]) { 34 | --ezp-input-grid-template: auto / auto auto 1fr auto; 35 | --ezp-input-input-padding-right: 0; 36 | } 37 | 38 | #{&}(.focused) { 39 | --ezp-input-icon-color: var(--ezp-theme-solid); 40 | --ezp-input-label-color: var(--ezp-theme-solid); 41 | } 42 | 43 | #{&}(:first-child) { 44 | --ezp-input-border-radius: 3px 3px 0 0; 45 | } 46 | 47 | #{&}(:last-child) { 48 | --ezp-input-border-radius: 0 0 3px 3px; 49 | } 50 | 51 | #{&}(:first-child:last-child) { 52 | --ezp-input-border-radius: 3px; 53 | } 54 | 55 | #{&}(:not(:last-child)) { 56 | --ezp-input-box-shadow-position-bottom: 0 0 0 0; 57 | } 58 | 59 | #{&}(:hover) { 60 | --ezp-input-background: linear-gradient( 61 | 0deg, 62 | var(--ezp-core-shade-primary) 0%, 63 | var(--ezp-core-shade-primary) 100% 64 | ), 65 | var(--ezp-core-surface-tertiary); 66 | } 67 | } 68 | } 69 | 70 | #label { 71 | color: var(--ezp-input-label-color, var(--ezp-core-foreground-secondary)); 72 | } 73 | 74 | #icon { 75 | color: var(--ezp-input-icon-color, var(--ezp-core-foreground-secondary)); 76 | } 77 | 78 | #input { 79 | appearance: textfield; 80 | background: transparent; 81 | border: 0; 82 | border-radius: 3px; 83 | box-shadow: none; 84 | box-sizing: border-box; 85 | color: var(--ezp-core-foreground-primary); 86 | cursor: inherit; 87 | font-family: inherit; 88 | font-feature-settings: 'tnum'; 89 | font-size: 14px; 90 | -webkit-font-smoothing: antialiased; 91 | height: 30px; 92 | letter-spacing: #{global.getLetterSpacing(14px)}; 93 | margin: 0; 94 | outline: none; 95 | padding: 0 var(--ezp-input-input-padding-right, var(--ezp-spacing-1)) 0 0; 96 | text-align: right; 97 | width: 100%; 98 | 99 | &[type='number'] { 100 | &::-webkit-inner-spin-button, 101 | &::-webkit-outer-spin-button { 102 | -webkit-appearance: none; 103 | } 104 | } 105 | } 106 | 107 | #suffix { 108 | color: var(--ezp-core-foreground-tertiary); 109 | } 110 | -------------------------------------------------------------------------------- /src/components/ezp-input/ezp-input.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, h, State, EventEmitter, Event } from '@stencil/core' 2 | import { IconNameTypes } from './../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-input', 6 | styleUrl: 'ezp-input.scss', 7 | shadow: true, 8 | }) 9 | export class EzpInput { 10 | private input?: HTMLInputElement 11 | private timeout = null 12 | 13 | /** 14 | * 15 | * Properties 16 | * 17 | */ 18 | 19 | /** Description... */ 20 | @Prop() label: string = 'Label' 21 | 22 | /** Description... */ 23 | @Prop({ mutable: true }) value: number | string 24 | 25 | /** Description... */ 26 | @Prop() type: string = 'text' 27 | 28 | /** Description... */ 29 | @Prop() icon: IconNameTypes = 'color' 30 | 31 | /** Description... */ 32 | @Prop({ reflect: true }) suffix: string 33 | 34 | /** Description... */ 35 | @Prop() eventType: string 36 | 37 | /** Description... */ 38 | @Prop() placeholder: string = '' 39 | 40 | 41 | /** 42 | * 43 | * Events 44 | * 45 | */ 46 | 47 | @Event() inputValueChanged: EventEmitter 48 | 49 | /** 50 | * 51 | * States 52 | * 53 | */ 54 | @State() focused: boolean = false 55 | 56 | handleChange(event) { 57 | if (this.timeout) { 58 | clearTimeout(this.timeout) 59 | } 60 | this.value = event.target.value ? event.target.value : this.type === 'number' ? 0 : '' 61 | this.timeout = setTimeout(() => { 62 | this.inputValueChanged.emit({ 63 | type: this.eventType.toLowerCase(), 64 | value: this.value, 65 | }) 66 | }, 500) 67 | } 68 | 69 | private setFocus = () => { 70 | this.input.focus() 71 | } 72 | 73 | private handleBlur = () => { 74 | this.focused = false 75 | } 76 | 77 | private handleFocus = () => { 78 | this.focused = true 79 | } 80 | 81 | render() { 82 | return ( 83 | 84 | 85 | 86 | this.handleChange(event)} 92 | ref={(input) => (this.input = input)} 93 | onFocus={this.handleFocus} 94 | onBlur={this.handleBlur} 95 | /> 96 | {this.suffix ? : null} 97 | 98 | ) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/components/ezp-input/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-input 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ---------- | ---------- | -------------- | ---------------------------------------- | ------------- | 9 | | `label` | `label` | Description... | `string` | `Label` | 10 | | `eventType` | `eventType` | Description... | `string` | `undefined` | 11 | | `value` | `value` | Description... | `number | string ` | `undefined` | 12 | | `type` | `type` | Description... | `string` | `'text'` | 13 | | `icon_name`| `icon_name`| Description... | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined'` | 14 | | `suffix` | `suffix` | Description... | `string` | `undefined` | 15 | 16 | ## Dependencies 17 | 18 | ### Used by 19 | 20 | - [ezp-printer-selection](../ezp-printer-selection) 21 | 22 | 23 | ### Graph 24 | 25 | ```mermaid 26 | graph TD; 27 | ezp-input --> ezp-label 28 | ezp-input --> ezp-icon 29 | ezp-printer-selection --> ezp-input 30 | style ezp-input fill:#f9f,stroke:#333,stroke-width:4px 31 | ``` 32 | 33 | --- 34 | -------------------------------------------------------------------------------- /src/components/ezp-label/ezp-label.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use 'sass:list'; 8 | @use './../../shared/global'; 9 | 10 | /** 11 | * 12 | * Variables 13 | * 14 | */ 15 | 16 | $levels: ( 17 | 'primary': ( 18 | 17px, 19 | 24px, 20 | ), 21 | 'secondary': ( 22 | 14px, 23 | 20px, 24 | ), 25 | 'tertiary': ( 26 | 12px, 27 | 18px, 28 | ), 29 | ); 30 | $weights: ( 31 | 'soft': 400, 32 | 'strong': 500, 33 | 'heavy': 600, 34 | ); 35 | 36 | /** 37 | * 38 | * Rules 39 | * 40 | */ 41 | 42 | :host { 43 | display: block; 44 | min-width: var(--ezp-label-minWidth, auto); 45 | padding-bottom: 1px; 46 | padding-top: 1px; 47 | 48 | &::before, 49 | &::after { 50 | content: ''; 51 | display: block; 52 | height: 0; 53 | } 54 | 55 | &::before { 56 | margin-top: calc((var(--ezp-label-offset) + 1px) * -1); 57 | } 58 | 59 | &::after { 60 | margin-bottom: calc((var(--ezp-label-offset) + 1px) * -1); 61 | } 62 | 63 | @at-root { 64 | #{&}(.no-wrap) { 65 | --ezp-label-whiteSpace: nowrap; 66 | } 67 | 68 | #{&}(.ellipsis) { 69 | --ezp-label-minWidth: 0; 70 | --ezp-label-overflow: hidden; 71 | --ezp-label-textOverflow: ellipsis; 72 | --ezp-label-whiteSpace: nowrap; 73 | --ezp-label-width: 100%; 74 | } 75 | @each $levelName, $levelProps in $levels { 76 | #{&}(.#{$levelName}) { 77 | $fontSize: list.nth($levelProps, 1); 78 | $lineHeight: list.nth($levelProps, 2); 79 | $letterSpacing: global.getLetterSpacing($fontSize); 80 | $offset: global.getOffset($fontSize, $lineHeight); 81 | 82 | --ezp-label-fontSize: #{$fontSize}; 83 | --ezp-label-lineHeight: #{$lineHeight}; 84 | --ezp-label-letterSpacing: #{$letterSpacing}; 85 | --ezp-label-offset: #{$offset}; 86 | } 87 | } 88 | 89 | @each $weightName, $weightValue in $weights { 90 | #{&}(.#{$weightName}) { 91 | --ezp-label-fontWeight: #{$weightValue}; 92 | } 93 | } 94 | } 95 | } 96 | 97 | #text { 98 | font-size: var(--ezp-label-fontSize); 99 | font-weight: var(--ezp-label-fontWeight); 100 | letter-spacing: var(--ezp-label-letterSpacing); 101 | line-height: var(--ezp-label-lineHeight); 102 | overflow: var(--ezp-label-overflow, visible); 103 | text-overflow: var(--ezp-label-textOverflow, clip); 104 | white-space: var(--ezp-label-whiteSpace, normal); 105 | width: var(--ezp-label-width, auto); 106 | } 107 | -------------------------------------------------------------------------------- /src/components/ezp-label/ezp-label.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, h } from '@stencil/core' 2 | import { LabelLevelTypes, WeightTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-label', 6 | styleUrl: 'ezp-label.scss', 7 | shadow: true, 8 | }) 9 | export class EzpLabel { 10 | /** 11 | * 12 | * Properties 13 | * 14 | */ 15 | 16 | /** Description... */ 17 | @Prop() ellipsis: boolean = false 18 | 19 | /** Description... */ 20 | @Prop() level: LabelLevelTypes = 'secondary' 21 | 22 | /** Description... */ 23 | @Prop() noWrap: boolean = false 24 | 25 | /** Description... */ 26 | @Prop() text: string = 'Label' 27 | 28 | /** Description... */ 29 | @Prop() weight: WeightTypes = 'soft' 30 | 31 | /** 32 | * 33 | * Render method 34 | * 35 | */ 36 | 37 | render() { 38 | return ( 39 | 44 |
{this.text}
45 |
46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/ezp-label/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-label 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ---------- | ---------- | -------------- | ---------------------------------------- | ------------- | 9 | | `ellipsis` | `ellipsis` | Description... | `boolean` | `false` | 10 | | `level` | `level` | Description... | `"primary" \| "secondary" \| "tertiary"` | `'secondary'` | 11 | | `noWrap` | `no-wrap` | Description... | `boolean` | `false` | 12 | | `text` | `text` | Description... | `string` | `'Label'` | 13 | | `weight` | `weight` | Description... | `"heavy" \| "soft" \| "strong"` | `'soft'` | 14 | 15 | ## Dependencies 16 | 17 | ### Used by 18 | 19 | - [ezp-dialog](../ezp-dialog) 20 | - [ezp-printer-selection](../ezp-printer-selection) 21 | - [ezp-select](../ezp-select) 22 | - [ezp-status](../ezp-status) 23 | - [ezp-stepper](../ezp-stepper) 24 | - [ezp-text-button](../ezp-text-button) 25 | - [ezp-upload](../ezp-upload) 26 | - [ezp-user-menu](../ezp-user-menu) 27 | - [ezp-input](../ezp-input) 28 | 29 | ### Graph 30 | 31 | ```mermaid 32 | graph TD; 33 | ezp-dialog --> ezp-label 34 | ezp-printer-selection --> ezp-label 35 | ezp-select --> ezp-label 36 | ezp-status --> ezp-label 37 | ezp-stepper --> ezp-label 38 | ezp-text-button --> ezp-label 39 | ezp-upload --> ezp-label 40 | ezp-user-menu --> ezp-label 41 | ezp-input --> ezp-label 42 | style ezp-label fill:#f9f,stroke:#333,stroke-width:4px 43 | ``` 44 | 45 | --- 46 | -------------------------------------------------------------------------------- /src/components/ezp-printer-selection/ezp-printer-selection.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use './../../shared/global'; 8 | 9 | /** 10 | * 11 | * Rules 12 | * 13 | */ 14 | 15 | :host { 16 | align-content: var(--ezp-printerSelection-alignContent, center); 17 | background: var(--ezp-printerSelection-background, var(--ezp-core-backdrop)); 18 | display: grid; 19 | grid-template: auto / var(--ezp-printerSelection-width, 320px); 20 | height: var(--ezp-printerSelection-height, 100vh); 21 | justify-content: var(--ezp-printerSelection-justifyContent, center); 22 | left: 0; 23 | position: var(--ezp-printerSelection-position, fixed); 24 | top: 0; 25 | width: 100%; 26 | z-index: 1; 27 | 28 | @at-root { 29 | #{&}(.seamless) { 30 | --ezp-printerSelection-alignContent: stretch; 31 | --ezp-printerSelection-background: transparent; 32 | --ezp-printerSelection-body-background: transparent; 33 | --ezp-printerSelection-body-padding: 0; 34 | --ezp-printerSelection-box-background: transparent; 35 | --ezp-printerSelection-box-borderRadius: 0; 36 | --ezp-printerSelection-box-boxShadow: none; 37 | --ezp-printerSelection-footer-boxShadowPosition: 0 0 0 0; 38 | --ezp-printerSelection-footer-padding: var(--ezp-spacing-2) 0; 39 | --ezp-printerSelection-header-boxShadowPosition: 0 0 0 0; 40 | --ezp-printerSelection-header-padding: var(--ezp-spacing-4) 0; 41 | --ezp-printerSelection-justifyContent: stretch; 42 | --ezp-printerSelection-position: static; 43 | } 44 | } 45 | } 46 | 47 | #box { 48 | background: var(--ezp-printerSelection-box-background, var(--ezp-core-surface-tertiary)); 49 | border-radius: var(--ezp-printerSelection-box-borderRadius, 6px); 50 | box-shadow: var(--ezp-printerSelection-box-boxShadow, 0 0 0 1px var(--ezp-black-10)); 51 | padding: var(--ezp-printerSelection-padding, 0); 52 | position: relative; 53 | } 54 | 55 | #header { 56 | border-radius: 6px 6px 0 0; 57 | box-shadow: var(--ezp-printerSelection-header-boxShadowPosition, 0 1px 0 0) 58 | var(--ezp-core-outline); 59 | color: var(--ezp-core-foreground-primary); 60 | display: grid; 61 | grid-column-gap: var(--ezp-spacing-2); 62 | grid-template-columns: auto 1fr auto; 63 | padding: var( 64 | --ezp-printerSelection-header-padding, 65 | var(--ezp-spacing-4) var(--ezp-spacing-3) var(--ezp-spacing-4) var(--ezp-spacing-4) 66 | ); 67 | position: relative; 68 | z-index: 3; 69 | } 70 | 71 | #toggle-menu { 72 | margin: calc(var(--ezp-spacing-3) * -1) 0; 73 | position: relative; 74 | z-index: 2; 75 | } 76 | 77 | #body { 78 | background: var(--ezp-printerSelection-body-background, var(--ezp-core-surface-secondary)); 79 | box-shadow: var(--ezp-printerSelection-body-boxShadow, none); 80 | display: flex; 81 | flex-direction: column; 82 | gap: var(--ezp-spacing-2); 83 | padding: var(--ezp-printerSelection-body-padding, var(--ezp-spacing-2)); 84 | } 85 | 86 | #footer { 87 | border-radius: 0 0 6px 6px; 88 | box-shadow: var(--ezp-printerSelection-footer-boxShadowPosition, 0 -1px 0 0) 89 | var(--ezp-core-outline); 90 | display: grid; 91 | gap: var(--ezp-spacing-2); 92 | grid-template-areas: var(--ezp-printerSelection-footer-areas, 'cancel print'); 93 | grid-template-columns: var(--ezp-printerSelection-footer-columns, repeat(2, 1fr)); 94 | grid-template-rows: var(--ezp-printerSelection-footer-rows, auto); 95 | justify-content: center; 96 | justify-items: stretch; 97 | padding: var(--ezp-printerSelection-footer-padding, var(--ezp-spacing-2)); 98 | position: relative; 99 | z-index: 3; 100 | } 101 | 102 | #cancel { 103 | grid-area: cancel; 104 | } 105 | 106 | #print { 107 | grid-area: print; 108 | } 109 | -------------------------------------------------------------------------------- /src/components/ezp-printer-selection/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-print 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------- | ---------------- | ----------- | --------- | ----------- | 9 | | `clientID` | `client-i-d` | Properties | `string` | `undefined` | 10 | | `file` | -- | | `File` | `undefined` | 11 | | `fileid` | `fileid` | | `string` | `undefined` | 12 | | `filename` | `filename` | | `string` | `undefined` | 13 | | `filetype` | `filetype` | | `string` | `undefined` | 14 | | `fileurl` | `fileurl` | | `string` | `undefined` | 15 | | `hideheader` | `hideheader` | | `boolean` | `false` | 16 | | `hidemenu` | `hidemenu` | | `boolean` | `false` | 17 | | `redirectURI` | `redirect-u-r-i` | | `string` | `undefined` | 18 | | `seamless` | `seamless` | | `boolean` | `undefined` | 19 | 20 | ## Events 21 | 22 | | Event | Description | Type | 23 | | ------------- | -------------- | ------------------------- | 24 | | `printCancel` | Description... | `CustomEvent` | 25 | | `printSubmit` | Description... | `CustomEvent` | 26 | 27 | ## Dependencies 28 | 29 | ### Used by 30 | 31 | - [ezp-printing](../ezp-printing) 32 | 33 | ### Depends on 34 | 35 | - [ezp-status](../ezp-status) 36 | - [ezp-label](../ezp-label) 37 | - [ezp-icon-button](../ezp-icon-button) 38 | - [ezp-select](../ezp-select) 39 | - [ezp-stepper](../ezp-stepper) 40 | - [ezp-text-button](../ezp-text-button) 41 | - [ezp-user-menu](../ezp-user-menu) 42 | 43 | ### Graph 44 | 45 | ```mermaid 46 | graph TD; 47 | ezp-printer-selection --> ezp-status 48 | ezp-printer-selection --> ezp-label 49 | ezp-printer-selection --> ezp-icon-button 50 | ezp-printer-selection --> ezp-select 51 | ezp-printer-selection --> ezp-stepper 52 | ezp-printer-selection --> ezp-text-button 53 | ezp-printer-selection --> ezp-user-menu 54 | ezp-printer-selection --> ezp-input 55 | ezp-status --> ezp-icon 56 | ezp-status --> ezp-label 57 | ezp-status --> ezp-text-button 58 | ezp-text-button --> ezp-label 59 | ezp-icon-button --> ezp-icon 60 | ezp-select --> ezp-backdrop 61 | ezp-select --> ezp-icon 62 | ezp-select --> ezp-label 63 | ezp-stepper --> ezp-icon 64 | ezp-stepper --> ezp-label 65 | ezp-user-menu --> ezp-backdrop 66 | ezp-user-menu --> ezp-label 67 | ezp-user-menu --> ezp-icon-button 68 | ezp-user-menu --> ezp-icon 69 | ezp-input --> ezp-icon 70 | ezp-input --> ezp-label 71 | ezp-printing --> ezp-printer-selection 72 | style ezp-printer-selection fill:#f9f,stroke:#333,stroke-width:4px 73 | ``` 74 | 75 | --- 76 | -------------------------------------------------------------------------------- /src/components/ezp-printing/ezp-printing.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use 'sass:map'; 8 | @use '@cortado-holding/colors' with ($namespace: 'ezp', $selector: ':host'); 9 | @use '@cortado-holding/colors/accents' with ($useDeclaration: false); 10 | @use '@cortado-holding/colors/core' with ($useDisplayP3: false, $useDeclaration: false); 11 | @use '@cortado-holding/colors/black'; 12 | @use '@cortado-holding/colors/white'; 13 | @use './../../shared/global'; 14 | 15 | /** 16 | * 17 | * Rules 18 | * 19 | */ 20 | 21 | :host { 22 | font-family: 'Inter', sans-serif; 23 | font-feature-settings: 'calt', 'liga', 'ss03', 'zero', 'cv05', 'cv10'; 24 | font-kerning: normal; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | margin: 0; 28 | -webkit-tap-highlight-color: transparent; 29 | text-rendering: optimizeLegibility; 30 | -webkit-text-size-adjust: none; 31 | 32 | @include global.duration-declaration; 33 | @include global.spacing-declaration; 34 | @include global.easing-declaration; 35 | 36 | @at-root { 37 | #{&}(.light) { 38 | @include colors.colors(map.get(accents.$palette, 'light'), 'accent'); 39 | @include colors.colors(map.get(core.$palette, 'light'), 'core'); 40 | } 41 | 42 | #{&}(.dark) { 43 | --ezp-dialog-box-boxShadow: inset 0 0 0 1px var(--ezp-core-outline), 44 | 0 0 0 1px var(--ezp-black-60); 45 | --ezp-printerSelection-body-boxShadow: inset 1px 0 0 0 var(--ezp-core-outline), 46 | inset -1px 0 0 0 var(--ezp-core-outline); 47 | --ezp-printerSelection-box-boxShadow: 0 0 0 1px var(--ezp-black-60); 48 | --ezp-printerSelection-footer-boxShadowPosition: inset 0 0 0 1px; 49 | --ezp-printerSelection-header-boxShadowPosition: inset 0 0 0 1px; 50 | --ezp-select-wrap-boxShadowPositionBottom: inset 0 -1px 0 0; 51 | --ezp-select-wrap-boxShadowPositionLeft: inset 1px 0 0 0; 52 | --ezp-select-wrap-boxShadowPositionRight: inset -1px 0 0 0; 53 | --ezp-select-wrap-boxShadowPositionTop: inset 0 1px 0 0; 54 | --ezp-status-box-boxShadow: inset 0 0 0 1px var(--ezp-core-outline), 55 | 0 0 0 1px var(--ezp-black-60); 56 | --ezp-stepper-boxShadowPosition: inset 0 0 0 1px; 57 | --ezp-userMenu-boxShadow: inset 0 0 0 1px var(--ezp-core-outline), 58 | 0 0 0 1px var(--ezp-black-60); 59 | --ezp-userMenu-header-boxShadowPosition: inset 0 -1px 0 0; 60 | --ezp-userMenu-links-boxShadowPosition: inset 0 -1px 0 0; 61 | --ezp-userMenu-theme-boxShadowPosition: inset 0 -1px 0 0; 62 | --ezp-input-box-shadow-position-top: inset 0 1px 0 0; 63 | --ezp-input-box-shadow-position-right: inset -1px 0 0 0; 64 | --ezp-input-box-shadow-position-bottom: inset 0 -1px 0 0; 65 | --ezp-input-box-shadow-position-left: inset 1px 0 0 0; 66 | 67 | @include colors.colors(map.get(accents.$palette, 'dark'), 'accent'); 68 | @include colors.colors(map.get(core.$palette, 'dark'), 'core'); 69 | } 70 | 71 | #{&}(.seamless) { 72 | --ezp-status-border-radius: 0; 73 | --ezp-backdrop-border-radius: 0; 74 | } 75 | 76 | @each $theme in global.$themes { 77 | #{&}(.#{$theme}) { 78 | --ezp-theme-solid: var(--ezp-accent-#{$theme}-solid); 79 | --ezp-theme-translucent: var(--ezp-accent-#{$theme}-translucent); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/components/ezp-printing/ezp-printing.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Component, 3 | Host, 4 | State, 5 | Listen, 6 | Method, 7 | h, 8 | Prop, 9 | Watch, 10 | EventEmitter, 11 | Event, 12 | } from '@stencil/core' 13 | import authStore, { EzpAuthorizationService, sendCodeToParentWindow } from '../../services/auth' 14 | import printStore, { EzpPrintService } from '../../services/print' 15 | import userStore from '../../services/user' 16 | import config from '../../shared/config.json' 17 | import { 18 | ThemeTypes, 19 | AppearanceTypes, 20 | TriggerTypes, 21 | SystemAppearanceTypes, 22 | } from './../../shared/types' 23 | import i18next from 'i18next' 24 | import { initi18n } from '../../utils/utils' 25 | 26 | @Component({ 27 | tag: 'ezp-printing', 28 | styleUrl: 'ezp-printing.scss', 29 | shadow: true, 30 | }) 31 | export class EzpPrinting { 32 | auth: EzpAuthorizationService 33 | 34 | @Prop() clientid: string 35 | @Prop() redirecturi: string 36 | @Prop({ mutable: true }) filename: string = '' 37 | @Prop() fileurl: string 38 | @Prop() filetype: string 39 | @Prop() custom: boolean 40 | @Prop() hidelogin: boolean 41 | @Prop() hidemenu: boolean = false 42 | @Prop() hideheader: boolean = false 43 | @Prop() authapihosturl: string 44 | @Prop() printapihosturl: string 45 | @Prop() theme: ThemeTypes = 'cyan' 46 | @Prop() fileid: string 47 | @Prop() appearance: AppearanceTypes = 'system' 48 | @Prop() trigger: TriggerTypes 49 | @Prop() language: string = '' 50 | @Prop() code: string 51 | @Prop() filedata: string 52 | @Prop() seamless: boolean = false 53 | 54 | /** 55 | * 56 | * States 57 | * 58 | */ 59 | 60 | /** Description... */ 61 | @State() printOpen: boolean = false 62 | @State() authOpen: boolean = false 63 | @State() onlyGetSasUri: boolean = false 64 | @State() noDocumentOpen: boolean = false 65 | @State() systemAppearance: SystemAppearanceTypes 66 | @State() file: File 67 | 68 | /** Watchers */ 69 | 70 | @Watch('filedata') 71 | watchFileData(newValue: string, oldValue: string) { 72 | console.log(newValue) 73 | if (newValue !== oldValue && newValue.length > 0) { 74 | let array = new Uint8Array(newValue.length) 75 | for (let i = 0; i < newValue.length; i++) { 76 | array[i] = newValue.charCodeAt(i) 77 | } 78 | this.file = new File([array], this.filename) 79 | } 80 | } 81 | 82 | @Watch('filename') 83 | watchFilename(newValue: string, oldValue: string) { 84 | if (newValue !== oldValue && newValue.length > 0) { 85 | this.filename = newValue 86 | } 87 | } 88 | 89 | @Watch('fileurl') 90 | watchFileUrl(newValue: string, oldValue: string) { 91 | if (newValue !== oldValue && newValue.length > 0) { 92 | this.fileurl = newValue 93 | } 94 | } 95 | 96 | @Watch('language') 97 | watchLanguage(newValue: string, oldValue: string) { 98 | if (newValue !== oldValue && newValue.length > 0) { 99 | this.language = newValue 100 | } 101 | } 102 | 103 | /** 104 | * 105 | * Listeners 106 | * 107 | */ 108 | 109 | /** Description... */ 110 | @Listen('printCancel') 111 | listenPrintCancel() { 112 | this.printOpen = false 113 | this.printFinished.emit() 114 | } 115 | 116 | /** Description... */ 117 | @Listen('printSubmit') 118 | listenPrintSubmit() { 119 | this.printOpen = false 120 | this.printFinished.emit() 121 | this.checkAuth() 122 | } 123 | 124 | /** Description... */ 125 | @Listen('authCancel') 126 | listenAuthCancel() { 127 | this.authOpen = false 128 | this.checkAuth() 129 | } 130 | 131 | @Listen('authSuccess') 132 | listenAuthSuccess() { 133 | if (this.onlyGetSasUri) { 134 | this.printOpen = false 135 | this.onlyGetSasUri = false 136 | } else if (this.filename != '') { 137 | this.printOpen = true 138 | } else { 139 | this.noDocumentOpen = true 140 | } 141 | } 142 | 143 | @Listen('uploadFile') 144 | listenUploadFile(event: CustomEvent) { 145 | this.filename = event.detail[0].name 146 | this.file = event.detail[0] 147 | this.open() 148 | } 149 | 150 | @Listen('dialogClose') 151 | listenDialogClose(event: CustomEvent) { 152 | if (event.detail === 'no-document-selected') { 153 | this.noDocumentOpen = false 154 | } 155 | } 156 | 157 | @Listen('dialogAction') 158 | listenDialogAction(event: CustomEvent) { 159 | if (event.detail === 'no-document-selected') { 160 | this.noDocumentOpen = false 161 | } 162 | } 163 | 164 | @Listen('logout') 165 | listenLogout() { 166 | this.logOut(); 167 | } 168 | 169 | /** 170 | * Events 171 | */ 172 | 173 | @Event({ 174 | eventName: 'printFinished', 175 | composed: true, 176 | bubbles: true, 177 | }) 178 | printFinished: EventEmitter 179 | 180 | /** 181 | * 182 | * Public methods 183 | * 184 | */ 185 | 186 | @Method() 187 | async open() { 188 | if (authStore.state.isAuthorized) { 189 | if (this.filename) { 190 | this.printOpen = true 191 | } else { 192 | this.noDocumentOpen = true 193 | } 194 | } else { 195 | this.authOpen = true 196 | } 197 | } 198 | 199 | @Method() 200 | async logOut() { 201 | this.auth.revokeRefreshToken() 202 | localStorage.removeItem('properties') 203 | localStorage.removeItem('refreshToken') 204 | localStorage.removeItem('access_token') 205 | localStorage.removeItem('printer') 206 | localStorage.removeItem('isAuthorized') 207 | this.printOpen = false 208 | } 209 | 210 | 211 | @Method() 212 | async getSasUri(): Promise { 213 | this.onlyGetSasUri = true 214 | 215 | const printService = new EzpPrintService(this.redirecturi, this.clientid) 216 | 217 | let response = await printService.prepareFileUpload(authStore.state.accessToken).catch(() => { 218 | this.open() 219 | return null 220 | }) 221 | 222 | if (response) { 223 | const sasUri = response.sasUri 224 | return sasUri 225 | } 226 | } 227 | 228 | @Method() 229 | async getAuthUri(): Promise { 230 | this.auth.generateCodeVerifier() 231 | await this.auth.generateCodeChallenge(authStore.state.codeVerifier) 232 | this.auth.buildAuthURI() 233 | return authStore.state.authUri 234 | } 235 | 236 | @Method() 237 | async checkAuth(): Promise { 238 | const printService = new EzpPrintService(this.redirecturi, this.clientid) 239 | 240 | let accessToken = authStore.state.accessToken 241 | 242 | if (accessToken === '') { 243 | accessToken = localStorage.getItem('access_token') 244 | authStore.state.accessToken = accessToken 245 | } 246 | 247 | if (localStorage.getItem('isAuthorized')) { 248 | authStore.state.isAuthorized = localStorage.getItem('isAuthorized') === 'true' 249 | } 250 | 251 | await printService 252 | .getConfig(authStore.state.accessToken) 253 | .then((response) => { 254 | if (response.ok) { 255 | authStore.state.isAuthorized = true 256 | } 257 | if (!response.ok) { 258 | authStore.state.isAuthorized = false 259 | throw new Error('http status ' + response.status) 260 | } 261 | }) 262 | .catch(() => { 263 | authStore.state.isAuthorized = false 264 | }) 265 | 266 | localStorage.setItem('isAuthorized', authStore.state.isAuthorized.toString()) 267 | 268 | return authStore.state.isAuthorized 269 | } 270 | 271 | refreshTokensPeriodically(seconds: number) { 272 | const authService = new EzpAuthorizationService(this.redirecturi, this.clientid) 273 | setInterval(() => { 274 | authService.refreshTokens() 275 | }, seconds * 1000) 276 | } 277 | 278 | async componentWillLoad() { 279 | const systemAppearanceDark = window.matchMedia('(prefers-color-scheme: dark)') 280 | 281 | this.systemAppearance = systemAppearanceDark.matches ? 'dark' : 'light' 282 | 283 | systemAppearanceDark.addEventListener('change', (event) => { 284 | this.systemAppearance = event.matches ? 'dark' : 'light' 285 | }) 286 | 287 | authStore.state.redirectUri = this.redirecturi 288 | userStore.state.theme = this.theme 289 | userStore.state.appearance = this.appearance 290 | 291 | if (this.authapihosturl) { 292 | authStore.state.authApiHostUrl = this.authapihosturl 293 | } else { 294 | authStore.state.authApiHostUrl = config.authApiHostUrl 295 | } 296 | 297 | if (this.printapihosturl) { 298 | printStore.state.printApiHostUrl = this.printapihosturl 299 | } else { 300 | printStore.state.printApiHostUrl = config.printingApiHostUrl 301 | } 302 | 303 | this.auth = new EzpAuthorizationService(this.redirecturi, this.clientid) 304 | 305 | sendCodeToParentWindow() 306 | initi18n(this.language) 307 | this.checkAuth() 308 | } 309 | 310 | componentDidLoad() { 311 | this.refreshTokensPeriodically(1800) 312 | } 313 | 314 | /** 315 | * 316 | * Render method 317 | * 318 | */ 319 | 320 | render() { 321 | return ( 322 | 329 | {this.trigger === 'custom' ? ( 330 | 331 | ) : this.trigger === 'file' ? ( 332 | 333 | ) : ( 334 | this.trigger === 'button' && ( 335 | this.open()} 341 | > 342 | ) 343 | )} 344 | {this.authOpen ? ( 345 | 352 | ) : this.printOpen ? ( 353 | 365 | ) : this.noDocumentOpen ? ( 366 | 373 | ) : null} 374 | {/* */} 375 | 376 | ) 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /src/components/ezp-printing/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-root 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ----------------- | ----------------- | ----------- | ---------------------------------------------------------------------------------- | ----------- | 9 | | `appearance` | `appearance` | | `"dark" \| "light" \| "system"` | `'system'` | 10 | | `authapihosturl` | `authapihosturl` | | `string` | `undefined` | 11 | | `clientid` | `clientid` | | `string` | `undefined` | 12 | | `code` | `code` | | `string` | `undefined` | 13 | | `custom` | `custom` | | `boolean` | `undefined` | 14 | | `filedata` | `filedata` | | `string` | `undefined` | 15 | | `fileid` | `fileid` | | `string` | `undefined` | 16 | | `filename` | `filename` | | `string` | `''` | 17 | | `filetype` | `filetype` | | `string` | `undefined` | 18 | | `fileurl` | `fileurl` | | `string` | `undefined` | 19 | | `hideheader` | `hideheader` | | `boolean` | `false` | 20 | | `hidelogin` | `hidelogin` | | `boolean` | `undefined` | 21 | | `hidemenu` | `hidemenu` | | `boolean` | `false` | 22 | | `language` | `language` | | `string` | `''` | 23 | | `printapihosturl` | `printapihosturl` | | `string` | `undefined` | 24 | | `redirecturi` | `redirecturi` | | `string` | `undefined` | 25 | | `seamless` | `seamless` | | `boolean` | `false` | 26 | | `theme` | `theme` | | `"blue" \| "cyan" \| "green" \| "orange" \| "pink" \| "red" \| "teal" \| "violet"` | `'cyan'` | 27 | | `trigger` | `trigger` | | `"button" \| "custom" \| "file"` | `undefined` | 28 | 29 | ## Events 30 | 31 | | Event | Description | Type | 32 | | --------------- | ----------- | ------------------ | 33 | | `printFinished` | Events | `CustomEvent` | 34 | 35 | ## Methods 36 | 37 | ### `checkAuth() => Promise` 38 | 39 | #### Returns 40 | 41 | Type: `Promise` 42 | 43 | ### `getAuthUri() => Promise` 44 | 45 | #### Returns 46 | 47 | Type: `Promise` 48 | 49 | ### `getSasUri() => Promise` 50 | 51 | #### Returns 52 | 53 | Type: `Promise` 54 | 55 | ### `logOut() => Promise` 56 | 57 | #### Returns 58 | 59 | Type: `Promise` 60 | 61 | ### `logOutandRevokeToken() => Promise` 62 | 63 | #### Returns 64 | 65 | Type: `Promise` 66 | 67 | ### `open() => Promise` 68 | 69 | Public methods 70 | 71 | #### Returns 72 | 73 | Type: `Promise` 74 | 75 | ## Dependencies 76 | 77 | ### Depends on 78 | 79 | - [ezp-upload](../ezp-upload) 80 | - [ezp-icon-button](../ezp-icon-button) 81 | - [ezp-auth](../ezp-auth) 82 | - [ezp-printer-selection](../ezp-printer-selection) 83 | - [ezp-dialog](../ezp-dialog) 84 | 85 | ### Graph 86 | 87 | ```mermaid 88 | graph TD; 89 | ezp-printing --> ezp-upload 90 | ezp-printing --> ezp-icon-button 91 | ezp-printing --> ezp-auth 92 | ezp-printing --> ezp-printer-selection 93 | ezp-printing --> ezp-dialog 94 | ezp-upload --> ezp-icon 95 | ezp-upload --> ezp-label 96 | ezp-icon-button --> ezp-icon 97 | ezp-auth --> ezp-status 98 | ezp-auth --> ezp-text-button 99 | ezp-auth --> ezp-dialog 100 | ezp-status --> ezp-icon 101 | ezp-status --> ezp-label 102 | ezp-status --> ezp-text-button 103 | ezp-text-button --> ezp-label 104 | ezp-dialog --> ezp-icon-button 105 | ezp-dialog --> ezp-icon 106 | ezp-dialog --> ezp-label 107 | ezp-dialog --> ezp-text-button 108 | ezp-printer-selection --> ezp-status 109 | ezp-printer-selection --> ezp-label 110 | ezp-printer-selection --> ezp-icon-button 111 | ezp-printer-selection --> ezp-select 112 | ezp-printer-selection --> ezp-stepper 113 | ezp-printer-selection --> ezp-text-button 114 | ezp-printer-selection --> ezp-user-menu 115 | ezp-select --> ezp-backdrop 116 | ezp-select --> ezp-icon 117 | ezp-select --> ezp-label 118 | ezp-stepper --> ezp-icon 119 | ezp-stepper --> ezp-label 120 | ezp-user-menu --> ezp-backdrop 121 | ezp-user-menu --> ezp-label 122 | ezp-user-menu --> ezp-icon-button 123 | ezp-user-menu --> ezp-icon 124 | style ezp-printing fill:#f9f,stroke:#333,stroke-width:4px 125 | ``` 126 | 127 | --- 128 | -------------------------------------------------------------------------------- /src/components/ezp-select/ezp-select.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | --ezp-select-duration: var(--ezp-duration-3); 9 | 10 | display: block; 11 | height: var(--ezp-select-toggle-height); 12 | position: relative; 13 | transition: var(--ezp-select-transition, z-index 0s var(--ezp-select-duration)); 14 | z-index: var(--ezp-select-zIndex, 3); 15 | 16 | @at-root { 17 | #{&}(:first-child) { 18 | --ezp-select-wrap-borderRadius: 3px 3px 0 0; 19 | } 20 | 21 | #{&}(:last-child) { 22 | --ezp-select-wrap-borderRadius: 0 0 3px 3px; 23 | } 24 | 25 | #{&}(:first-child:last-child) { 26 | --ezp-select-wrap-borderRadius: 3px; 27 | } 28 | 29 | #{&}(:not(:last-child):not(.is-expanded)) { 30 | --ezp-select-wrap-boxShadowPositionBottom: 0 0 0 0; 31 | } 32 | 33 | #{&}(.toggle-horizontal) { 34 | --ezp-select-label-alignSelf: start; 35 | --ezp-select-toggle-gridTemplateAreas: 'label value accessory'; 36 | --ezp-select-toggle-gridTemplateColumns: 1fr repeat(2, auto); 37 | --ezp-select-toggle-height: 30px; 38 | --ezp-select-toggle-gridTemplateRows: auto; 39 | --ezp-select-value-alignSelf: end; 40 | } 41 | 42 | #{&}(.toggle-horizontal.has-icon) { 43 | --ezp-select-toggle-gridTemplateAreas: 'icon label value accessory'; 44 | --ezp-select-toggle-gridTemplateColumns: auto 1fr repeat(2, auto); 45 | } 46 | 47 | #{&}(.toggle-vertical) { 48 | --ezp-select-label-alignSelf: center; 49 | --ezp-select-toggle-gridTemplateAreas: 'label accessory' 'value accessory'; 50 | --ezp-select-toggle-gridTemplateColumns: 1fr auto; 51 | --ezp-select-toggle-height: 48px; 52 | --ezp-select-toggle-gridTemplateRows: repeat(2, auto); 53 | --ezp-select-value-alignSelf: center; 54 | } 55 | 56 | #{&}(.toggle-vertical.has-icon) { 57 | --ezp-select-toggle-gridTemplateAreas: 'icon label accessory' 'icon value accessory'; 58 | --ezp-select-toggle-gridTemplateColumns: auto 1fr auto; 59 | } 60 | 61 | #{&}(.option-horizontal) { 62 | --ezp-select-details-gridTemplateColumns: repeat(2, 1fr); 63 | --ezp-select-details-gridTemplateRows: auto; 64 | --ezp-select-option-height: 30px; 65 | } 66 | 67 | #{&}(.option-vertical) { 68 | --ezp-select-details-gridTemplateColumns: 1fr; 69 | --ezp-select-details-gridTemplateRows: repeat(2, auto); 70 | --ezp-select-option-height: 48px; 71 | } 72 | 73 | #{&}(.is-expanded) { 74 | --ezp-select-accessory-color: var(--ezp-theme-solid); 75 | --ezp-select-transition: z-index 0s 0s; 76 | --ezp-select-zIndex: 9999; 77 | --ezp-select-icon-color: var(--ezp-theme-solid); 78 | --ezp-select-label-color: var(--ezp-theme-solid); 79 | --ezp-select-wrap-borderRadius: 3px; 80 | } 81 | 82 | #{&}(.disabled) { 83 | --ezp-select-accessory-color: var(--ezp-core-foreground-tertiary); 84 | --ezp-select-icon-color: var(--ezp-core-foreground-tertiary); 85 | --ezp-select-label-color: var(--ezp-core-foreground-tertiary); 86 | --ezp-select-toggle-background-hover: transparent; 87 | --ezp-select-toggle-cursor: default; 88 | --value-color: var(--ezp-core-foreground-tertiary); 89 | } 90 | } 91 | } 92 | 93 | #wrap { 94 | background: var(--ezp-core-surface-tertiary); 95 | border-radius: var(--ezp-select-wrap-borderRadius, 0); 96 | box-shadow: var(--ezp-select-wrap-boxShadowPositionTop, 0 -1px 0 0) var(--ezp-core-outline), 97 | var(--ezp-select-wrap-boxShadowPositionRight, 1px 0 0 0) var(--ezp-core-outline), 98 | var(--ezp-select-wrap-boxShadowPositionBottom, 0 1px 0 0) var(--ezp-core-outline), 99 | var(--ezp-select-wrap-boxShadowPositionLeft, -1px 0 0 0) var(--ezp-core-outline); 100 | transform: translateY(var(--ezp-select-wrap-translateY, 0)); 101 | transition: transform var(--ezp-select-duration) var(--ezp-easing-out-quart), 102 | border-radius var(--ezp-select-duration) var(--ezp-easing-out-quart), 103 | box-shadow var(--ezp-select-duration) var(--ezp-easing-out-quart); 104 | } 105 | 106 | #toggle { 107 | background: var(--ezp-select-toggle-background, transparent); 108 | box-sizing: border-box; 109 | cursor: var(--ezp-select-toggle-cursor, pointer); 110 | display: grid; 111 | gap: var(--ezp-spacing-2) var(--ezp-spacing-3); 112 | grid-template-areas: var(--ezp-select-toggle-gridTemplateAreas, none); 113 | grid-template-columns: var(--ezp-select-toggle-gridTemplateColumns, none); 114 | grid-template-rows: var(--ezp-select-toggle-gridTemplateRows, auto); 115 | height: var(--ezp-select-toggle-height, auto); 116 | padding: var(--ezp-spacing-3); 117 | 118 | &:hover { 119 | --ezp-select-toggle-background: var( 120 | --ezp-select-toggle-background-hover, 121 | var(--ezp-core-shade-primary) 122 | ); 123 | } 124 | } 125 | 126 | #icon { 127 | align-self: center; 128 | color: var(--ezp-select-icon-color, var(--ezp-core-foreground-secondary)); 129 | grid-area: icon; 130 | margin: calc(var(--ezp-spacing-2) * -1) 0; 131 | } 132 | 133 | #label { 134 | align-self: var(--ezp-select-label-alignSelf, auto); 135 | color: var(--ezp-select-label-color, var(--ezp-core-foreground-secondary)); 136 | grid-area: label; 137 | } 138 | 139 | #value { 140 | align-self: var(--ezp-select-value-alignSelf, auto); 141 | color: var(--value-color, var(--ezp-core-foreground-primary)); 142 | grid-area: value; 143 | } 144 | 145 | #accessory { 146 | align-self: center; 147 | color: var(--ezp-select-accessory-color, var(--ezp-core-foreground-secondary)); 148 | grid-area: accessory; 149 | margin: calc(var(--ezp-spacing-2) * -1) 0; 150 | } 151 | 152 | #list { 153 | box-shadow: inset 0 1px 0 0 var(--ezp-core-outline); 154 | height: var(--ezp-select-list-height, 0); 155 | overflow: auto; 156 | transition: height var(--ezp-select-duration) var(--ezp-easing-out-quart); 157 | } 158 | 159 | .option { 160 | background: var(--ezp-select-option-background); 161 | box-sizing: border-box; 162 | cursor: pointer; 163 | display: grid; 164 | gap: var(--ezp-spacing-3); 165 | grid-template: auto / auto 1fr; 166 | height: var(--ezp-select-option-height, auto); 167 | padding: 0 0 0 var(--ezp-spacing-3); 168 | 169 | &:not(:last-child) { 170 | --ezp-select-details-boxShadow: inset 0 -1px 0 0 var(--ezp-core-outline); 171 | } 172 | 173 | &:hover { 174 | --ezp-select-option-background: var(--ezp-core-shade-primary); 175 | } 176 | 177 | &:active { 178 | --ezp-select-option-background: var(--ezp-core-shade-secondary); 179 | } 180 | 181 | &.is-selected { 182 | --ezp-select-indicator-opacity: 1; 183 | --ezp-select-indicator-transform: translateX(0); 184 | --ezp-select-indicator-transition: opacity var(--ezp-select-duration) 185 | var(--ezp-easing-out-quart), 186 | transform var(--ezp-select-duration) var(--ezp-easing-out-quart), visibility 0s 0s; 187 | --ezp-select-indicator-visibility: visible; 188 | } 189 | } 190 | 191 | .indicator { 192 | align-self: center; 193 | color: var(--ezp-theme-solid); 194 | margin: calc(var(--ezp-spacing-2) * -1) 0; 195 | opacity: var(--ezp-select-indicator-opacity, 0); 196 | transform: var(--ezp-select-indicator-transform, translateX(var(--ezp-spacing-3))); 197 | transition: var( 198 | --ezp-select-indicator-transition, 199 | opacity var(--ezp-select-duration) var(--ezp-easing-out-quart), 200 | transform var(--ezp-select-duration) var(--ezp-easing-out-quart), 201 | visibility 0s var(--ezp-select-duration) 202 | ); 203 | visibility: var(--ezp-select-indicator-visibility, hidden); 204 | } 205 | 206 | .details { 207 | box-shadow: var(--ezp-select-details-boxShadow); 208 | display: grid; 209 | gap: var(--ezp-spacing-2); 210 | grid-template-columns: var(--ezp-select-details-gridTemplateColumns, 1fr); 211 | grid-template-rows: var(--ezp-select-details-gridTemplateRows, auto); 212 | padding: var(--ezp-spacing-3) var(--ezp-spacing-3) var(--ezp-spacing-3) 0; 213 | } 214 | 215 | .title { 216 | align-self: start; 217 | color: var(--ezp-core-foreground-primary); 218 | } 219 | 220 | .meta { 221 | align-self: end; 222 | color: var(--ezp-core-foreground-secondary); 223 | } 224 | -------------------------------------------------------------------------------- /src/components/ezp-select/ezp-select.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, State, Watch, Element, Event, EventEmitter, h } from '@stencil/core' 2 | import { SelectFlowTypes, SelectOptionType, IconNameTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-select', 6 | styleUrl: 'ezp-select.scss', 7 | shadow: true, 8 | }) 9 | export class EzpSelect { 10 | @Element() component!: HTMLEzpSelectElement 11 | 12 | private container: HTMLDivElement 13 | private backdrop: HTMLEzpBackdropElement = document.createElement('ezp-backdrop') 14 | private containerHeight: number = 0 15 | private expandCover: boolean = false 16 | private expandRise: boolean = false 17 | private list: HTMLDivElement 18 | private listHeight: number = 0 19 | private spacing: number = 6 20 | private toggleHeight: number = 0 21 | private wrapDiff: number = 0 22 | private wrapHeight: number = 0 23 | private wrapTop: number = 0 24 | private duration: number = 0 25 | 26 | /** 27 | * 28 | * Properties 29 | * 30 | */ 31 | 32 | /** Description... */ 33 | @Prop() icon: IconNameTypes 34 | 35 | /** Description... */ 36 | @Prop() label: string = 'Label' 37 | 38 | /** Description... */ 39 | @Prop() optionFlow: SelectFlowTypes 40 | 41 | /** Description... */ 42 | @Prop() options: SelectOptionType[] 43 | 44 | /** Description... */ 45 | @Prop() placeholder: string = 'Placeholder' 46 | 47 | /** Description... */ 48 | @Prop() preSelected: any 49 | 50 | /** Description... */ 51 | @Prop() toggleFlow: SelectFlowTypes = 'horizontal' 52 | 53 | /** Description... */ 54 | @Prop() disabled: boolean = false 55 | 56 | /** 57 | * 58 | * States 59 | * 60 | */ 61 | 62 | /** Description... */ 63 | @State() expanded: boolean = false 64 | 65 | /** Description... */ 66 | @State() selected: SelectOptionType = { id: false, title: '', meta: '' } 67 | 68 | /** 69 | * 70 | * Events 71 | * 72 | */ 73 | 74 | @Event() selectToggle: EventEmitter 75 | @Event() selectSelection: EventEmitter 76 | 77 | /** 78 | * 79 | * Watchers 80 | * 81 | */ 82 | 83 | @Watch('expanded') 84 | watchExpanded() { 85 | if (this.expandCover) { 86 | this.component.style.setProperty( 87 | '--ezp-select-list-height', 88 | this.expanded ? `${this.containerHeight - this.toggleHeight}px` : '0px' 89 | ) 90 | this.component.style.setProperty( 91 | '--ezp-select-wrap-translateY', 92 | this.expanded ? `${this.wrapTop * -1 + this.spacing}px` : '0px' 93 | ) 94 | } else if (this.expandRise) { 95 | this.component.style.setProperty( 96 | '--ezp-select-list-height', 97 | this.expanded ? `${this.listHeight}px` : '0px' 98 | ) 99 | this.component.style.setProperty( 100 | '--ezp-select-wrap-translateY', 101 | this.expanded ? `${this.wrapDiff + this.spacing}px` : '0px' 102 | ) 103 | } else { 104 | this.component.style.setProperty( 105 | '--ezp-select-list-height', 106 | this.expanded ? `${this.listHeight}px` : '0px' 107 | ) 108 | } 109 | 110 | if (this.expanded) { 111 | this.backdrop.visible = true 112 | this.container.appendChild(this.backdrop) 113 | } else { 114 | this.backdrop.visible = false 115 | } 116 | } 117 | 118 | @Watch('preSelected') 119 | preSelectedChanged() { 120 | this.preSelect(); 121 | } 122 | 123 | /** 124 | * 125 | * Private methods 126 | * 127 | */ 128 | 129 | private toggle = () => { 130 | this.containerHeight = this.container.clientHeight - this.spacing * 2 131 | this.listHeight = this.list.scrollHeight 132 | this.wrapTop = this.component.offsetTop 133 | this.wrapHeight = this.toggleHeight + this.listHeight 134 | this.expandCover = this.wrapHeight > this.containerHeight 135 | this.expandRise = this.wrapHeight > this.containerHeight - this.wrapTop 136 | this.wrapDiff = this.containerHeight - this.wrapHeight - this.wrapTop 137 | this.expanded = !this.expanded 138 | this.selectToggle.emit(this.expanded) 139 | } 140 | 141 | private select = (id: number | string | boolean) => { 142 | const delay = this.selected?.id === id ? 0 : this.duration * 1000 143 | 144 | this.selected = this.options.find((option) => option.id === id) 145 | this.selectSelection.emit(this.selected) 146 | 147 | window.setTimeout(() => { 148 | this.toggle() 149 | }, delay) 150 | } 151 | 152 | private preSelect = () => { 153 | this.selected = this.options?.find((option) => 154 | typeof this.preSelected === 'number' 155 | ? option.id === this.preSelected 156 | : typeof this.preSelected === 'string' 157 | ? option.title === this.preSelected 158 | : null 159 | ) 160 | } 161 | 162 | /** 163 | * 164 | * Lifecycle methods 165 | * 166 | */ 167 | 168 | componentWillLoad() { 169 | this.container = this.component.closest('[data-backdrop-surface]') 170 | 171 | this.backdrop.addEventListener('backdropHideStart', () => { 172 | this.expanded = false 173 | }) 174 | 175 | this.backdrop.addEventListener('backdropHideEnd', () => { 176 | this.container.removeChild(this.backdrop) 177 | }) 178 | 179 | if (this.preSelected !== undefined && this.preSelected !== '' && this.preSelected !== null) { 180 | this.preSelect() 181 | } 182 | } 183 | 184 | componentDidLoad() { 185 | const styles = getComputedStyle(this.component) 186 | 187 | this.toggleHeight = parseInt(styles.getPropertyValue('--ezp-select-toggle-height')) 188 | this.duration = parseFloat(styles.getPropertyValue('--ezp-select-duration')) 189 | } 190 | 191 | componentWillUpdate() { 192 | if ( 193 | this.selected?.id === false && 194 | this.preSelected !== undefined && 195 | this.preSelected !== '' && 196 | this.preSelected !== null 197 | ) { 198 | this.preSelect() 199 | } 200 | } 201 | 202 | /** 203 | * 204 | * Render method 205 | * 206 | */ 207 | 208 | render() { 209 | const hostClasses = [ 210 | this.expanded ? 'is-expanded' : '', 211 | this.icon ? 'has-icon' : '', 212 | `toggle-${this.toggleFlow}`, 213 | this.optionFlow ? `option-${this.optionFlow}` : '', 214 | this.disabled ? 'disabled' : '', 215 | ] 216 | const labelLevel = this.toggleFlow === 'horizontal' ? 'secondary' : 'tertiary' 217 | 218 | return ( 219 | 220 |
221 |
!this.disabled && this.toggle()}> 222 | {this.icon ? : null} 223 | 224 | 229 | 230 |
231 |
(this.list = element)}> 232 | {this.options?.map((option) => { 233 | if (option.title !== '') { 234 | return ( 235 |
this.select(option.id)} 240 | > 241 | 242 |
243 | 244 | {option.meta !== '' ? ( 245 | 246 | ) : null} 247 |
248 |
249 | ) 250 | } 251 | })} 252 |
253 |
254 |
255 | ) 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/components/ezp-select/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-select 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------- | -------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | 9 | | `disabled` | `disabled` | Description... | `boolean` | `false` | 10 | | `icon` | `icon` | Description... | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 11 | | `label` | `label` | Description... | `string` | `'Label'` | 12 | | `optionFlow` | `option-flow` | Description... | `"horizontal" \| "vertical"` | `undefined` | 13 | | `options` | -- | Description... | `SelectOptionType[]` | `undefined` | 14 | | `placeholder` | `placeholder` | Description... | `string` | `'Placeholder'` | 15 | | `preSelected` | `pre-selected` | Description... | `any` | `undefined` | 16 | | `toggleFlow` | `toggle-flow` | Description... | `"horizontal" \| "vertical"` | `'horizontal'` | 17 | 18 | ## Events 19 | 20 | | Event | Description | Type | 21 | | ----------------- | ----------- | ------------------ | 22 | | `selectSelection` | | `CustomEvent` | 23 | | `selectToggle` | Events | `CustomEvent` | 24 | 25 | ## Dependencies 26 | 27 | ### Used by 28 | 29 | - [ezp-printer-selection](../ezp-printer-selection) 30 | 31 | ### Depends on 32 | 33 | - [ezp-backdrop](../ezp-backdrop) 34 | - [ezp-icon](../ezp-icon) 35 | - [ezp-label](../ezp-label) 36 | 37 | ### Graph 38 | 39 | ```mermaid 40 | graph TD; 41 | ezp-select --> ezp-backdrop 42 | ezp-select --> ezp-icon 43 | ezp-select --> ezp-label 44 | ezp-printer-selection --> ezp-select 45 | style ezp-select fill:#f9f,stroke:#333,stroke-width:4px 46 | ``` 47 | 48 | --- 49 | -------------------------------------------------------------------------------- /src/components/ezp-status/ezp-status.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use './../../shared/global'; 8 | 9 | /** 10 | * 11 | * Rules 12 | * 13 | */ 14 | 15 | :host { 16 | align-items: center; 17 | background: var(--ezp-core-backdrop); 18 | border-radius: var(--ezp-status-border-radius, 6px); 19 | box-sizing: border-box; 20 | display: flex; 21 | height: 100%; 22 | justify-content: center; 23 | left: 0; 24 | padding: var(--ezp-spacing-6); 25 | position: absolute; 26 | top: 0; 27 | width: 100%; 28 | z-index: 9999; 29 | 30 | @include global.media-appearance('dark') { 31 | --box-separator-position: inset 0 0 0 var(--hairline-width-positive); 32 | } 33 | } 34 | 35 | #box { 36 | --ezp-icon-fill: var(--ezp-theme-solid); 37 | 38 | align-items: center; 39 | background: var(--ezp-core-surface-tertiary); 40 | border-radius: 3px; 41 | box-shadow: var(--ezp-status-box-boxShadow, 0 0 0 1px var(--ezp-black-10)); 42 | display: flex; 43 | flex-direction: column; 44 | gap: var(--ezp-spacing-4); 45 | padding: var(--ezp-spacing-4) var(--ezp-spacing-5); 46 | } 47 | 48 | #indicator { 49 | animation: rotate var(--ezp-duration-6) linear infinite; 50 | display: block; 51 | height: 42px; 52 | transform: rotate(0); 53 | width: 42px; 54 | } 55 | 56 | #track { 57 | fill: none; 58 | stroke: var(--ezp-core-outline); 59 | stroke-width: 4px; 60 | } 61 | 62 | #value { 63 | animation: offset var(--ezp-duration-6) linear alternate infinite; 64 | fill: none; 65 | stroke: var(--ezp-theme-solid); 66 | stroke-dasharray: 110; 67 | stroke-dashoffset: 110; 68 | stroke-linecap: round; 69 | stroke-width: 4px; 70 | } 71 | 72 | #description { 73 | color: var(--ezp-core-foreground-primary); 74 | } 75 | 76 | #footer { 77 | display: flex; 78 | gap: var(--ezp-spacing-2); 79 | } 80 | 81 | /** 82 | * 83 | * Keyframes 84 | * 85 | */ 86 | 87 | @keyframes offset { 88 | 100% { 89 | stroke-dashoffset: 55; 90 | } 91 | } 92 | 93 | @keyframes rotate { 94 | 100% { 95 | transform: rotate(360deg); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/components/ezp-status/ezp-status.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, Event, EventEmitter, h } from '@stencil/core' 2 | import { IconNameTypes } from '../../shared/types' 3 | import i18next from 'i18next' 4 | 5 | @Component({ 6 | tag: 'ezp-status', 7 | styleUrl: 'ezp-status.scss', 8 | shadow: true, 9 | }) 10 | export class EzpStatus { 11 | /** 12 | * 13 | * Properties 14 | * 15 | */ 16 | 17 | @Prop() description: string = 'Description' 18 | @Prop() processing: boolean = false 19 | @Prop() instance: string 20 | @Prop() icon?: IconNameTypes 21 | @Prop() cancel?: string | boolean 22 | @Prop() close?: string | boolean 23 | @Prop() retry?: string | boolean 24 | 25 | /** 26 | * 27 | * Events 28 | * 29 | */ 30 | 31 | @Event() statusCancel: EventEmitter 32 | @Event() statusClose: EventEmitter 33 | @Event() statusRetry: EventEmitter 34 | 35 | /** 36 | * 37 | * Private methods 38 | * 39 | */ 40 | 41 | private handleCancel = () => { 42 | this.statusCancel.emit(this.instance) 43 | } 44 | 45 | private handleClose = () => { 46 | this.statusClose.emit(this.instance) 47 | } 48 | 49 | private handleRetry = () => { 50 | this.statusRetry.emit(this.instance) 51 | } 52 | 53 | /** 54 | * 55 | * Lifecycle methods 56 | * 57 | */ 58 | 59 | componentWillLoad() {} 60 | 61 | /** 62 | * 63 | * Render method 64 | * 65 | */ 66 | 67 | render() { 68 | return ( 69 | 70 |
71 | {this.processing ? ( 72 | 73 | 74 | 75 | 76 | ) : this.icon ? ( 77 | 78 | ) : null} 79 | 80 | {(this.cancel || this.close || this.retry) && ( 81 | 115 | )} 116 |
117 |
118 | ) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/components/ezp-status/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-status 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ------------- | ------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | 9 | | `cancel` | `cancel` | | `boolean \| string` | `undefined` | 10 | | `close` | `close` | | `boolean \| string` | `undefined` | 11 | | `description` | `description` | Properties | `string` | `'Description'` | 12 | | `icon` | `icon` | | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 13 | | `instance` | `instance` | | `string` | `undefined` | 14 | | `processing` | `processing` | | `boolean` | `false` | 15 | | `retry` | `retry` | | `boolean \| string` | `undefined` | 16 | 17 | ## Events 18 | 19 | | Event | Description | Type | 20 | | -------------- | ----------- | ------------------ | 21 | | `statusCancel` | Events | `CustomEvent` | 22 | | `statusClose` | | `CustomEvent` | 23 | | `statusRetry` | | `CustomEvent` | 24 | 25 | ## Dependencies 26 | 27 | ### Used by 28 | 29 | - [ezp-auth](../ezp-auth) 30 | - [ezp-printer-selection](../ezp-printer-selection) 31 | 32 | ### Depends on 33 | 34 | - [ezp-icon](../ezp-icon) 35 | - [ezp-label](../ezp-label) 36 | - [ezp-text-button](../ezp-text-button) 37 | 38 | ### Graph 39 | 40 | ```mermaid 41 | graph TD; 42 | ezp-status --> ezp-icon 43 | ezp-status --> ezp-label 44 | ezp-status --> ezp-text-button 45 | ezp-text-button --> ezp-label 46 | ezp-auth --> ezp-status 47 | ezp-printer-selection --> ezp-status 48 | style ezp-status fill:#f9f,stroke:#333,stroke-width:4px 49 | ``` 50 | 51 | --- 52 | -------------------------------------------------------------------------------- /src/components/ezp-stepper/ezp-stepper.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use './../../shared/global'; 8 | 9 | /** 10 | * 11 | * Rules 12 | * 13 | */ 14 | 15 | :host { 16 | align-items: center; 17 | background: var(--ezp-core-surface-tertiary); 18 | border-radius: 3px; 19 | box-shadow: var(--ezp-stepper-boxShadowPosition, 0 0 0 1px) var(--ezp-core-outline); 20 | display: grid; 21 | gap: var(--ezp-spacing-3); 22 | grid-template: var(--ezp-stepper-gridTemplate, auto / auto 1fr auto); 23 | padding: 0 var(--ezp-spacing-2) 0 var(--ezp-spacing-3); 24 | position: relative; 25 | z-index: 0; 26 | 27 | @at-root { 28 | #{&}(.focused) { 29 | --ezp-stepper-icon-color: var(--ezp-theme-solid); 30 | --ezp-stepper-label-color: var(--ezp-theme-solid); 31 | } 32 | 33 | #{&}(.has-icon) { 34 | --ezp-stepper-gridTemplate: auto / repeat(2, auto) 1fr auto; 35 | } 36 | } 37 | } 38 | 39 | #toggle { 40 | background: var(--ezp-stepper-toggle-background, transparent); 41 | border-radius: 3px; 42 | cursor: pointer; 43 | height: 100%; 44 | left: 0; 45 | position: absolute; 46 | top: 0; 47 | width: 100%; 48 | z-index: -1; 49 | 50 | @media (hover: hover) { 51 | &:hover { 52 | --ezp-stepper-toggle-background: var(--ezp-core-shade-primary); 53 | } 54 | } 55 | } 56 | 57 | #icon { 58 | color: var(--ezp-stepper-icon-color, var(--ezp-core-foreground-secondary)); 59 | pointer-events: none; 60 | } 61 | 62 | #label { 63 | color: var(--ezp-stepper-label-color, var(--ezp-core-foreground-secondary)); 64 | pointer-events: none; 65 | } 66 | 67 | #input { 68 | /** 69 | * 70 | * FIXME 71 | * 72 | * - adapt font properties 73 | * - hover / focus state 74 | * 75 | */ 76 | 77 | -moz-appearance: textfield; 78 | appearance: none; 79 | background: transparent; 80 | border: 0; 81 | border-radius: 3px; 82 | box-shadow: none; 83 | box-sizing: border-box; 84 | color: var(--ezp-core-foreground-primary); 85 | cursor: inherit; 86 | font-family: inherit; 87 | font-feature-settings: 'tnum'; 88 | font-size: 14px; 89 | -webkit-font-smoothing: antialiased; 90 | height: 30px; 91 | letter-spacing: #{global.getLetterSpacing(14px)}; 92 | margin: 0; 93 | outline: none; 94 | padding: 0; 95 | pointer-events: none; 96 | text-align: right; 97 | width: 100%; 98 | } 99 | 100 | #input::-webkit-outer-spin-button, 101 | #input::-webkit-inner-spin-button { 102 | -webkit-appearance: none; 103 | margin: 0; 104 | } 105 | 106 | .buttons { 107 | display: flex; 108 | } 109 | 110 | .button { 111 | align-items: center; 112 | appearance: none; 113 | background: var(--ezp-stepper-button-background, transparent); 114 | border: 0; 115 | border-radius: 50%; 116 | box-shadow: none; 117 | color: var(--ezp-stepper-button-color, var(--ezp-theme-solid)); 118 | cursor: var(--ezp-stepper-button-cursor, pointer); 119 | display: flex; 120 | justify-content: center; 121 | margin: 0; 122 | outline: none; 123 | padding: var(--ezp-spacing-1); 124 | 125 | &:not(:disabled) { 126 | @media (hover: hover) { 127 | &:hover { 128 | --ezp-stepper-button-background: var(--ezp-core-shade-primary); 129 | } 130 | 131 | &:active { 132 | --ezp-stepper-button-background: var(--ezp-core-shade-secondary); 133 | } 134 | } 135 | } 136 | 137 | &:disabled { 138 | --ezp-stepper-button-color: var(--ezp-core-foreground-tertiary); 139 | --ezp-stepper-button-cursor: default; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/components/ezp-stepper/ezp-stepper.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, State, Event, EventEmitter, Watch, h } from '@stencil/core' 2 | import { IconNameTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-stepper', 6 | styleUrl: 'ezp-stepper.scss', 7 | shadow: true, 8 | }) 9 | export class EzpStepper { 10 | private input?: HTMLInputElement 11 | 12 | /** 13 | * 14 | * Properties 15 | * 16 | */ 17 | 18 | /** Description... */ 19 | @Prop() icon: IconNameTypes 20 | 21 | /** Description... */ 22 | @Prop() label: string = 'Label' 23 | 24 | /** Description... */ 25 | @Prop() max: number 26 | 27 | /** Description... */ 28 | @Prop() min: number = 1 29 | 30 | /** 31 | * 32 | * States 33 | * 34 | */ 35 | 36 | /** Description... */ 37 | @State() canDecrease: boolean = true 38 | 39 | /** Description... */ 40 | @State() canIncrease: boolean = true 41 | 42 | /** Description... */ 43 | @State() focused: boolean = false 44 | 45 | /** Description... */ 46 | @State() value: number = 1 47 | 48 | /** 49 | * 50 | * Events 51 | * 52 | */ 53 | 54 | @Event() stepperChanged: EventEmitter 55 | 56 | /** 57 | * 58 | * Watchers 59 | * 60 | */ 61 | 62 | @Watch('value') 63 | watchValue() { 64 | this.canIncrease = this.max !== undefined ? this.value < this.max : true 65 | this.canDecrease = this.min !== undefined ? this.value > this.min : true 66 | this.stepperChanged.emit(this.value) 67 | } 68 | 69 | /** 70 | * 71 | * Private methods 72 | * 73 | */ 74 | 75 | private handleDecrease = () => { 76 | this.value-- 77 | } 78 | 79 | private handleIncrease = () => { 80 | this.value++ 81 | } 82 | 83 | private handleInput = () => { 84 | this.value = this.input.value !== '' ? parseInt(this.input.value) : this.min 85 | } 86 | 87 | private setFocus = () => { 88 | this.input.focus() 89 | } 90 | 91 | private handleBlur = () => { 92 | this.focused = false 93 | } 94 | 95 | private handleFocus = () => { 96 | this.focused = true 97 | } 98 | 99 | /** 100 | * 101 | * Lifecycle methods 102 | * 103 | */ 104 | 105 | componentWillLoad() { 106 | this.watchValue() 107 | } 108 | 109 | /** 110 | * 111 | * Render method 112 | * 113 | */ 114 | 115 | render() { 116 | return ( 117 | 118 |
119 | {this.icon ? : null} 120 | 121 | (this.input = input)} 125 | min={this.min.toString()} 126 | max={this.max.toString()} 127 | value={this.value.toString()} 128 | onInput={this.handleInput} 129 | onFocus={this.handleFocus} 130 | onBlur={this.handleBlur} 131 | /> 132 |
133 | 136 | 139 |
140 | 141 | ) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/components/ezp-stepper/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-stepper 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | -------- | --------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | 9 | | `icon` | `icon` | Description... | `"account" \| "checkmark" \| "close" \| "color" \| "copies" \| "dark" \| "duplex" \| "expand" \| "help" \| "light" \| "logout" \| "menu" \| "minus" \| "orientation" \| "plus" \| "printer" \| "quality" \| "size" \| "system" \| "drag-drop" \| "checkmark-alt" \| "question-mark" \| "exclamation-mark" \| "logo"` | `undefined` | 10 | | `label` | `label` | Description... | `string` | `'Label'` | 11 | | `max` | `max` | Description... | `number` | `undefined` | 12 | | `min` | `min` | Description... | `number` | `1` | 13 | 14 | ## Events 15 | 16 | | Event | Description | Type | 17 | | ---------------- | ----------- | ------------------ | 18 | | `stepperChanged` | Events | `CustomEvent` | 19 | 20 | ## Dependencies 21 | 22 | ### Used by 23 | 24 | - [ezp-printer-selection](../ezp-printer-selection) 25 | 26 | ### Depends on 27 | 28 | - [ezp-icon](../ezp-icon) 29 | - [ezp-label](../ezp-label) 30 | 31 | ### Graph 32 | 33 | ```mermaid 34 | graph TD; 35 | ezp-stepper --> ezp-icon 36 | ezp-stepper --> ezp-label 37 | ezp-printer-selection --> ezp-stepper 38 | style ezp-stepper fill:#f9f,stroke:#333,stroke-width:4px 39 | ``` 40 | 41 | --- 42 | -------------------------------------------------------------------------------- /src/components/ezp-text-button/ezp-text-button.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | display: flex; 9 | 10 | @at-root { 11 | #{&}(.primary) { 12 | --ezp-textButton-background-disabled: var(--ezp-core-shade-primary); 13 | --ezp-textButton-background-init: var(--ezp-theme-solid); 14 | --ezp-textButton-color-disabled: var(--ezp-core-foreground-tertiary); 15 | --ezp-textButton-color-init: var(--ezp-white-100); 16 | --ezp-textButton-opacity-active: 0.74; 17 | --ezp-textButton-opacity-hover: 0.84; 18 | } 19 | 20 | #{&}(.secondary) { 21 | --ezp-textButton-background-active: var(--ezp-core-shade-tertiary); 22 | --ezp-textButton-background-disabled: var(--ezp-core-shade-primary); 23 | --ezp-textButton-background-hover: var(--ezp-core-shade-secondary); 24 | --ezp-textButton-background-init: var(--ezp-core-shade-primary); 25 | --ezp-textButton-color-disabled: var(--ezp-core-foreground-tertiary); 26 | --ezp-textButton-color-init: var(--ezp-theme-solid); 27 | } 28 | 29 | #{&}(.tertiary) { 30 | --ezp-textButton-background-active: var(--ezp-core-shade-secondary); 31 | --ezp-textButton-background-disabled: transparent; 32 | --ezp-textButton-background-hover: var(--ezp-core-shade-primary); 33 | --ezp-textButton-background-init: transparent; 34 | --ezp-textButton-color-disabled: var(--ezp-core-foreground-tertiary); 35 | --ezp-textButton-color-init: var(--ezp-theme-solid); 36 | } 37 | } 38 | } 39 | 40 | #button { 41 | align-items: center; 42 | appearance: none; 43 | background: var(--ezp-textButton-background, var(--ezp-textButton-background-init)); 44 | border: 0; 45 | border-radius: 3px; 46 | box-shadow: none; 47 | color: var(--ezp-textButton-color, var(--ezp-textButton-color-init)); 48 | cursor: var(--ezp-textButton-cursor, pointer); 49 | display: flex; 50 | font-family: inherit; 51 | justify-content: center; 52 | margin: 0; 53 | opacity: var(--ezp-textButton-opacity, 1); 54 | outline: none; 55 | padding: var(--ezp-spacing-3); 56 | white-space: nowrap; 57 | width: 100%; 58 | 59 | &:not(:disabled) { 60 | @media (hover: hover) { 61 | &:hover { 62 | --ezp-textButton-background: var( 63 | --ezp-textButton-background-hover, 64 | var(--ezp-textButton-background-init) 65 | ); 66 | --ezp-textButton-opacity: var(--ezp-textButton-opacity-hover, 1); 67 | } 68 | 69 | &:active { 70 | --ezp-textButton-background: var( 71 | --ezp-textButton-background-active, 72 | var(--ezp-textButton-background-init) 73 | ); 74 | --ezp-textButton-opacity: var(--ezp-textButton-opacity-active, 1); 75 | } 76 | } 77 | } 78 | 79 | &:disabled { 80 | --ezp-textButton-background: var(--ezp-textButton-background-disabled); 81 | --ezp-textButton-color: var(--ezp-textButton-color-disabled); 82 | --ezp-textButton-cursor: default; 83 | } 84 | } 85 | 86 | #label { 87 | pointer-events: none; 88 | } 89 | -------------------------------------------------------------------------------- /src/components/ezp-text-button/ezp-text-button.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, h } from '@stencil/core' 2 | import { TextButtonLevelTypes, TextButtonTypeTypes } from '../../shared/types' 3 | 4 | @Component({ 5 | tag: 'ezp-text-button', 6 | styleUrl: 'ezp-text-button.scss', 7 | shadow: true, 8 | }) 9 | export class EzpTextButton { 10 | /** 11 | * 12 | * Properties 13 | * 14 | */ 15 | 16 | /** Description... */ 17 | @Prop() blank: boolean = false 18 | 19 | /** Description... */ 20 | @Prop() disabled: boolean = false 21 | 22 | /** Description... */ 23 | @Prop() href: string 24 | 25 | /** Description... */ 26 | @Prop() level: TextButtonLevelTypes = 'primary' 27 | 28 | /** Description... */ 29 | @Prop() label: string 30 | 31 | /** Description... */ 32 | @Prop() type: TextButtonTypeTypes 33 | 34 | /** Description... */ 35 | @Prop() small: boolean = false 36 | 37 | /** 38 | * 39 | * Render method 40 | * 41 | */ 42 | 43 | render() { 44 | const TagType = this.type !== undefined ? 'button' : 'a' 45 | const attributes = 46 | this.type !== undefined 47 | ? { 48 | type: this.type, 49 | disabled: this.disabled, 50 | } 51 | : { 52 | href: this.href, 53 | target: this.blank ? '_blank' : '_self', 54 | } 55 | 56 | return ( 57 | 58 | 59 | 65 | 66 | 67 | ) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/ezp-text-button/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-text-button 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | ---------- | ---------- | -------------- | ---------------------------------------- | ----------- | 9 | | `blank` | `blank` | Description... | `boolean` | `false` | 10 | | `disabled` | `disabled` | Description... | `boolean` | `false` | 11 | | `href` | `href` | Description... | `string` | `undefined` | 12 | | `label` | `label` | Description... | `string` | `undefined` | 13 | | `level` | `level` | Description... | `"primary" \| "secondary" \| "tertiary"` | `'primary'` | 14 | | `small` | `small` | Description... | `boolean` | `false` | 15 | | `type` | `type` | Description... | `"button"` | `undefined` | 16 | 17 | ## Dependencies 18 | 19 | ### Used by 20 | 21 | - [ezp-auth](../ezp-auth) 22 | - [ezp-dialog](../ezp-dialog) 23 | - [ezp-printer-selection](../ezp-printer-selection) 24 | - [ezp-status](../ezp-status) 25 | 26 | ### Depends on 27 | 28 | - [ezp-label](../ezp-label) 29 | 30 | ### Graph 31 | 32 | ```mermaid 33 | graph TD; 34 | ezp-text-button --> ezp-label 35 | ezp-auth --> ezp-text-button 36 | ezp-dialog --> ezp-text-button 37 | ezp-printer-selection --> ezp-text-button 38 | ezp-status --> ezp-text-button 39 | style ezp-text-button fill:#f9f,stroke:#333,stroke-width:4px 40 | ``` 41 | 42 | --- 43 | -------------------------------------------------------------------------------- /src/components/ezp-upload/ezp-upload.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Rules 4 | * 5 | */ 6 | 7 | :host { 8 | align-content: center; 9 | background: var(--ezp-upload-background, transparent); 10 | border: 1px dashed var(--ezp-upload-border-color, var(--ezp-core-foreground-tertiary)); 11 | border-radius: 3px; 12 | box-sizing: border-box; 13 | color: var(--ezp-core-foreground-primary); 14 | display: grid; 15 | grid-template: auto / minmax(auto, 280px); 16 | height: var(--ezp-upload-height, auto); 17 | justify-content: center; 18 | padding: var(--ezp-spacing-3); 19 | position: relative; 20 | transition: background-color var(--ezp-duration-3) var(--ezp-easing-out-quart), 21 | border-color var(--ezp-duration-3) var(--ezp-easing-out-quart); 22 | width: var(--ezp-upload-width, auto); 23 | 24 | @at-root { 25 | #{&}(.dragging) { 26 | --ezp-upload-background: var(--ezp-theme-translucent); 27 | --ezp-upload-border-color: var(--ezp-theme-translucent); 28 | --ezp-upload-header-blur: 16px; 29 | --ezp-upload-header-opacity: 0.42; 30 | } 31 | } 32 | } 33 | 34 | #header { 35 | --ezp-icon-fill: var(--ezp-theme-solid); 36 | 37 | align-items: center; 38 | display: flex; 39 | filter: blur(var(--ezp-upload-header-blur, 0)); 40 | flex-direction: column; 41 | gap: var(--ezp-spacing-5); 42 | opacity: var(--ezp-upload-header-opacity, 1); 43 | text-align: center; 44 | transition: filter var(--ezp-duration-3) var(--ezp-easing-out-quart), 45 | opacity var(--ezp-duration-3) var(--ezp-easing-out-quart); 46 | } 47 | 48 | #meta { 49 | display: flex; 50 | flex-wrap: wrap; 51 | gap: var(--ezp-spacing-3) 3px; 52 | justify-content: center; 53 | } 54 | 55 | #select { 56 | color: var(--ezp-theme-solid); 57 | cursor: pointer; 58 | opacity: var(--ezp-upload-select-opacity, 1); 59 | text-decoration: underline; 60 | white-space: nowrap; 61 | 62 | @media (hover: hover) { 63 | &:hover { 64 | --ezp-upload-select-opacity: 0.58; 65 | } 66 | } 67 | } 68 | 69 | #input { 70 | appearance: none; 71 | height: 0; 72 | opacity: 0; 73 | pointer-events: none; 74 | position: absolute; 75 | width: 0; 76 | } 77 | -------------------------------------------------------------------------------- /src/components/ezp-upload/ezp-upload.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Listen, Event, EventEmitter, State, h, Fragment } from '@stencil/core' 2 | import i18next from 'i18next' 3 | @Component({ 4 | tag: 'ezp-upload', 5 | styleUrl: 'ezp-upload.scss', 6 | shadow: true, 7 | }) 8 | export class EzpUpload { 9 | private input?: HTMLInputElement 10 | private form?: HTMLFormElement 11 | 12 | /** 13 | * 14 | * States 15 | * 16 | */ 17 | 18 | @State() filename: string = '' 19 | @State() dragging: boolean = false 20 | 21 | /** 22 | * 23 | * Events 24 | * 25 | */ 26 | 27 | @Event() uploadFile: EventEmitter 28 | 29 | /** 30 | * 31 | * Listeners 32 | * 33 | */ 34 | 35 | @Listen('dragenter') 36 | handleDragEnter() { 37 | this.dragging = true 38 | } 39 | 40 | @Listen('dragover', { passive: false }) 41 | handleDragOver(event: DragEvent) { 42 | event.stopPropagation() 43 | event.preventDefault() 44 | event.dataTransfer.dropEffect = 'copy' 45 | } 46 | 47 | @Listen('dragleave') 48 | handleDragLeave() { 49 | this.dragging = false 50 | } 51 | 52 | @Listen('drop', { passive: false }) 53 | handleDrop(event: DragEvent) { 54 | event.stopPropagation() 55 | event.preventDefault() 56 | 57 | this.dragging = false 58 | this.filename = event.dataTransfer.files[0].name 59 | this.uploadFile.emit(event.dataTransfer.files) 60 | } 61 | 62 | @Listen('printCancel', { target: 'document' }) 63 | listenPrintCancel() { 64 | this.form.reset() 65 | this.filename = '' 66 | } 67 | 68 | /** 69 | * 70 | * Private methods 71 | * 72 | */ 73 | 74 | private handleInput = () => { 75 | this.filename = this.input.files[0].name 76 | this.uploadFile.emit(this.input.files) 77 | } 78 | 79 | /** 80 | * 81 | * Render method 82 | * 83 | */ 84 | 85 | render() { 86 | return ( 87 | 88 |
(this.form = form)}> 89 | 117 |
118 |
119 | ) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/components/ezp-upload/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-upload 2 | 3 | 4 | 5 | ## Events 6 | 7 | | Event | Description | Type | 8 | | ------------ | ----------- | ------------------ | 9 | | `uploadFile` | Events | `CustomEvent` | 10 | 11 | ## Dependencies 12 | 13 | ### Used by 14 | 15 | - [ezp-printing](../ezp-printing) 16 | 17 | ### Depends on 18 | 19 | - [ezp-icon](../ezp-icon) 20 | - [ezp-label](../ezp-label) 21 | 22 | ### Graph 23 | 24 | ```mermaid 25 | graph TD; 26 | ezp-upload --> ezp-icon 27 | ezp-upload --> ezp-label 28 | ezp-printing --> ezp-upload 29 | style ezp-upload fill:#f9f,stroke:#333,stroke-width:4px 30 | ``` 31 | 32 | --- 33 | -------------------------------------------------------------------------------- /src/components/ezp-user-menu/ezp-user-menu.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use './../../shared/global'; 8 | 9 | /** 10 | * 11 | * Rules 12 | * 13 | */ 14 | 15 | :host { 16 | background: var(--ezp-core-surface-tertiary); 17 | border-radius: 6px; 18 | box-shadow: var(--ezp-userMenu-boxShadow, 0 0 0 1px var(--ezp-black-10)); 19 | display: block; 20 | opacity: var(--ezp-userMenu-opacity, 0); 21 | position: absolute; 22 | right: 0; 23 | top: 0; 24 | transform: scale(var(--ezp-userMenu-scale, 0.94, 0.94)); 25 | transform-origin: right top; 26 | transition: var( 27 | --ezp-userMenu-transition, 28 | opacity var(--ezp-duration-3) var(--ezp-easing-out-quart), 29 | transform var(--ezp-duration-3) var(--ezp-easing-out-quart), 30 | visibility 0s var(--ezp-duration-3) 31 | ); 32 | visibility: var(--ezp-userMenu-visibility, hidden); 33 | z-index: 5; 34 | 35 | @at-root { 36 | #{&}(.is-open) { 37 | --ezp-userMenu-opacity: 1; 38 | --ezp-userMenu-scale: 1, 1; 39 | --ezp-userMenu-transition: opacity var(--ezp-duration-3) var(--ezp-easing-out-quart), 40 | transform var(--ezp-duration-3) var(--ezp-easing-out-quart), visibility 0s 0s; 41 | --ezp-userMenu-visibility: visible; 42 | } 43 | } 44 | 45 | @include global.media-appearance('dark') { 46 | --ezp-userMenu-separator-position: inset 0 0 0 var(--hairline-width-positive); 47 | } 48 | } 49 | 50 | #header { 51 | align-items: center; 52 | box-shadow: var(--ezp-userMenu-header-boxShadowPosition, 0 1px 0 0) var(--ezp-core-outline); 53 | box-shadow: 0; 54 | display: grid; 55 | grid-template: auto / 1fr auto; 56 | padding: var(--ezp-spacing-4) var(--ezp-spacing-3) var(--ezp-spacing-4) var(--ezp-spacing-4); 57 | } 58 | 59 | #name { 60 | color: var(--ezp-core-foreground-primary); 61 | } 62 | 63 | #close { 64 | margin: calc(var(--ezp-spacing-3) * -1) 0; 65 | } 66 | 67 | #links { 68 | box-shadow: var(--ezp-userMenu-links-boxShadowPosition, 0 1px 0 0) var(--ezp-core-outline); 69 | display: flex; 70 | flex-direction: column; 71 | padding: var(--ezp-spacing-2); 72 | } 73 | 74 | .link { 75 | align-items: center; 76 | background: var(--ezp-userMenu-link-background, transparent); 77 | border-radius: 3px; 78 | color: var(--ezp-core-foreground-primary); 79 | cursor: pointer; 80 | display: grid; 81 | gap: var(--ezp-spacing-3); 82 | grid-template: auto / auto 1fr; 83 | padding: var(--ezp-spacing-3); 84 | text-decoration: none; 85 | 86 | &:hover { 87 | --ezp-userMenu-link-background: var(--ezp-core-shade-primary); 88 | } 89 | 90 | &__icon { 91 | color: var(--ezp-theme-solid); 92 | margin: calc(var(--ezp-spacing-2) * -1) 0; 93 | } 94 | } 95 | 96 | .caption { 97 | color: var(--ezp-core-foreground-primary); 98 | } 99 | 100 | #theme { 101 | box-shadow: var(--ezp-userMenu-theme-boxShadowPosition, 0 1px 0 0) var(--ezp-core-outline); 102 | display: grid; 103 | gap: var(--ezp-spacing-3); 104 | grid-template: repeat(2, auto) / 1fr; 105 | padding: var(--ezp-spacing-4) var(--ezp-spacing-4) var(--ezp-spacing-3); 106 | } 107 | 108 | #swatches { 109 | display: flex; 110 | margin: 0 calc(var(--ezp-spacing-2) * -1); 111 | } 112 | 113 | .swatch { 114 | align-items: center; 115 | appearance: none; 116 | background: var(--ezp-userMenu-swatch-background, transparent); 117 | border: 0; 118 | border-radius: 50%; 119 | color: var(--ezp-userMenu-swatch-color); 120 | cursor: var(--ezp-userMenu-swatch-cursor, pointer); 121 | display: flex; 122 | justify-content: center; 123 | margin: 0; 124 | opacity: var(--ezp-userMenu-swatch-opacity, 1); 125 | outline: none; 126 | padding: var(--ezp-spacing-2); 127 | 128 | &:not(.selected) { 129 | @media (hover: hover) { 130 | &:hover { 131 | --ezp-userMenu-swatch-background: var(--ezp-userMenu-swatch-theme-translucent); 132 | } 133 | 134 | &:active { 135 | --ezp-userMenu-swatch-opacity: 0.74; 136 | } 137 | } 138 | } 139 | 140 | &.selected { 141 | --ezp-userMenu-dot-boxShadowSize: 10px; 142 | --ezp-userMenu-swatch-background: var(--ezp-userMenu-swatch-theme-translucent); 143 | --ezp-userMenu-swatch-cursor: normal; 144 | 145 | > .dot { 146 | animation: dot-bounce var(--ezp-duration-2) ease-in-out; 147 | } 148 | } 149 | 150 | @each $theme in global.$themes { 151 | &--#{$theme} { 152 | --ezp-userMenu-swatch-theme-solid: var(--ezp-accent-#{$theme}-solid); 153 | --ezp-userMenu-swatch-theme-translucent: var(--ezp-accent-#{$theme}-translucent); 154 | } 155 | } 156 | } 157 | 158 | .dot { 159 | background: var(--ezp-userMenu-dot-background, transparent); 160 | border-radius: 50%; 161 | box-shadow: inset 0 0 0 var(--ezp-userMenu-dot-boxShadowSize, 4px) 162 | var(--ezp-userMenu-swatch-theme-solid); 163 | height: 18px; 164 | transition: box-shadow var(--ezp-duration-2); 165 | width: 18px; 166 | } 167 | 168 | #appearance { 169 | display: grid; 170 | gap: var(--ezp-spacing-3); 171 | grid-template: repeat(2, auto) / 1fr; 172 | padding: var(--ezp-spacing-4) var(--ezp-spacing-4) var(--ezp-spacing-3); 173 | } 174 | 175 | #tabs { 176 | display: grid; 177 | grid-template: auto / repeat(3, 1fr); 178 | margin: 0 calc(var(--ezp-spacing-2) * -1); 179 | } 180 | 181 | .tab { 182 | --ezp-icon-height: 8px; 183 | 184 | align-items: center; 185 | appearance: none; 186 | background: var(--ezp-userMenu-tab-background, transparent); 187 | border: 0; 188 | border-radius: 14px; 189 | color: var(--ezp-userMenu-tab-color, var(--ezp-core-foreground-secondary)); 190 | cursor: var(--ezp-userMenu-tab-cursor, pointer); 191 | display: flex; 192 | gap: var(--ezp-spacing-2); 193 | justify-content: center; 194 | margin: 0; 195 | outline: none; 196 | padding: var(--ezp-spacing-3) var(--ezp-spacing-3) var(--ezp-spacing-3) var(--ezp-spacing-2); 197 | 198 | &:not(.selected) { 199 | @media (hover: hover) { 200 | &:hover { 201 | --ezp-userMenu-tab-background: var(--ezp-core-shade-primary); 202 | } 203 | 204 | &:active { 205 | --ezp-userMenu-tab-background: var(--ezp-core-shade-secondary); 206 | } 207 | } 208 | } 209 | 210 | &.selected { 211 | --ezp-userMenu-tab-background: var(--ezp-theme-translucent); 212 | --ezp-userMenu-tab-color: var(--ezp-theme-solid); 213 | --ezp-userMenu-tab-cursor: normal; 214 | } 215 | } 216 | 217 | @keyframes dot-bounce { 218 | 0% { 219 | transform: scale(1, 1); 220 | } 221 | 222 | 50% { 223 | transform: scale(0.84, 0.84); 224 | } 225 | 226 | 100% { 227 | transform: scale(1, 1); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/components/ezp-user-menu/ezp-user-menu.tsx: -------------------------------------------------------------------------------- 1 | import { Component, Host, Prop, Event, Element, EventEmitter, Watch, h } from '@stencil/core' 2 | import authStore, { EzpAuthorizationService } from '../../services/auth' 3 | import userStore from '../../services/user' 4 | import { IconNameTypes, ThemeTypes, AppearanceTypes } from '../../shared/types' 5 | import i18next from 'i18next' 6 | 7 | @Component({ 8 | tag: 'ezp-user-menu', 9 | styleUrl: 'ezp-user-menu.scss', 10 | shadow: true, 11 | }) 12 | export class EzpUserMenu { 13 | @Element() component!: HTMLEzpUserMenuElement 14 | 15 | private container: HTMLDivElement 16 | private backdrop: HTMLEzpBackdropElement = document.createElement('ezp-backdrop') 17 | private links = [ 18 | { 19 | title: i18next.t('user_menu.account'), 20 | icon: 'account', 21 | href: 'https://app.ezeep.com', 22 | }, 23 | { 24 | title: i18next.t('user_menu.help'), 25 | icon: 'help', 26 | href: 'https://support.ezeep.com', 27 | }, 28 | ] 29 | private themes = ['pink', 'red', 'orange', 'green', 'teal', 'cyan', 'blue', 'violet'] 30 | private appearances = [ 31 | { title: i18next.t('user_menu.system'), name: 'system' }, 32 | { title: i18next.t('user_menu.light'), name: 'light' }, 33 | { title: i18next.t('user_menu.dark'), name: 'dark' }, 34 | ] 35 | 36 | @Prop() name: string = 'John Doe' 37 | @Prop({ mutable: true }) open: boolean = false 38 | 39 | /** 40 | * 41 | * Events 42 | * 43 | */ 44 | 45 | @Event() userMenuClosure: EventEmitter 46 | @Event() logoutEmitter: EventEmitter 47 | auth: EzpAuthorizationService 48 | /** 49 | * 50 | * Privatre methods 51 | * 52 | */ 53 | 54 | /** 55 | * 56 | * Watchers 57 | * 58 | */ 59 | 60 | @Watch('open') 61 | watchOpen() { 62 | if (this.open) { 63 | this.backdrop.visible = true 64 | this.container.appendChild(this.backdrop) 65 | } else { 66 | this.backdrop.visible = false 67 | this.userMenuClosure.emit() 68 | } 69 | } 70 | 71 | private handleClose = () => { 72 | this.userMenuClosure.emit() 73 | } 74 | 75 | private logOut = () => { 76 | localStorage.removeItem('properties') 77 | localStorage.removeItem('refreshToken') 78 | localStorage.removeItem('access_token') 79 | localStorage.removeItem('printer') 80 | localStorage.removeItem('isAuthorized') 81 | authStore.state.isAuthorized = false 82 | this.logoutEmitter.emit() 83 | } 84 | 85 | private handleTheme = (theme: ThemeTypes) => { 86 | userStore.state.theme = theme 87 | } 88 | 89 | private handleAppearance = (appearance: AppearanceTypes) => { 90 | userStore.state.appearance = appearance 91 | } 92 | 93 | /** 94 | * 95 | * Render method 96 | * 97 | */ 98 | 99 | componentWillLoad() { 100 | this.container = this.component.closest('[data-backdrop-surface]') 101 | 102 | this.backdrop.addEventListener('backdropHideStart', () => { 103 | this.open = false 104 | }) 105 | 106 | this.backdrop.addEventListener('backdropHideEnd', () => { 107 | this.container.removeChild(this.backdrop) 108 | }) 109 | } 110 | 111 | /** 112 | * 113 | * Render method 114 | * 115 | */ 116 | 117 | render() { 118 | return ( 119 | 120 | 124 | 136 |
137 | 138 |
139 | {this.themes.map((theme) => ( 140 | 148 | ))} 149 |
150 |
151 |
152 | 157 |
158 | {this.appearances.map((appearance) => ( 159 | 166 | ))} 167 |
168 |
169 |
170 | ) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/components/ezp-user-menu/readme.md: -------------------------------------------------------------------------------- 1 | # ezp-user-menu 2 | 3 | 4 | 5 | ## Properties 6 | 7 | | Property | Attribute | Description | Type | Default | 8 | | -------- | --------- | ----------- | --------- | ------------ | 9 | | `name` | `name` | | `string` | `'John Doe'` | 10 | | `open` | `open` | | `boolean` | `false` | 11 | 12 | ## Events 13 | 14 | | Event | Description | Type | 15 | | ----------------- | ----------- | ------------------ | 16 | | `logoutEmitter` | | `CustomEvent` | 17 | | `userMenuClosure` | Events | `CustomEvent` | 18 | 19 | ## Dependencies 20 | 21 | ### Used by 22 | 23 | - [ezp-printer-selection](../ezp-printer-selection) 24 | 25 | ### Depends on 26 | 27 | - [ezp-backdrop](../ezp-backdrop) 28 | - [ezp-label](../ezp-label) 29 | - [ezp-icon-button](../ezp-icon-button) 30 | - [ezp-icon](../ezp-icon) 31 | 32 | ### Graph 33 | 34 | ```mermaid 35 | graph TD; 36 | ezp-user-menu --> ezp-backdrop 37 | ezp-user-menu --> ezp-label 38 | ezp-user-menu --> ezp-icon-button 39 | ezp-user-menu --> ezp-icon 40 | ezp-icon-button --> ezp-icon 41 | ezp-printer-selection --> ezp-user-menu 42 | style ezp-user-menu fill:#f9f,stroke:#333,stroke-width:4px 43 | ``` 44 | 45 | --- 46 | -------------------------------------------------------------------------------- /src/data/file-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | ".bmp", 3 | ".csv", 4 | ".doc", 5 | ".docm", 6 | ".docx", 7 | ".dot", 8 | ".dotm", 9 | ".dotx", 10 | ".eml", 11 | ".gif", 12 | ".htm", 13 | ".html", 14 | ".jpeg", 15 | ".jpg", 16 | ".log", 17 | ".mht", 18 | ".mhtml", 19 | ".odf", 20 | ".odg", 21 | ".odm", 22 | ".odp", 23 | ".odt", 24 | ".otg", 25 | ".oth", 26 | ".otp", 27 | ".ott", 28 | ".pdf", 29 | ".png", 30 | ".pot", 31 | ".potm", 32 | ".potx", 33 | ".pps", 34 | ".ppsx", 35 | ".ppt", 36 | ".pptm", 37 | ".pptx", 38 | ".rtf", 39 | ".scp", 40 | ".sda", 41 | ".sdd", 42 | ".sds", 43 | ".sdw", 44 | ".sgl", 45 | ".smf", 46 | ".sti", 47 | ".stw", 48 | ".sxd", 49 | ".sxg", 50 | ".sxi", 51 | ".sxm", 52 | ".sxw", 53 | ".tif", 54 | ".tiff", 55 | ".tpf", 56 | ".txt", 57 | ".vor", 58 | ".wtx", 59 | ".xls", 60 | ".xlsb", 61 | ".xlsm", 62 | ".xlsx", 63 | ".xlt", 64 | ".xltm", 65 | ".xltx", 66 | ".xml", 67 | ".xps" 68 | ] 69 | -------------------------------------------------------------------------------- /src/data/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "login_dialog": { 3 | "heading": "Drucke mit ezeep Blue", 4 | "description": "Melde dich mit deinem ezeep Blue-Konto an, um zu drucken.", 5 | "action": "Anmelden" 6 | }, 7 | "no_document_dialog": { 8 | "heading": "Keine Datei ausgewählt", 9 | "description": "Wähle zunächst eine Datei zum Drucken aus.", 10 | "action": "OK" 11 | }, 12 | "printer_selection": { 13 | "loading": "Lade meine Drucker…", 14 | "print": "Drucken", 15 | "printer": "Drucker", 16 | "print_processing": "Drucken…", 17 | "print_success": "Erfolgreich gedruckt.", 18 | "print_failed": "Druck fehlgeschlagen", 19 | "not_supported": "Der ausgewählte Dateityp wird nicht unterstützt.", 20 | "color": "Farbe", 21 | "orientation": "Ausrichtung", 22 | "size": "Papierformat", 23 | "duplex": "Beidseitig", 24 | "quality": "Druckqualität", 25 | "select_printer": "Wähle einen Drucker", 26 | "select_color": "Wähle Farbe/Graustufen", 27 | "select_size": "Wähle Papierformat", 28 | "select_orientation": "Wähle Ausrichtung", 29 | "select_quality": "Wähle Druckqualität", 30 | "select_duplex": "Wähle Beidseitig", 31 | "color_color": "Farbe", 32 | "color_grayscale": "Graustufen", 33 | "orientation_portrait": "Hochformat", 34 | "orientation_landscape": "Querformat", 35 | "unknown_location": "Unbekannter Standort", 36 | "no_printers": "Keine Drucker gefunden.", 37 | "prepare_upload": "Vorbereiten…", 38 | "uploading": "Hochladen…", 39 | "duplex_none": "Keine", 40 | "copies": "Kopien", 41 | "duplex_long": "auf der langen Seite umdrehen", 42 | "duplex_short": "auf der kurzen Seite umdrehen", 43 | "pull_print_success": "Bereit zum Auslösen.", 44 | "width":"Breite", 45 | "length":"Länge", 46 | "trays": "Papierfach", 47 | "select_trays": "Wähle Papierfach", 48 | "page_ranges": "Druckbereich", 49 | "paper_ranges": "Papier-Ranges" 50 | }, 51 | "button_actions": { 52 | "login": "Anmelden", 53 | "print": "Drucken", 54 | "cancel": "Abbrechen", 55 | "close": "Schließen", 56 | "retry": "Wiederholen", 57 | "select_printer": "Wähle Drucker" 58 | }, 59 | "upload": { 60 | "description": "Ziehe eine Datei hier in den Browser ", 61 | "meta_leading": "oder", 62 | "meta_select": "wähle eine Datei aus", 63 | "meta_trailing": "um mit ezeep Blue zu drucken.", 64 | "selected_file": "Ausgewählte Datei:" 65 | }, 66 | "user_menu": { 67 | "theme": "Farbschema", 68 | "appearance": "Darstellung", 69 | "system": "System", 70 | "light": "Hell", 71 | "dark": "Dunkel", 72 | "account": "Konto verwalten", 73 | "help": "Hilfe & Unterstützung", 74 | "logout": "Ausloggen" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/data/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "login_dialog": { 3 | "heading": "Print with ezeep Blue", 4 | "description": "Sign in with your ezeep Blue account to print.", 5 | "action": "Sign in" 6 | }, 7 | "no_document_dialog": { 8 | "heading": "No file selected", 9 | "description": "Select a file first to print.", 10 | "action": "OK" 11 | }, 12 | "printer_selection": { 13 | "loading": "Loading My Printers…", 14 | "print_processing": "Printing…", 15 | "print_success": "Successfully printed.", 16 | "pull_print_success": "Ready to be released.", 17 | "print_failed": "Print failed", 18 | "not_supported": "The selected file type is not supported.", 19 | "prepare_upload": "Preparing…", 20 | "uploading": "Uploading…", 21 | "print": "Print", 22 | "printer": "Printer", 23 | "color": "Color", 24 | "orientation": "Orientation", 25 | "size": "Paper Size", 26 | "duplex": "Double-sided", 27 | "duplex_none": "None", 28 | "duplex_long": "flip on long side", 29 | "duplex_short": "flip on short side", 30 | "quality": "Print Quality", 31 | "select_printer": "Select a printer", 32 | "select_color": "Select Color/Grayscale", 33 | "select_orientation": "Select Orientation", 34 | "select_size": "Select Paper Size", 35 | "select_quality": "Select Print Quality", 36 | "select_duplex": "Select Double-sided", 37 | "color_color": "Color", 38 | "color_grayscale": "Grayscale", 39 | "orientation_portrait": "Portrait", 40 | "orientation_landscape": "Landscape", 41 | "unknown_location": "Unknown location", 42 | "no_printers": "No printers found.", 43 | "copies": "Copies", 44 | "width":"Width", 45 | "length":"Length", 46 | "trays": "Tray", 47 | "select_trays": "Select Tray", 48 | "page_ranges": "Page Ranges", 49 | "paper_ranges": "Paper Ranges" 50 | }, 51 | "button_actions": { 52 | "login": "Sign In", 53 | "print": "Print", 54 | "cancel": "Cancel", 55 | "close": "Close", 56 | "retry": "Retry", 57 | "select_printer": "Select Printer" 58 | }, 59 | "upload": { 60 | "description": "Drop a file here into the browser ", 61 | "meta_leading": "or", 62 | "meta_select": "select a file", 63 | "meta_trailing": "to print with ezeep Blue.", 64 | "selected_file": "Selected File:" 65 | }, 66 | "user_menu": { 67 | "theme": "Color theme", 68 | "appearance": "Appearance", 69 | "system": "System", 70 | "light": "Light", 71 | "dark": "Dark", 72 | "account": "Manage Account", 73 | "help": "Help & Support", 74 | "logout": "Logout" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/data/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": [ 3 | { 4 | "id": 1, 5 | "name": "Grayscale" 6 | }, 7 | { 8 | "id": 2, 9 | "name": "Color" 10 | } 11 | ], 12 | "orientations": [ 13 | { 14 | "id": 1, 15 | "name": "Portrait" 16 | }, 17 | { 18 | "id": 2, 19 | "name": "Landscape" 20 | } 21 | ], 22 | "sizes": [ 23 | { 24 | "id": 1, 25 | "name": "Auto", 26 | "description": "Document size" 27 | }, 28 | { 29 | "id": 2, 30 | "name": "Letter", 31 | "description": "8.5 x 11 in" 32 | }, 33 | { 34 | "id": 3, 35 | "name": "Ledger", 36 | "description": "11 x 17 in" 37 | }, 38 | { 39 | "id": 4, 40 | "name": "Legal", 41 | "description": "8.5 x 14 in" 42 | }, 43 | { 44 | "id": 5, 45 | "name": "Executive", 46 | "description": "7.25 x 10.5 in" 47 | }, 48 | { 49 | "id": 6, 50 | "name": "A3", 51 | "description": "11.7 x 16.5 in" 52 | }, 53 | { 54 | "id": 7, 55 | "name": "A4", 56 | "description": "8.3 x 11.7 in" 57 | }, 58 | { 59 | "id": 8, 60 | "name": "A5", 61 | "description": "5.8 x 8.3 in" 62 | }, 63 | { 64 | "id": 9, 65 | "name": "Folio", 66 | "description": "8.5 x 13 in" 67 | }, 68 | { 69 | "id": 10, 70 | "name": "Com-10", 71 | "description": "4.125 x 9.5 in" 72 | } 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /src/data/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "firstName": "John", 4 | "lastName": "Doe", 5 | "email": "john.deo@company.com", 6 | "organizations": [ 7 | { 8 | "id": 1, 9 | "name": "Organization 1", 10 | "printers": [ 11 | { 12 | "id": 1, 13 | "name": "Printer Name 1", 14 | "location": "Printer Location 1" 15 | }, 16 | { 17 | "id": 2, 18 | "name": "Printer Name 2", 19 | "location": "Printer Location 2" 20 | }, 21 | { 22 | "id": 3, 23 | "name": "Printer Name 3", 24 | "location": "Printer Location 3" 25 | }, 26 | { 27 | "id": 4, 28 | "name": "Printer Name 4", 29 | "location": "Printer Location 4" 30 | }, 31 | { 32 | "id": 5, 33 | "name": "Printer Name 5", 34 | "location": "Printer Location 5" 35 | }, 36 | { 37 | "id": 7, 38 | "name": "Printer Name 7", 39 | "location": "Printer Location 7" 40 | }, 41 | { 42 | "id": 8, 43 | "name": "Printer Name 8", 44 | "location": "Printer Location 8" 45 | }, 46 | { 47 | "id": 9, 48 | "name": "Printer Name 9", 49 | "location": "Printer Location 9" 50 | }, 51 | { 52 | "id": 10, 53 | "name": "Printer Name 10", 54 | "location": "Printer Location 10" 55 | }, 56 | { 57 | "id": 11, 58 | "name": "Printer Name 11", 59 | "location": "Printer Location 11" 60 | }, 61 | { 62 | "id": 12, 63 | "name": "Printer Name 12", 64 | "location": "Printer Location 12" 65 | }, 66 | { 67 | "id": 13, 68 | "name": "Printer Name 13", 69 | "location": "Printer Location 13" 70 | } 71 | ] 72 | }, 73 | { 74 | "id": 2, 75 | "name": "Organization 2", 76 | "printers": [ 77 | { 78 | "id": 1, 79 | "name": "Printer Name 1", 80 | "location": "Printer Location 1" 81 | }, 82 | { 83 | "id": 2, 84 | "name": "Printer Name 2", 85 | "location": "Printer Location 2" 86 | }, 87 | { 88 | "id": 3, 89 | "name": "Printer Name 3", 90 | "location": "Printer Location 3" 91 | }, 92 | { 93 | "id": 4, 94 | "name": "Printer Name 4", 95 | "location": "Printer Location 4" 96 | }, 97 | { 98 | "id": 5, 99 | "name": "Printer Name 5", 100 | "location": "Printer Location 5" 101 | }, 102 | { 103 | "id": 6, 104 | "name": "Printer Name 6", 105 | "location": "Printer Location 6" 106 | } 107 | ] 108 | }, 109 | { 110 | "id": 3, 111 | "name": "Organization 3", 112 | "printers": [ 113 | { 114 | "id": 1, 115 | "name": "Printer Name 1", 116 | "location": "Printer Location 1" 117 | }, 118 | { 119 | "id": 2, 120 | "name": "Printer Name 2", 121 | "location": "Printer Location 2" 122 | }, 123 | { 124 | "id": 3, 125 | "name": "Printer Name 3", 126 | "location": "Printer Location 3" 127 | }, 128 | { 129 | "id": 4, 130 | "name": "Printer Name 4", 131 | "location": "Printer Location 4" 132 | }, 133 | { 134 | "id": 5, 135 | "name": "Printer Name 5", 136 | "location": "Printer Location 5" 137 | }, 138 | { 139 | "id": 6, 140 | "name": "Printer Name 6", 141 | "location": "Printer Location 6" 142 | } 143 | ] 144 | } 145 | ] 146 | } 147 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ezeep-js 7 | 8 | 9 | 54 | 55 | 56 | 57 | 70 | 79 | 80 | 81 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { Components, JSX } from './components' 2 | -------------------------------------------------------------------------------- /src/services/auth.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from '@stencil/store' 2 | import { encodeFormData } from '../utils/utils' 3 | 4 | export class EzpAuthorizationService { 5 | constructor(redirectURI: string, clientID: string) { 6 | this.redirectURI = redirectURI 7 | this.clientID = clientID 8 | 9 | this.oauthUrl = authStore.state.authApiHostUrl 10 | this.authURI = new URL(`https://${this.oauthUrl}/oauth/authorize/`) 11 | this.accessTokenURL = `https://${this.oauthUrl}/oauth/access_token/` 12 | } 13 | 14 | clientID: string 15 | redirectURI: string 16 | oauthUrl: string 17 | authURI: URL 18 | urlParams = new URLSearchParams() 19 | isAuthorized = false 20 | accessTokenURL: string 21 | codeVerifier: string 22 | codeChallenge: string 23 | accessToken: string 24 | refreshToken: string 25 | 26 | generateCodeVerifier() { 27 | if (authStore.state.codeVerifier !== '') { 28 | this.codeVerifier = authStore.state.codeVerifier 29 | } else { 30 | const arr = new Uint8Array(128) 31 | const randomValueArray = crypto.getRandomValues(arr) 32 | const codeVerifier = btoa(randomValueArray.toString()).substr(0, 128) 33 | this.codeVerifier = codeVerifier 34 | authStore.state.codeVerifier = this.codeVerifier 35 | } 36 | } 37 | 38 | async generateCodeChallenge(codeVerifier: string) { 39 | const encoder = new TextEncoder() 40 | const codeData = encoder.encode(codeVerifier) 41 | const digest = await crypto.subtle.digest('SHA-256', codeData) 42 | const base64Digest = btoa(String.fromCharCode.apply(null, new Uint8Array(digest))) 43 | this.codeChallenge = base64Digest.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') 44 | } 45 | 46 | buildAuthURI() { 47 | this.urlParams.append('response_type', 'code') 48 | this.urlParams.append('client_id', this.clientID) 49 | this.urlParams.append('redirect_uri', this.redirectURI) 50 | this.urlParams.append('code_challenge', this.codeChallenge) 51 | this.urlParams.append('code_challenge_method', 'S256') 52 | this.authURI.search = this.urlParams.toString() 53 | authStore.state.authUri = this.authURI.toString() 54 | } 55 | 56 | getAccessToken() { 57 | return fetch(this.accessTokenURL, { 58 | credentials: 'include', 59 | headers: { 60 | Authorization: 'Basic ' + btoa(this.clientID + ':'), 61 | 'Content-Type': 'application/x-www-form-urlencoded', 62 | }, 63 | method: 'POST', 64 | body: encodeFormData({ 65 | grant_type: 'authorization_code', 66 | scope: 'printing', 67 | code: authStore.state.code, 68 | redirect_uri: this.redirectURI, 69 | code_verifier: authStore.state.codeVerifier, 70 | }), 71 | }) 72 | .then((response) => { 73 | return response.json() // parse response 74 | }) 75 | .then((data) => { 76 | // actual object 77 | if (data.access_token) { 78 | authStore.state.isAuthorized = true 79 | this.isAuthorized = authStore.state.isAuthorized 80 | localStorage.setItem('isAuthorized', this.isAuthorized.toString()) 81 | 82 | this.accessToken = data.access_token 83 | localStorage.setItem('access_token', this.accessToken) 84 | authStore.state.accessToken = this.accessToken 85 | 86 | this.refreshToken = data.refresh_token 87 | localStorage.setItem('refreshToken', this.refreshToken) 88 | authStore.state.refreshToken = this.refreshToken 89 | } 90 | }) 91 | } 92 | 93 | refreshTokens() { 94 | fetch(this.accessTokenURL, { 95 | credentials: 'include', 96 | headers: { 97 | Authorization: 'Basic ' + btoa(this.clientID + ':'), 98 | 'Content-Type': 'application/x-www-form-urlencoded', 99 | }, 100 | method: 'POST', 101 | body: encodeFormData({ 102 | grant_type: 'refresh_token', 103 | scope: 'printing', 104 | refresh_token: authStore.state.refreshToken, 105 | }), 106 | }) 107 | .then((response) => response.json()) 108 | .then((data) => { 109 | if (data.access_token) { 110 | this.accessToken = data.access_token 111 | localStorage.setItem('access_token', this.accessToken) 112 | authStore.state.accessToken = this.accessToken 113 | 114 | this.refreshToken = data.refresh_token 115 | localStorage.setItem('refreshToken', this.refreshToken) 116 | authStore.state.refreshToken = this.refreshToken 117 | 118 | authStore.state.isAuthorized = true 119 | } 120 | }) 121 | } 122 | 123 | revokeRefreshToken() { 124 | if (authStore.state.refreshToken) 125 | fetch(`https://${this.oauthUrl}/oauth/revoke/`, { 126 | credentials: 'include', 127 | headers: { 128 | Authorization: 'Basic ' + btoa(this.clientID + ':'), 129 | 'Content-Type': 'application/x-www-form-urlencoded', 130 | }, 131 | method: 'POST', 132 | body: encodeFormData({ 133 | token: authStore.state.refreshToken, 134 | }), 135 | }).catch((error) => { 136 | console.log(error) 137 | }) 138 | } 139 | } 140 | 141 | const authStore = createStore({ 142 | code: '', 143 | codeVerifier: '', 144 | accessToken: '', 145 | refreshToken: '', 146 | isAuthorized: false, 147 | devApi: false, 148 | authApiHostUrl: '', 149 | redirectUri: '', 150 | authUri: '', 151 | }) 152 | 153 | export default authStore 154 | 155 | export function sendCodeToParentWindow() { 156 | // get the URL parameters which will include the auth code 157 | const params = new URLSearchParams(window.location.search) 158 | const code = params.get('code') 159 | if (window.opener) { 160 | if (code) { 161 | // send them to the opening window 162 | window.opener.postMessage(code, authStore.state.redirectUri) 163 | window.close() 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/services/print.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from '@stencil/store' 2 | import authStore, { EzpAuthorizationService } from './auth' 3 | import fetchIntercept from 'fetch-intercept' 4 | import { /* PrinterProperties, */ PrinterConfig, PrinterProperties } from '../shared/types' 5 | import { AnonymousCredential, BlockBlobClient, newPipeline } from '@azure/storage-blob' 6 | export class EzpPrintService { 7 | constructor(redirectURI: string, clientID: string) { 8 | this.redirectURI = redirectURI 9 | this.clientID = clientID 10 | this.printingApi = printStore.state.printApiHostUrl 11 | 12 | this.checkStoredRefreshToken() 13 | } 14 | 15 | clientID: string 16 | redirectURI: string 17 | devApi: boolean 18 | printerConfig: PrinterConfig 19 | printingApi: string 20 | abortController: AbortController | null = null; 21 | 22 | private checkStoredRefreshToken() { 23 | if (authStore.state.refreshToken !== '') { 24 | return 25 | } 26 | if (localStorage.getItem('refreshToken') === null) { 27 | authStore.state.refreshToken = '' 28 | } else { 29 | authStore.state.refreshToken = localStorage.getItem('refreshToken') 30 | } 31 | } 32 | 33 | registerFetchInterceptor() { 34 | fetchIntercept.register({ 35 | request: (url, config) => { 36 | // Modify the url or config here 37 | return [url, config] 38 | }, 39 | requestError: (error) => { 40 | // Called when an error occured during another 'request' interceptor call 41 | return Promise.reject(error) 42 | }, 43 | // check for response status here 44 | response: (response) => { 45 | if (response.status === 401) { 46 | if (authStore.state.refreshToken === '') { 47 | return response 48 | } 49 | const authService = new EzpAuthorizationService(this.redirectURI, this.clientID) 50 | authService.refreshTokens() 51 | } 52 | // Modify the reponse object 53 | return response 54 | }, 55 | responseError: (error) => { 56 | // Handle a fetch error 57 | return Promise.reject(error) 58 | }, 59 | }) 60 | } 61 | 62 | getPrinterList(accessToken: string) { 63 | return fetch(`https://${this.printingApi}/sfapi/GetPrinter/`, { 64 | method: 'GET', 65 | headers: { 66 | Authorization: 'Bearer ' + accessToken, 67 | }, 68 | }).then((response) => response.json()) 69 | } 70 | 71 | async getConfig(accessToken: string) { 72 | return fetch(`https://${this.printingApi}/sfapi/GetConfiguration/`, { 73 | method: 'GET', 74 | headers: { 75 | Authorization: 'Bearer ' + accessToken, 76 | }, 77 | }) 78 | } 79 | 80 | getPrinterProperties(accessToken: string, printerID: string) { 81 | return fetch(`https://${this.printingApi}/sfapi/GetPrinterProperties/?id=${printerID}`, { 82 | method: 'GET', 83 | headers: { 84 | Authorization: 'Bearer ' + accessToken, 85 | }, 86 | }).then((response) => { 87 | return response.json() 88 | }) 89 | } 90 | 91 | getAllPrinterProperties(accessToken: string) { 92 | return fetch(`https://${this.printingApi}/sfapi/GetPrinterProperties/`, { 93 | method: 'GET', 94 | headers: { 95 | Authorization: 'Bearer ' + accessToken, 96 | }, 97 | }).then((response) => { 98 | return response.json() 99 | }) 100 | } 101 | 102 | printFileByUrl( 103 | accessToken: string, 104 | fileUrl: string, 105 | fileType: string, 106 | printerID: string, 107 | properties: PrinterProperties, 108 | filename?: string, 109 | printAndDelete?: boolean 110 | ) { 111 | this.abortController = new AbortController(); 112 | 113 | return fetch(`https://${this.printingApi}/sfapi/Print/`, { 114 | method: 'POST', 115 | headers: { 116 | Authorization: 'Bearer ' + accessToken, 117 | 'Content-Type': 'application/json', 118 | }, 119 | body: JSON.stringify({ 120 | fileurl: fileUrl, 121 | type: fileType, 122 | printerid: printerID, 123 | ...(filename && { alias: filename }), 124 | ...(printAndDelete && { printanddelete: printAndDelete }), 125 | properties, 126 | }), 127 | signal: this.abortController.signal 128 | }) 129 | } 130 | 131 | abortPrint() { 132 | if (this.abortController) { 133 | this.abortController.abort(); 134 | this.abortController = null; 135 | } 136 | } 137 | 138 | printByFileID( 139 | accessToken: string, 140 | fileID: string, 141 | fileType: string, 142 | printerID: string, 143 | properties: PrinterProperties, 144 | filename?: string, 145 | printAndDelete?: boolean 146 | ) { 147 | return fetch(`https://${this.printingApi}/sfapi/Print/`, { 148 | method: 'POST', 149 | headers: { 150 | Authorization: 'Bearer ' + accessToken, 151 | 'Content-Type': 'application/json', 152 | }, 153 | body: JSON.stringify({ 154 | fileid: fileID, 155 | type: fileType, 156 | printerid: printerID, 157 | ...(filename && { alias: filename }), 158 | ...(printAndDelete && { printanddelete: printAndDelete }), 159 | properties, 160 | }), 161 | }).then((response) => response.json()) 162 | } 163 | 164 | prepareFileUpload(accessToken: string) { 165 | return fetch(`https://${this.printingApi}/sfapi/PrepareUpload/`, { 166 | method: 'GET', 167 | headers: { 168 | Authorization: 'Bearer ' + accessToken, 169 | }, 170 | }).then((response) => response.json()) 171 | } 172 | 173 | uploadFile(sasURI: string, formData: FormData) { 174 | return fetch(`${sasURI}`, { 175 | method: 'PUT', 176 | headers: { 177 | 'x-ms-blob-type': 'BlockBlob', 178 | 'Content-Type:': 'multipart/form-data', // try and not set it, see if it does it automatically 179 | }, 180 | body: formData, 181 | }).then((response) => response.json()) 182 | } 183 | 184 | async uploadBlobFiles(sasUri: string, file: File) { 185 | printStore.state.uploadProgress = 0 186 | const pipeline = newPipeline(new AnonymousCredential(), { 187 | retryOptions: { maxTries: 4 }, 188 | userAgentOptions: { userAgentPrefix: 'AdvancedSample V1.0.0' }, // Customized telemetry string 189 | keepAliveOptions: { 190 | // Keep alive is enabled by default, disable keep alive by setting false 191 | enable: false, 192 | }, 193 | }) 194 | 195 | const client = new BlockBlobClient(sasUri, pipeline) 196 | const response = await client.uploadData(file, { 197 | blockSize: 4 * 1024 * 1024, //4mb blocksize 198 | concurrency: 20, 199 | onProgress: (e) => { 200 | let progress = (100 * e.loadedBytes) / file.size 201 | printStore.state.uploadProgress = progress 202 | }, 203 | blobHTTPHeaders: { blobContentType: 'application/octet-stream' }, 204 | }) 205 | 206 | return response 207 | } 208 | 209 | getPrintStatus = () => { 210 | return fetch( 211 | `https://${this.printingApi}/sfapi/Status/?id=${encodeURIComponent(printStore.state.jobID)}`, 212 | { 213 | headers: { 214 | Authorization: 'Bearer ' + authStore.state.accessToken, 215 | }, 216 | } 217 | ).then((response) => response.json()) 218 | } 219 | } 220 | 221 | const printStore = createStore({ 222 | printers: [], 223 | jobID: '', 224 | printFinished: false, 225 | printApiHostUrl: '', 226 | printerProperties: {}, 227 | fileID: '', 228 | fileUrl: '', 229 | fileType: '', 230 | printerID: '', 231 | fileName: '', 232 | uploadProgress: 0, 233 | supportedFileExtensions: '', 234 | }) 235 | 236 | export default printStore 237 | -------------------------------------------------------------------------------- /src/services/user.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from '@stencil/store' 2 | import authStore from './auth' 3 | 4 | export class EzpUserService { 5 | getUserInfo() { 6 | return fetch(`https://${authStore.state.authApiHostUrl}/v1/users/me`, { 7 | headers: { 8 | Authorization: 'Bearer ' + authStore.state.accessToken, 9 | 'Content-Type': 'application/json', 10 | }, 11 | method: 'GET', 12 | }).then((response) => { 13 | return response.json() 14 | }) 15 | } 16 | } 17 | 18 | const userStore = createStore({ 19 | user: null, 20 | theme: '', 21 | appearance: '', 22 | }) 23 | 24 | export default userStore 25 | -------------------------------------------------------------------------------- /src/shared/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "authApiHostUrl": "account.ezeep.com", 3 | "printingApiHostUrl": "printapi.ezeep.com" 4 | } 5 | -------------------------------------------------------------------------------- /src/shared/global.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Modules 4 | * 5 | */ 6 | 7 | @use 'sass:math'; 8 | @use 'sass:map'; 9 | @use 'sass:list'; 10 | @use 'sass:color'; 11 | 12 | /** 13 | * 14 | * Variables 15 | * 16 | */ 17 | 18 | $breakpoints: ( 19 | 'sm': 480px, 20 | 'md': 800px, 21 | 'lg': 1120px, 22 | ); 23 | $easings: ( 24 | 'in-quart': cubic-bezier(0.11, 0, 0.5, 0), 25 | 'in-out-quart': cubic-bezier(0.76, 0, 0.24, 1), 26 | 'in-out-cubic': cubic-bezier(0.65, 0, 0.35, 1), 27 | 'in-expo': cubic-bezier(0.7, 0, 0.84, 0), 28 | 'out-quart': cubic-bezier(0.25, 1, 0.5, 1), 29 | 'out-expo': cubic-bezier(0.16, 1, 0.3, 1), 30 | ); 31 | $durations: (0.1s, 0.16s, 0.26s, 0.42s, 0.68s, 1s); 32 | $ratios: (1, 2, 3); 33 | $spacings: (4px, 6px, 10px, 16px, 26px, 42px, 68px, 110px); 34 | $themes: ('blue', 'cyan', 'green', 'teal', 'orange', 'pink', 'red', 'violet'); 35 | 36 | /** 37 | * 38 | * Functions 39 | * 40 | */ 41 | 42 | @function getLetterSpacing($fontSize) { 43 | $a: -0.0223; 44 | $b: 0.185; 45 | $c: -0.1745; 46 | $d: math.div($fontSize, 1px); 47 | 48 | @return ($a + ($b * math.pow(math.$e, $c * $d))) * 1em; 49 | } 50 | 51 | @function getOffset($fontSize, $lineHeight) { 52 | $boxHeight: 2 * math.round(math.div(((math.div(2048, 2816)) * $fontSize), 2)); 53 | 54 | @return math.div(($lineHeight - $boxHeight), 2); 55 | } 56 | 57 | /** 58 | * 59 | * Mixins 60 | * 61 | */ 62 | 63 | @mixin media-viewport($breakpoint, $direction: 'min', $dimension: 'width') { 64 | @media (#{$direction}-#{$dimension}: #{map.get($breakpoints, $breakpoint)}) { 65 | @content; 66 | } 67 | } 68 | 69 | @mixin media-input($device) { 70 | @media (pointer: #{if($device == 'touch', 'coarse', 'fine')}) { 71 | @content; 72 | } 73 | } 74 | 75 | @mixin media-appearance($appearance) { 76 | @media (prefers-color-scheme: #{$appearance}) { 77 | @content; 78 | } 79 | } 80 | 81 | @mixin media-density($ratio) { 82 | @media (-webkit-min-device-pixel-ratio: #{$ratio}), (min-device-pixel-ratio: #{$ratio}) { 83 | @content; 84 | } 85 | } 86 | 87 | @mixin duration-declaration() { 88 | @each $duration in $durations { 89 | --ezp-duration-#{list.index($durations, $duration)}: #{$duration}; 90 | } 91 | } 92 | 93 | @mixin spacing-declaration() { 94 | @each $spacing in $spacings { 95 | --ezp-spacing-#{list.index($spacings, $spacing)}: #{$spacing}; 96 | } 97 | } 98 | 99 | @mixin easing-declaration() { 100 | @each $easing in $easings { 101 | --ezp-easing-#{list.nth($easing, 1)}: #{list.nth($easing, 2)}; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/shared/global.ts: -------------------------------------------------------------------------------- 1 | export default async () => { 2 | const font = new FontFace( 3 | 'Inter', 4 | 'url(https://rsms.me/inter/font-files/Inter-roman.var.woff2)', 5 | { 6 | style: 'normal', 7 | weight: '400 600', 8 | } 9 | ) 10 | 11 | font.load().then(() => { 12 | document.fonts.add(font) 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/types.d.ts: -------------------------------------------------------------------------------- 1 | export type IconButtonLevelTypes = 'primary' | 'secondary' | 'tertiary' | 'quaternary' 2 | export type IconButtonTypeTypes = 'button' 3 | export type IconNameTypes = 4 | | 'account' 5 | | 'checkmark-alt' 6 | | 'checkmark' 7 | | 'close' 8 | | 'color' 9 | | 'copies' 10 | | 'dark' 11 | | 'drag-drop' 12 | | 'duplex' 13 | | 'exclamation-mark' 14 | | 'expand' 15 | | 'height' 16 | | 'help' 17 | | 'light' 18 | | 'logo' 19 | | 'logout' 20 | | 'menu' 21 | | 'minus' 22 | | 'orientation' 23 | | 'plus' 24 | | 'printer' 25 | | 'quality' 26 | | 'question-mark' 27 | | 'size' 28 | | 'system' 29 | | 'width' 30 | | 'paper_range' 31 | | 'trays' 32 | export type IconSizeTypes = 'normal' | 'large' | 'huge' 33 | export type PrintOrganizationType = { id: number; name: string; printers: PrintPrinterType[] } 34 | export type PrintPrinterType = { id: number; name: string; location: string } 35 | export type PrintUserType = { 36 | id: number 37 | firstName: string 38 | lastName: string 39 | email: string 40 | organizations: PrintOrganizationType[] 41 | } 42 | export type SelectFlowTypes = 'vertical' | 'horizontal' 43 | export type SelectOptionType = { 44 | id: number | string | boolean 45 | title: string 46 | meta: string 47 | type?: string 48 | } 49 | export type TextButtonLevelTypes = 'primary' | 'secondary' | 'tertiary' 50 | export type TextButtonTypeTypes = 'button' 51 | export type LabelLevelTypes = 'primary' | 'secondary' | 'tertiary' 52 | export type WeightTypes = 'soft' | 'strong' | 'heavy' 53 | export type ThemeTypes = 'pink' | 'red' | 'orange' | 'green' | 'teal' | 'cyan' | 'blue' | 'violet' 54 | export type AppearanceTypes = 'system' | 'light' | 'dark' 55 | export type TriggerTypes = 'custom' | 'file' | 'button' 56 | export type AlertType = { open: boolean; heading: string; description: string } 57 | export type SystemAppearanceTypes = 'light' | 'dark' 58 | export interface PrinterProperties { 59 | paper?: string 60 | paperid?: number | string 61 | color?: boolean | string 62 | duplex?: boolean | string 63 | duplexmode?: number | string 64 | orientation?: number | string 65 | copies?: number | string 66 | resolution?: string | number 67 | paperlength? : string | number 68 | paperwidth? : string | number 69 | defaultSource?: number | string 70 | trayname?: string 71 | PageRanges? : string 72 | } 73 | 74 | export interface Printer { 75 | id: string 76 | location: string 77 | name: string 78 | is_queue: boolean 79 | } 80 | 81 | export interface PaperFormat { 82 | Id: number 83 | Name: string 84 | XRes: number 85 | YRes: number 86 | Default: boolean 87 | } 88 | 89 | export interface Trays { 90 | Default : boolean 91 | Index: number 92 | Name: string 93 | } 94 | 95 | export interface PrinterConfig { 96 | Default?: { 97 | Duplex?: string 98 | Color?: string 99 | Orientation?: string 100 | Resolution?: string 101 | Paper?: string 102 | Tray?: string 103 | }, 104 | Collate?: boolean 105 | Color?: boolean 106 | ColorSupported?: boolean 107 | Driver?: string 108 | DuplexMode?: number 109 | DuplexSupported?: boolean 110 | Id?: string 111 | Location?: string 112 | MediaSupported?: Array 113 | MediaSupportedId?: Array 114 | Name?: string 115 | OrientationsSupported?: Array 116 | OrientationsSupportedId?: Array 117 | PaperFormats?: Array 118 | Resolutions?: Array 119 | DefaultResolution?: string 120 | TPUID?: number 121 | Trays?: Array 122 | } 123 | 124 | export interface User { 125 | azureProfile: any 126 | dateJoined: string 127 | displayName: string 128 | email: string 129 | firstName: string 130 | id: string 131 | idProfile: { 132 | name: string 133 | oid: string 134 | preferredUsername: string 135 | } 136 | isVerfified: boolean 137 | lastName: string 138 | roles: Array 139 | userInvitations: any 140 | } 141 | -------------------------------------------------------------------------------- /src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next' 2 | import translationsDE from '../data/locales/de.json' 3 | import translationsEN from '../data/locales/en.json' 4 | import { PrinterProperties } from './../shared/types'; 5 | 6 | export function encodeFormData(data: { [x: string]: string | number | boolean }): string { 7 | return Object.keys(data) 8 | .map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])) 9 | .join('&') 10 | } 11 | 12 | export function capitalize(string: string) { 13 | return string.charAt(0).toUpperCase() + string.slice(1) 14 | } 15 | 16 | export function initi18n(language?: string) { 17 | const resources = { 18 | en: { 19 | translation: translationsEN, 20 | }, 21 | de: { 22 | translation: translationsDE, 23 | }, 24 | } 25 | // override browserlanguage if language is provided 26 | if (language != '') { 27 | i18next.init({ 28 | resources, 29 | lng: language, 30 | // allow keys to be phrases having `:`, `.` 31 | nsSeparator: false, 32 | fallbackLng: 'en', 33 | }) 34 | } else { 35 | i18next.init({ 36 | resources, 37 | lng: navigator.language, 38 | // allow keys to be phrases having `:`, `.` 39 | nsSeparator: false, 40 | fallbackLng: 'en', 41 | }) 42 | } 43 | } 44 | 45 | export const poll = async ({ fn, validate, interval, maxAttempts }) => { 46 | let attempts = 0 47 | 48 | const executePoll = async (resolve, reject) => { 49 | const result = await fn() 50 | attempts++ 51 | 52 | if (validate(result)) { 53 | return resolve(result) 54 | } else if (maxAttempts && attempts === maxAttempts) { 55 | return reject(new Error('Exceeded max attempts.')) 56 | } else { 57 | setTimeout(executePoll, interval, resolve, reject) 58 | } 59 | } 60 | 61 | return new Promise(executePoll) 62 | } 63 | 64 | export const removeEmptyStrings = (obj: { [x: string]: any }) => { 65 | let newObj = {} 66 | Object.keys(obj).forEach((prop) => { 67 | if (obj[prop] !== '') { 68 | newObj[prop] = obj[prop] 69 | } 70 | }) 71 | return newObj 72 | } 73 | 74 | export const managePaperDimensions = (properties :PrinterProperties)=>{ 75 | if(properties.paperid != PAPER_ID){ 76 | delete properties.paperlength 77 | delete properties.paperwidth 78 | } 79 | 80 | if(properties.paperlength && properties.paperwidth){ 81 | properties.paperlength = +properties.paperlength * 10 82 | properties.paperwidth = +properties.paperwidth * 10 83 | } 84 | 85 | return properties 86 | } 87 | 88 | export const formatPageRange = (pageRange) => { 89 | return pageRange.replace(/,/g, ';') 90 | } 91 | 92 | export const validatePageRange = (pageRange) => { 93 | if (!pageRange) { 94 | return true 95 | } 96 | const regex = /^(\d+(-\d+)?(,\d+(-\d+)?)*|(\d+,\d+(-\d+)?(,\d+(-\d+)?)*)+)$/; 97 | const isValid = regex.test(pageRange); 98 | if (!isValid) { 99 | return false 100 | } 101 | let ranges = pageRange.split(','); 102 | for (let i = 0; i < ranges.length; i++) { 103 | let rng = ranges[i].trim(); 104 | if (rng.includes('-')) { 105 | let [start, end] = rng.split('-'); 106 | start = parseInt(start); 107 | end = parseInt(end); 108 | if (isNaN(start) || isNaN(end) || start > end || start <= 0) { 109 | return false; 110 | } 111 | } else { 112 | let page = parseInt(rng); 113 | if (isNaN(page) || page <= 0) { 114 | return false 115 | } 116 | } 117 | } 118 | return true 119 | } 120 | 121 | export const PAPER_ID = 256 122 | -------------------------------------------------------------------------------- /stencil.config.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | 3 | import { Config } from '@stencil/core' 4 | import { sass } from '@stencil/sass' 5 | import replacePlugin from '@rollup/plugin-replace' 6 | import { angularOutputTarget } from '@stencil/angular-output-target' 7 | import { reactOutputTarget } from '@stencil/react-output-target' 8 | import nodePolyfills from 'rollup-plugin-node-polyfills' 9 | import fs from 'fs' 10 | 11 | export const config: Config = { 12 | namespace: 'ezeep', 13 | globalScript: 'src/shared/global.ts', 14 | plugins: [sass({ includePaths: ['node_modules'] })], 15 | outputTargets: [ 16 | angularOutputTarget({ 17 | componentCorePackage: '@ezeep/ezeep-js', // name of npm package 18 | directivesProxyFile: 'dist/directives/proxies.ts', // output file that gets generated by the outputTarget 19 | }), 20 | // reactOutputTarget({ 21 | // componentCorePackage: '@ezeep/ezeep-js', 22 | // proxiesFile: 'dist/directives/proxies.ts', 23 | // }), 24 | { 25 | type: 'dist', 26 | esmLoaderPath: '../loader', 27 | }, 28 | { 29 | type: 'dist-custom-elements-bundle', 30 | }, 31 | { 32 | type: 'docs-readme', 33 | footer: '', 34 | }, 35 | { 36 | type: 'www', 37 | serviceWorker: null, 38 | copy: [{ src: 'data' }], 39 | }, 40 | ], 41 | // needs to be commented out for build on github actions to work 42 | // devServer: { 43 | // address: process.env.DEV_SERVER_ADDRESS, 44 | // port: parseInt(process.env.DEV_SERVER_PORT), 45 | // https: { 46 | // cert: fs.readFileSync(process.env.DEV_SERVER_HTTPS_CERT, 'utf8'), 47 | // key: fs.readFileSync(process.env.DEV_SERVER_HTTPS_KEY, 'utf8'), 48 | // }, 49 | // }, 50 | rollupPlugins: { 51 | after: [ 52 | nodePolyfills(), 53 | replacePlugin({ 54 | preventAssignment: true, 55 | delimiters: ['<%', '%>'], 56 | }), 57 | ], 58 | }, 59 | buildEs5: 'prod', 60 | } 61 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "allowUnreachableCode": false, 5 | "declaration": false, 6 | "experimentalDecorators": true, 7 | "lib": ["dom", "es2017"], 8 | "moduleResolution": "node", 9 | "module": "esnext", 10 | "target": "es2017", 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "jsx": "react", 14 | "jsxFactory": "h", 15 | "resolveJsonModule": true, 16 | "jsxFragmentFactory": "Fragment" 17 | }, 18 | "include": ["src"], 19 | "exclude": ["node_modules"] 20 | } 21 | --------------------------------------------------------------------------------