Add frontend folder & setup frontend project

This commit is contained in:
Huffle 2025-05-14 13:15:59 +02:00
parent f29eeae9d9
commit 47f43cb0dc
158 changed files with 28017 additions and 0 deletions

View File

@ -0,0 +1,16 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
Chrome >=79
ChromeAndroid >=79
Firefox >=70
Edge >=79
Safari >=14
iOS >=14

View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -0,0 +1,47 @@
{
"root": true,
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/prefer-standalone": "off",
"@angular-eslint/component-class-suffix": [
"error",
{
"suffixes": ["Page", "Component"]
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {}
}
]
}

View File

@ -0,0 +1,70 @@
# Specifies intentionally untracked files to ignore when using Git
# http://git-scm.com/docs/gitignore
*~
*.sw[mnpcod]
.tmp
*.tmp
*.tmp.*
UserInterfaceState.xcuserstate
$RECYCLE.BIN/
*.log
log.txt
/.sourcemaps
/.versions
/coverage
# Ionic
/.ionic
/www
/platforms
/plugins
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-project
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular
/.angular/cache
.sass-cache/
/.nx
/.nx/cache
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

View File

@ -0,0 +1,5 @@
{
"recommendations": [
"ionic.ionic"
]
}

View File

@ -0,0 +1,3 @@
{
"typescript.preferences.autoImportFileExcludePatterns": ["@ionic/angular/common", "@ionic/angular/standalone"]
}

View File

@ -0,0 +1,158 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"app": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "www",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "assets"
},
{
"glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg"
}
],
"styles": [
"@angular/material/prebuilt-themes/azure-blue.css",
"src/global.scss",
"src/theme/variables.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
},
"ci": {
"progress": false
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "app:build:production"
},
"development": {
"buildTarget": "app:build:development"
},
"ci": {
"progress": false
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "src/assets",
"output": "assets"
},
{
"glob": "**/*.svg",
"input": "node_modules/ionicons/dist/ionicons/svg",
"output": "./svg"
}
],
"styles": [
"@angular/material/prebuilt-themes/azure-blue.css",
"src/global.scss",
"src/theme/variables.scss"
],
"scripts": []
},
"configurations": {
"ci": {
"progress": false,
"watch": false
}
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"cli": {
"schematicCollections": [
"@ionic/angular-toolkit"
],
"analytics": false
},
"schematics": {
"@ionic/angular-toolkit:component": {
"styleext": "scss"
},
"@ionic/angular-toolkit:page": {
"styleext": "scss"
}
}
}

View File

@ -0,0 +1,9 @@
import type { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'io.ionic.starter',
appName: 'allowance-planner-v2',
webDir: 'www'
};
export default config;

View File

@ -0,0 +1,7 @@
{
"name": "allowance-planner-v2",
"integrations": {
"capacitor": {}
},
"type": "angular"
}

View File

@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/app'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
{
"name": "allowance-planner-v2",
"version": "0.0.1",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"lint": "ng lint"
},
"private": true,
"dependencies": {
"@angular/animations": "^19.0.0",
"@angular/cdk": "^19.2.15",
"@angular/common": "^19.0.0",
"@angular/compiler": "^19.0.0",
"@angular/core": "^19.0.0",
"@angular/forms": "^19.0.0",
"@angular/material": "^19.2.15",
"@angular/platform-browser": "^19.0.0",
"@angular/platform-browser-dynamic": "^19.0.0",
"@angular/router": "^19.0.0",
"@capacitor/app": "7.0.1",
"@capacitor/core": "7.2.0",
"@capacitor/haptics": "7.0.1",
"@capacitor/keyboard": "7.0.1",
"@capacitor/status-bar": "7.0.1",
"@ionic/angular": "^8.0.0",
"@ionic/pwa-elements": "^3.3.0",
"ionicons": "^7.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.0.0",
"@angular-eslint/builder": "^19.0.0",
"@angular-eslint/eslint-plugin": "^19.0.0",
"@angular-eslint/eslint-plugin-template": "^19.0.0",
"@angular-eslint/schematics": "^19.0.0",
"@angular-eslint/template-parser": "^19.0.0",
"@angular/cli": "^19.0.0",
"@angular/compiler-cli": "^19.0.0",
"@angular/language-service": "^19.0.0",
"@capacitor/cli": "7.2.0",
"@ionic/angular-toolkit": "^12.0.0",
"@types/jasmine": "~5.1.0",
"@typescript-eslint/eslint-plugin": "^8.18.0",
"@typescript-eslint/parser": "^8.18.0",
"eslint": "^9.16.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsdoc": "^48.2.1",
"eslint-plugin-prefer-arrow": "1.2.2",
"jasmine-core": "~5.1.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.6.3"
},
"description": "An Ionic project"
}

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AllowancePage } from './allowance.page';
const routes: Routes = [
{
path: '',
component: AllowancePage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AllowancePageRoutingModule {}

View File

@ -0,0 +1,18 @@
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { AllowancePage } from './allowance.page';
import { AllowancePageRoutingModule } from './allowance-routing.module';
@NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
AllowancePageRoutingModule
],
declarations: [AllowancePage]
})
export class AllowancePageModule {}

View File

@ -0,0 +1,15 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Allowance
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Allowance</ion-title>
</ion-toolbar>
</ion-header>
</ion-content>

View File

@ -0,0 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
import { AllowancePage } from './allowance.page';
describe('AllowancePage', () => {
let component: AllowancePage;
let fixture: ComponentFixture<AllowancePage>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AllowancePage],
imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
}).compileComponents();
fixture = TestBed.createComponent(AllowancePage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-allowance',
templateUrl: 'allowance.page.html',
styleUrls: ['allowance.page.scss'],
standalone: false,
})
export class AllowancePage {
constructor() {}
}

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
}
];
@NgModule({
imports: [
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule {}

View File

@ -0,0 +1,3 @@
<ion-app>
<ion-router-outlet></ion-router-outlet>
</ion-app>

View File

@ -0,0 +1,21 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
});

View File

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
standalone: false,
})
export class AppComponent {
constructor() {}
}

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
bootstrap: [AppComponent],
})
export class AppModule {}

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HistoryPage } from './history.page';
const routes: Routes = [
{
path: '',
component: HistoryPage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class HistoryPageRoutingModule {}

View File

@ -0,0 +1,18 @@
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HistoryPage } from './history.page';
import { HistoryPageRoutingModule } from './history-routing.module';
@NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
HistoryPageRoutingModule
],
declarations: [HistoryPage]
})
export class HistoryPageModule {}

View File

@ -0,0 +1,15 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
History
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">History</ion-title>
</ion-toolbar>
</ion-header>
</ion-content>

View File

@ -0,0 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
import { HistoryPage } from './history.page';
describe('HistoryPage', () => {
let component: HistoryPage;
let fixture: ComponentFixture<HistoryPage>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HistoryPage],
imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
}).compileComponents();
fixture = TestBed.createComponent(HistoryPage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-history',
templateUrl: 'history.page.html',
styleUrls: ['history.page.scss'],
standalone: false,
})
export class HistoryPage {
constructor() {}
}

View File

@ -0,0 +1,39 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabsPage } from './tabs.page';
const routes: Routes = [
{
path: 'tabs',
component: TabsPage,
children: [
{
path: 'history',
loadChildren: () => import('../history/history.module').then(m => m.HistoryPageModule)
},
{
path: 'allowance',
loadChildren: () => import('../allowance/allowance.module').then(m => m.AllowancePageModule)
},
{
path: 'tasks',
loadChildren: () => import('../tasks/tasks.module').then(m => m.TasksPageModule)
},
{
path: '',
redirectTo: '/tabs/allowance',
pathMatch: 'full'
}
]
},
{
path: '',
redirectTo: '/tabs/allowance',
pathMatch: 'full'
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
})
export class TabsPageRoutingModule {}

View File

@ -0,0 +1,21 @@
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {MatIconModule} from '@angular/material/icon';
import { TabsPageRoutingModule } from './tabs-routing.module';
import { TabsPage } from './tabs.page';
@NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
TabsPageRoutingModule,
MatIconModule,
],
declarations: [TabsPage]
})
export class TabsPageModule {}

View File

@ -0,0 +1,17 @@
<ion-tabs>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="history" href="/tabs/history">
<mat-icon>history</mat-icon>
</ion-tab-button>
<ion-tab-button tab="allowance" href="/tabs/allowance">
<mat-icon>savings</mat-icon>
</ion-tab-button>
<ion-tab-button tab="tasks" href="/tabs/tasks">
<mat-icon>task_alt</mat-icon>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,26 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TabsPage } from './tabs.page';
describe('TabsPage', () => {
let component: TabsPage;
let fixture: ComponentFixture<TabsPage>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TabsPage],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TabsPage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-tabs',
templateUrl: 'tabs.page.html',
styleUrls: ['tabs.page.scss'],
standalone: false,
})
export class TabsPage {
constructor() {}
}

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TasksPage } from './tasks.page';
const routes: Routes = [
{
path: '',
component: TasksPage,
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TasksPageRoutingModule {}

View File

@ -0,0 +1,18 @@
import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TasksPage } from './tasks.page';
import { TasksPageRoutingModule } from './tasks-routing.module';
@NgModule({
imports: [
IonicModule,
CommonModule,
FormsModule,
TasksPageRoutingModule
],
declarations: [TasksPage]
})
export class TasksPageModule {}

View File

@ -0,0 +1,15 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
Tasks
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large">Tasks</ion-title>
</ion-toolbar>
</ion-header>
</ion-content>

View File

@ -0,0 +1,26 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
import { TasksPage } from './tasks.page';
describe('TasksPage', () => {
let component: TasksPage;
let fixture: ComponentFixture<TasksPage>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TasksPage],
imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
}).compileComponents();
fixture = TestBed.createComponent(TasksPage);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-tasks',
templateUrl: 'tasks.page.html',
styleUrls: ['tasks.page.scss'],
standalone: false,
})
export class TasksPage {
constructor() {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 930 B

View File

@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.

View File

@ -0,0 +1,37 @@
/*
* App Global CSS
* ----------------------------------------------------------------------------
* Put style rules here that you want to apply globally. These styles are for
* the entire app and not just one component. Additionally, this file can be
* used as an entry point to import other CSS/Sass files to be included in the
* output CSS.
* For more information on global stylesheets, visit the documentation:
* https://ionicframework.com/docs/layout/global-stylesheets
*/
/* Core CSS required for Ionic components to work properly */
@import "@ionic/angular/css/core.css";
/* Basic CSS for apps built with Ionic */
@import "@ionic/angular/css/normalize.css";
@import "@ionic/angular/css/structure.css";
@import "@ionic/angular/css/typography.css";
@import "@ionic/angular/css/display.css";
/* Optional CSS utils that can be commented out */
@import "@ionic/angular/css/padding.css";
@import "@ionic/angular/css/float-elements.css";
@import "@ionic/angular/css/text-alignment.css";
@import "@ionic/angular/css/text-transformation.css";
@import "@ionic/angular/css/flex-utils.css";
/**
* Ionic Dark Mode
* -----------------------------------------------------
* For more info, please see:
* https://ionicframework.com/docs/theming/dark-mode
*/
/* @import "@ionic/angular/css/palettes/dark.always.css"; */
/* @import "@ionic/angular/css/palettes/dark.class.css"; */
@import "@ionic/angular/css/palettes/dark.system.css";

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Ionic App</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -0,0 +1,9 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { defineCustomElements } from '@ionic/pwa-elements/loader';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
// Call the element loader before the bootstrapModule/bootstrapApplication call
defineCustomElements(window);

View File

@ -0,0 +1,55 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes recent versions of Safari, Chrome (including
* Opera), Edge on the desktop, and iOS and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
import './zone-flags';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@ -0,0 +1,14 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);

View File

@ -0,0 +1,2 @@
// For information on how to create your own theme, please see:
// http://ionicframework.com/docs/theming/

View File

@ -0,0 +1,6 @@
/**
* Prevents Angular change detection from
* running with certain Web Component callbacks
*/
// eslint-disable-next-line no-underscore-dangle
(window as any).__Zone_disable_customElements = true;

View File

@ -0,0 +1,15 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}

View File

@ -0,0 +1,33 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2022",
"module": "es2020",
"lib": [
"es2018",
"dom"
],
"useDefineForClassFields": false
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@ -0,0 +1,18 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

16
frontend/node_modules/.package-lock.json generated vendored Normal file
View File

@ -0,0 +1,16 @@
{
"name": "frontend",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@ionic/pwa-elements": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@ionic/pwa-elements/-/pwa-elements-3.3.0.tgz",
"integrity": "sha512-vbykpxd2nGRlA67AnqDwsiVf8PUmInLyi6lQdnPDjeiML1WZa0CPe6r632nGDV9PTi+sWNde9Xexg9SD6Pwyqw==",
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
}
}
}
}

21
frontend/node_modules/@ionic/pwa-elements/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Ionic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
'use strict';

View File

@ -0,0 +1,26 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
/*
Stencil Client Patch Browser v3.4.0 | MIT Licensed | https://stenciljs.com
*/
const patchBrowser = () => {
const importMeta = (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('ionicpwaelements.cjs.js', document.baseURI).href));
const opts = {};
// TODO(STENCIL-663): Remove code related to deprecated `safari10` field.
if (importMeta !== '') {
opts.resourcesUrl = new URL('.', importMeta).href;
// TODO(STENCIL-661): Remove code related to the dynamic import shim
// TODO(STENCIL-663): Remove code related to deprecated `safari10` field.
}
return index.promiseResolve(opts);
};
patchBrowser().then(options => {
return index.bootstrapLazy([["pwa-camera-modal.cjs",[[1,"pwa-camera-modal",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"present":[64],"dismiss":[64]}]]],["pwa-action-sheet.cjs",[[1,"pwa-action-sheet",{"header":[1],"cancelable":[4],"options":[16],"open":[32]}]]],["pwa-toast.cjs",[[1,"pwa-toast",{"message":[1],"duration":[2],"closing":[32]}]]],["pwa-camera.cjs",[[1,"pwa-camera",{"facingMode":[1,"facing-mode"],"handlePhoto":[16],"hidePicker":[4,"hide-picker"],"handleNoDeviceError":[16],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"],"photo":[32],"photoSrc":[32],"showShutterOverlay":[32],"flashIndex":[32],"hasCamera":[32],"rotation":[32],"deviceError":[32]}]]],["pwa-camera-modal-instance.cjs",[[1,"pwa-camera-modal-instance",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"]},[[16,"keyup","handleBackdropKeyUp"]]]]]], options);
});
exports.setNonce = index.setNonce;

View File

@ -0,0 +1,22 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
/*
Stencil Client Patch Esm v3.4.0 | MIT Licensed | https://stenciljs.com
*/
const patchEsm = () => {
return index.promiseResolve();
};
const defineCustomElements = (win, options) => {
if (typeof window === 'undefined') return Promise.resolve();
return patchEsm().then(() => {
return index.bootstrapLazy([["pwa-camera-modal.cjs",[[1,"pwa-camera-modal",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"present":[64],"dismiss":[64]}]]],["pwa-action-sheet.cjs",[[1,"pwa-action-sheet",{"header":[1],"cancelable":[4],"options":[16],"open":[32]}]]],["pwa-toast.cjs",[[1,"pwa-toast",{"message":[1],"duration":[2],"closing":[32]}]]],["pwa-camera.cjs",[[1,"pwa-camera",{"facingMode":[1,"facing-mode"],"handlePhoto":[16],"hidePicker":[4,"hide-picker"],"handleNoDeviceError":[16],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"],"photo":[32],"photoSrc":[32],"showShutterOverlay":[32],"flashIndex":[32],"hasCamera":[32],"rotation":[32],"deviceError":[32]}]]],["pwa-camera-modal-instance.cjs",[[1,"pwa-camera-modal-instance",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"]},[[16,"keyup","handleBackdropKeyUp"]]]]]], options);
});
};
exports.setNonce = index.setNonce;
exports.defineCustomElements = defineCustomElements;

View File

@ -0,0 +1,46 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
const actionSheetCss = ":host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:-apple-system, BlinkMacSystemFont, \"Helvetica Neue\", \"Roboto\", sans-serif}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0);-webkit-transition:400ms background-color cubic-bezier(.36,.66,.04,1);transition:400ms background-color cubic-bezier(.36,.66,.04,1)}.wrapper.open{background-color:rgba(0, 0, 0, 0.32)}.title{color:#999;height:23px;line-height:23px;padding-bottom:17px;-webkit-padding-end:16px;padding-inline-end:16px;-webkit-padding-start:16px;padding-inline-start:16px;padding-left:16px;padding-right:16px;padding-top:20px}.content{width:568px;-ms-flex-item-align:end;align-self:flex-end;background-color:#fff;-webkit-transition:400ms -webkit-transform cubic-bezier(.36,.66,.04,1);transition:400ms -webkit-transform cubic-bezier(.36,.66,.04,1);transition:400ms transform cubic-bezier(.36,.66,.04,1);transition:400ms transform cubic-bezier(.36,.66,.04,1), 400ms -webkit-transform cubic-bezier(.36,.66,.04,1);-webkit-transform:translateY(100%);transform:translateY(100%)}.wrapper.open .content{-webkit-transform:translateY(0%);transform:translateY(0%)}@media only screen and (max-width: 568px){.content{width:100%}}.action-sheet-option{cursor:pointer;height:52px;line-height:52px}.action-sheet-button{color:rgb(38, 38, 38);font-size:16px;-webkit-padding-end:16px;padding-inline-end:16px;-webkit-padding-start:16px;padding-inline-start:16px;padding-left:16px;padding-right:16px;padding-top:0px}.action-sheet-button:hover{background-color:#F6F6F6}";
const PWAActionSheet = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.onSelection = index.createEvent(this, "onSelection", 7);
this.header = undefined;
this.cancelable = true;
this.options = [];
this.open = false;
}
componentDidLoad() {
requestAnimationFrame(() => {
this.open = true;
});
}
dismiss() {
if (this.cancelable) {
this.close();
}
}
close() {
this.open = false;
setTimeout(() => {
this.el.parentNode.removeChild(this.el);
}, 500);
}
handleOptionClick(e, i) {
e.stopPropagation();
this.onSelection.emit(i);
this.close();
}
render() {
return (index.h("div", { class: `wrapper${this.open ? ' open' : ''}`, onClick: () => this.dismiss() }, index.h("div", { class: "content" }, index.h("div", { class: "title" }, this.header), this.options.map((option, i) => index.h("div", { class: "action-sheet-option", onClick: (e) => this.handleOptionClick(e, i) }, index.h("div", { class: "action-sheet-button" }, option.title))))));
}
get el() { return index.getElement(this); }
};
PWAActionSheet.style = actionSheetCss;
exports.pwa_action_sheet = PWAActionSheet;

View File

@ -0,0 +1,45 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
const cameraModalInstanceCss = ":host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict;--inset-width:600px;--inset-height:600px}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0.15)}.content{-webkit-box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);width:var(--inset-width);height:var(--inset-height);max-height:100%}@media only screen and (max-width: 600px){.content{width:100%;height:100%}}";
const PWACameraModal = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.onPhoto = index.createEvent(this, "onPhoto", 7);
this.noDeviceError = index.createEvent(this, "noDeviceError", 7);
this.handlePhoto = async (photo) => {
this.onPhoto.emit(photo);
};
this.handleNoDeviceError = async (photo) => {
this.noDeviceError.emit(photo);
};
this.facingMode = 'user';
this.hidePicker = false;
this.noDevicesText = 'No camera found';
this.noDevicesButtonText = 'Choose image';
}
handleBackdropClick(e) {
if (e.target !== this.el) {
this.onPhoto.emit(null);
}
}
handleComponentClick(e) {
e.stopPropagation();
}
handleBackdropKeyUp(e) {
if (e.key === "Escape") {
this.onPhoto.emit(null);
}
}
render() {
return (index.h("div", { class: "wrapper", onClick: e => this.handleBackdropClick(e) }, index.h("div", { class: "content" }, index.h("pwa-camera", { onClick: e => this.handleComponentClick(e), facingMode: this.facingMode, hidePicker: this.hidePicker, handlePhoto: this.handlePhoto, handleNoDeviceError: this.handleNoDeviceError, noDevicesButtonText: this.noDevicesButtonText, noDevicesText: this.noDevicesText }))));
}
get el() { return index.getElement(this); }
};
PWACameraModal.style = cameraModalInstanceCss;
exports.pwa_camera_modal_instance = PWACameraModal;

View File

@ -0,0 +1,47 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
const cameraModalCss = ":host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0.15)}.content{-webkit-box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);width:600px;height:600px}";
const PWACameraModal = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.onPhoto = index.createEvent(this, "onPhoto", 7);
this.noDeviceError = index.createEvent(this, "noDeviceError", 7);
this.facingMode = 'user';
this.hidePicker = false;
}
async present() {
const camera = document.createElement('pwa-camera-modal-instance');
camera.facingMode = this.facingMode;
camera.hidePicker = this.hidePicker;
camera.addEventListener('onPhoto', async (e) => {
if (!this._modal) {
return;
}
const photo = e.detail;
this.onPhoto.emit(photo);
});
camera.addEventListener('noDeviceError', async (e) => {
this.noDeviceError.emit(e);
});
document.body.append(camera);
this._modal = camera;
}
async dismiss() {
if (!this._modal) {
return;
}
this._modal && this._modal.parentNode.removeChild(this._modal);
this._modal = null;
}
render() {
return (index.h("div", null));
}
};
PWACameraModal.style = cameraModalCss;
exports.pwa_camera_modal = PWACameraModal;

View File

@ -0,0 +1,485 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
/**
* MediaStream ImageCapture polyfill
*
* @license
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let ImageCapture = window.ImageCapture;
if (typeof ImageCapture === 'undefined') {
ImageCapture = class {
/**
* TODO https://www.w3.org/TR/image-capture/#constructors
*
* @param {MediaStreamTrack} videoStreamTrack - A MediaStreamTrack of the 'video' kind
*/
constructor(videoStreamTrack) {
if (videoStreamTrack.kind !== 'video')
throw new DOMException('NotSupportedError');
this._videoStreamTrack = videoStreamTrack;
if (!('readyState' in this._videoStreamTrack)) {
// Polyfill for Firefox
this._videoStreamTrack.readyState = 'live';
}
// MediaStream constructor not available until Chrome 55 - https://www.chromestatus.com/feature/5912172546752512
this._previewStream = new MediaStream([videoStreamTrack]);
this.videoElement = document.createElement('video');
this.videoElementPlaying = new Promise(resolve => {
this.videoElement.addEventListener('playing', resolve);
});
if (HTMLMediaElement) {
this.videoElement.srcObject = this._previewStream; // Safari 11 doesn't allow use of createObjectURL for MediaStream
}
else {
this.videoElement.src = URL.createObjectURL(this._previewStream);
}
this.videoElement.muted = true;
this.videoElement.setAttribute('playsinline', ''); // Required by Safari on iOS 11. See https://webkit.org/blog/6784
this.videoElement.play();
this.canvasElement = document.createElement('canvas');
// TODO Firefox has https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
this.canvas2dContext = this.canvasElement.getContext('2d');
}
/**
* https://w3c.github.io/mediacapture-image/index.html#dom-imagecapture-videostreamtrack
* @return {MediaStreamTrack} The MediaStreamTrack passed into the constructor
*/
get videoStreamTrack() {
return this._videoStreamTrack;
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-getphotocapabilities
* @return {Promise<PhotoCapabilities>} Fulfilled promise with
* [PhotoCapabilities](https://www.w3.org/TR/image-capture/#idl-def-photocapabilities)
* object on success, rejected promise on failure
*/
getPhotoCapabilities() {
return new Promise(function executorGPC(resolve, reject) {
// TODO see https://github.com/w3c/mediacapture-image/issues/97
const MediaSettingsRange = {
current: 0, min: 0, max: 0,
};
resolve({
exposureCompensation: MediaSettingsRange,
exposureMode: 'none',
fillLightMode: ['none'],
focusMode: 'none',
imageHeight: MediaSettingsRange,
imageWidth: MediaSettingsRange,
iso: MediaSettingsRange,
redEyeReduction: false,
whiteBalanceMode: 'none',
zoom: MediaSettingsRange,
});
reject(new DOMException('OperationError'));
});
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-setoptions
* @param {Object} photoSettings - Photo settings dictionary, https://www.w3.org/TR/image-capture/#idl-def-photosettings
* @return {Promise<void>} Fulfilled promise on success, rejected promise on failure
*/
setOptions(_photoSettings = {}) {
return new Promise(function executorSO(_resolve, _reject) {
// TODO
});
}
/**
* TODO
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-takephoto
* @return {Promise<Blob>} Fulfilled promise with [Blob](https://www.w3.org/TR/FileAPI/#blob)
* argument on success; rejected promise on failure
*/
takePhoto() {
const self = this;
return new Promise(function executorTP(resolve, reject) {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
self.canvasElement.toBlob(resolve);
}
catch (error) {
reject(new DOMException('UnknownError'));
}
});
});
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-grabframe
* @return {Promise<ImageBitmap>} Fulfilled promise with
* [ImageBitmap](https://www.w3.org/TR/html51/webappapis.html#webappapis-images)
* argument on success; rejected promise on failure
*/
grabFrame() {
const self = this;
return new Promise(function executorGF(resolve, reject) {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
// TODO polyfill https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapFactories/createImageBitmap for IE
resolve(window.createImageBitmap(self.canvasElement));
}
catch (error) {
reject(new DOMException('UnknownError'));
}
});
});
}
};
}
window.ImageCapture = ImageCapture;
const cameraCss = ":host{--header-height:4em;--footer-height:9em;--header-height-landscape:3em;--footer-height-landscape:6em;--shutter-size:6em;--icon-size-header:1.5em;--icon-size-footer:2.5em;--margin-size-header:1.5em;--margin-size-footer:2.0em;font-family:-apple-system, BlinkMacSystemFont,\n “Segoe UI”, “Roboto”, “Droid Sans”, “Helvetica Neue”, sans-serif;display:block;width:100%;height:100%}.items{-webkit-box-sizing:border-box;box-sizing:border-box;display:-ms-flexbox;display:flex;width:100%;height:100%;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.items .item{-ms-flex:1;flex:1;text-align:center}.items .item:first-child{text-align:left}.items .item:last-child{text-align:right}.camera-wrapper{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;height:100%}.camera-header{color:white;background-color:black;height:var(--header-height)}.camera-header .items{padding:var(--margin-size-header)}.camera-footer{position:relative;color:white;background-color:black;height:var(--footer-height)}.camera-footer .items{padding:var(--margin-size-footer)}@media (max-height: 375px){.camera-header{--header-height:var(--header-height-landscape)}.camera-footer{--footer-height:var(--footer-height-landscape)}.camera-footer .shutter{--shutter-size:4em}}.camera-video{position:relative;-ms-flex:1;flex:1;overflow:hidden;background-color:black}video{width:100%;height:100%;max-height:100%;min-height:100%;-o-object-fit:cover;object-fit:cover;background-color:black}.pick-image{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;position:absolute;left:var(--margin-size-footer);top:0;height:100%;width:var(--icon-size-footer);color:white}.pick-image input{visibility:hidden}.pick-image svg{cursor:pointer;fill:white;width:var(--icon-size-footer);height:var(--icon-size-footer)}.shutter{position:absolute;left:50%;top:50%;width:var(--shutter-size);height:var(--shutter-size);margin-top:calc(var(--shutter-size) / -2);margin-left:calc(var(--shutter-size) / -2);border-radius:100%;background-color:#c6cdd8;padding:12px;-webkit-box-sizing:border-box;box-sizing:border-box}.shutter:active .shutter-button{background-color:#9da9bb}.shutter-button{background-color:white;border-radius:100%;width:100%;height:100%}.rotate{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;position:absolute;right:var(--margin-size-footer);top:0;height:100%;width:var(--icon-size-footer);color:white}.rotate img{width:var(--icon-size-footer);height:var(--icon-size-footer)}.shutter-overlay{z-index:5;position:absolute;width:100%;height:100%;background-color:black}.error{width:100%;height:100%;color:white;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.no-device{background-color:black;-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;color:white}.no-device label{cursor:pointer;background:#fff;border-radius:6px;padding:6px 8px;color:black}.no-device input{visibility:hidden;height:0;margin-top:16px}.accept{background-color:black;-ms-flex:1;flex:1;overflow:hidden}.accept .accept-image{width:100%;height:100%;max-height:100%;background-position:center center;background-size:cover;background-repeat:no-repeat}.close img{cursor:pointer;width:var(--icon-size-header);height:var(--icon-size-header)}.flash img{width:var(--icon-size-header);height:var(--icon-size-header)}.accept-use img{width:var(--icon-size-footer);height:var(--icon-size-footer)}.accept-cancel img{width:var(--icon-size-footer);height:var(--icon-size-footer)}.offscreen-image-render{top:0;left:0;visibility:hidden;pointer-events:none;width:100%;height:100%}";
const CameraPWA = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
// Whether the device has multiple cameras (front/back)
this.hasMultipleCameras = false;
// Whether the device has flash support
this.hasFlash = false;
// Flash modes for camera
this.flashModes = [];
// Current flash mode
this.flashMode = 'off';
this.handlePickFile = (_e) => {
};
this.handleShutterClick = (_e) => {
console.debug('shutter click');
this.capture();
};
this.handleRotateClick = (_e) => {
this.rotate();
};
this.handleClose = (_e) => {
this.handlePhoto && this.handlePhoto(null);
};
this.handleFlashClick = (_e) => {
this.cycleFlash();
};
this.handleCancelPhoto = (_e) => {
const track = this.stream && this.stream.getTracks()[0];
let c = track && track.getConstraints();
this.photo = null;
this.photoSrc = null;
if (c) {
this.initCamera({
video: {
facingMode: c.facingMode
}
});
}
else {
this.initCamera();
}
};
this.handleAcceptPhoto = (_e) => {
this.handlePhoto && this.handlePhoto(this.photo);
};
this.handleFileInputChange = async (e) => {
const input = e.target;
const file = input.files[0];
try {
const orientation = await this.getOrientation(file);
console.debug('Got orientation', orientation);
this.photoOrientation = orientation;
}
catch (e) {
}
this.handlePhoto && this.handlePhoto(file);
};
this.handleVideoMetadata = (e) => {
console.debug('Video metadata', e);
};
this.facingMode = 'user';
this.handlePhoto = undefined;
this.hidePicker = false;
this.handleNoDeviceError = undefined;
this.noDevicesText = 'No camera found';
this.noDevicesButtonText = 'Choose image';
this.photo = undefined;
this.photoSrc = undefined;
this.showShutterOverlay = false;
this.flashIndex = 0;
this.hasCamera = null;
this.rotation = 0;
this.deviceError = null;
}
async componentDidLoad() {
this.defaultConstraints = {
video: {
facingMode: this.facingMode
}
};
// Figure out how many cameras we have
await this.queryDevices();
// Initialize the camera
await this.initCamera();
}
disconnectedCallback() {
this.stopStream();
this.photoSrc && URL.revokeObjectURL(this.photoSrc);
}
hasImageCapture() {
return 'ImageCapture' in window;
}
/**
* Query the list of connected devices and figure out how many video inputs we have.
*/
async queryDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(d => d.kind == 'videoinput');
this.hasCamera = !!videoDevices.length;
this.hasMultipleCameras = videoDevices.length > 1;
}
catch (e) {
this.deviceError = e;
}
}
async initCamera(constraints) {
if (!constraints) {
constraints = this.defaultConstraints;
}
try {
const stream = await navigator.mediaDevices.getUserMedia(Object.assign({ video: true, audio: false }, constraints));
this.initStream(stream);
}
catch (e) {
this.deviceError = e;
this.handleNoDeviceError && this.handleNoDeviceError(e);
}
}
async initStream(stream) {
this.stream = stream;
this.videoElement.srcObject = stream;
if (this.hasImageCapture()) {
this.imageCapture = new window.ImageCapture(stream.getVideoTracks()[0]);
await this.initPhotoCapabilities(this.imageCapture);
}
else {
this.deviceError = 'No image capture';
this.handleNoDeviceError && this.handleNoDeviceError();
}
// Always re-render
index.forceUpdate(this.el);
}
async initPhotoCapabilities(imageCapture) {
const c = await imageCapture.getPhotoCapabilities();
if (c.fillLightMode && c.fillLightMode.length > 1) {
this.flashModes = c.fillLightMode.map(m => m);
// Try to recall the current flash mode
if (this.flashMode) {
this.flashMode = this.flashModes[this.flashModes.indexOf(this.flashMode)] || 'off';
this.flashIndex = this.flashModes.indexOf(this.flashMode) || 0;
}
else {
this.flashIndex = 0;
}
}
}
stopStream() {
if (this.videoElement) {
this.videoElement.srcObject = null;
}
this.stream && this.stream.getTracks().forEach(track => track.stop());
}
async capture() {
if (this.hasImageCapture()) {
try {
const photo = await this.imageCapture.takePhoto({
fillLightMode: this.flashModes.length > 1 ? this.flashMode : undefined
});
await this.flashScreen();
this.promptAccept(photo);
}
catch (e) {
console.error('Unable to take photo!', e);
}
}
this.stopStream();
}
async promptAccept(photo) {
this.photo = photo;
const orientation = await this.getOrientation(photo);
console.debug('Got orientation', orientation);
this.photoOrientation = orientation;
if (orientation) {
switch (orientation) {
case 1:
case 2:
this.rotation = 0;
break;
case 3:
case 4:
this.rotation = 180;
break;
case 5:
case 6:
this.rotation = 90;
break;
case 7:
case 8:
this.rotation = 270;
break;
}
}
this.photoSrc = URL.createObjectURL(photo);
}
getOrientation(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = (event) => {
const view = new DataView(event.target.result);
if (view.getUint16(0, false) !== 0xFFD8) {
return resolve(-2);
}
const length = view.byteLength;
let offset = 2;
while (offset < length) {
const marker = view.getUint16(offset, false);
offset += 2;
if (marker === 0xFFE1) {
if (view.getUint32(offset += 2, false) !== 0x45786966) {
return resolve(-1);
}
const little = view.getUint16(offset += 6, false) === 0x4949;
offset += view.getUint32(offset + 4, little);
const tags = view.getUint16(offset, little);
offset += 2;
for (let i = 0; i < tags; i++) {
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
return resolve(view.getUint16(offset + (i * 12) + 8, little));
}
}
}
else if ((marker & 0xFF00) !== 0xFF00) {
break;
}
else {
offset += view.getUint16(offset, false);
}
}
return resolve(-1);
};
reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
});
}
rotate() {
this.stopStream();
const track = this.stream && this.stream.getTracks()[0];
if (!track) {
return;
}
let c = track.getConstraints();
let facingMode = c.facingMode;
if (!facingMode) {
let c = track.getCapabilities();
if (c.facingMode) {
facingMode = c.facingMode[0];
}
}
if (facingMode === 'environment') {
this.initCamera({
video: {
facingMode: 'user'
}
});
}
else {
this.initCamera({
video: {
facingMode: 'environment'
}
});
}
}
setFlashMode(mode) {
console.debug('New flash mode: ', mode);
this.flashMode = mode;
}
cycleFlash() {
if (this.flashModes.length > 0) {
this.flashIndex = (this.flashIndex + 1) % this.flashModes.length;
this.setFlashMode(this.flashModes[this.flashIndex]);
}
}
async flashScreen() {
return new Promise((resolve, _reject) => {
this.showShutterOverlay = true;
setTimeout(() => {
this.showShutterOverlay = false;
resolve();
}, 100);
});
}
iconExit() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Cg id='Icon_5_'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M402.2,134L378,109.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L139.6,109.8 c-1.6-1.6-4.1-1.6-5.7,0L109.8,134c-1.6,1.6-1.6,4.1,0,5.7l113.5,113.5c1.6,1.6,1.6,4.1,0,5.7L109.8,372.4c-1.6,1.6-1.6,4.1,0,5.7 l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l113.5-113.5c1.6-1.6,4.1-1.6,5.7,0l113.5,113.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1 c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l113.5-113.5C403.7,138.1,403.7,135.5,402.2,134z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconPhotos() {
return (index.h("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '512', height: '512', viewBox: '0 0 512 512' }, index.h("path", { d: 'M450.29,112H142c-34,0-62,27.51-62,61.33V418.67C80,452.49,108,480,142,480H450c34,0,62-26.18,62-60V173.33C512,139.51,484.32,112,450.29,112Zm-77.15,61.34a46,46,0,1,1-46.28,46A46.19,46.19,0,0,1,373.14,173.33Zm-231.55,276c-17,0-29.86-13.75-29.86-30.66V353.85l90.46-80.79a46.54,46.54,0,0,1,63.44,1.83L328.27,337l-113,112.33ZM480,418.67a30.67,30.67,0,0,1-30.71,30.66H259L376.08,333a46.24,46.24,0,0,1,59.44-.16L480,370.59Z' }), index.h("path", { d: 'M384,32H64A64,64,0,0,0,0,96V352a64.11,64.11,0,0,0,48,62V152a72,72,0,0,1,72-72H446A64.11,64.11,0,0,0,384,32Z' })));
}
iconConfirm() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Ccircle fill='%232CD865' cx='256' cy='256' r='256'/%3E%3Cg id='Icon_1_'%3E%3Cg%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M208,301.4l-55.4-55.5c-1.5-1.5-4-1.6-5.6-0.1l-23.4,22.3c-1.6,1.6-1.7,4.1-0.1,5.7l81.6,81.4 c3.1,3.1,8.2,3.1,11.3,0l171.8-171.7c1.6-1.6,1.6-4.2-0.1-5.7l-23.4-22.3c-1.6-1.5-4.1-1.5-5.6,0.1L213.7,301.4 C212.1,303,209.6,303,208,301.4z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconReverseCamera() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M352,0H160C72,0,0,72,0,160v192c0,88,72,160,160,160h192c88,0,160-72,160-160V160C512,72,440,0,352,0z M356.7,365.8l-3.7,3.3c-27,23.2-61.4,35.9-96.8,35.9c-72.4,0-135.8-54.7-147-125.6c-0.3-1.9-2-3.3-3.9-3.3H64 c-3.3,0-5.2-3.8-3.2-6.4l61.1-81.4c1.6-2.1,4.7-2.1,6.4-0.1l63.3,81.4c2,2.6,0.2,6.5-3.2,6.5h-40.6c-2.5,0-4.5,2.4-3.9,4.8 c11.5,51.5,59.2,90.6,112.4,90.6c26.4,0,51.8-9.7,73.7-27.9l3.1-2.5c1.6-1.3,3.9-1.1,5.3,0.3l18.5,18.6 C358.5,361.6,358.4,364.3,356.7,365.8z M451.4,245.6l-61,83.5c-1.6,2.2-4.8,2.2-6.4,0.1l-63.3-83.3c-2-2.6-0.1-6.4,3.2-6.4h40.8 c2.5,0,4.4-2.3,3.9-4.8c-5.1-24.2-17.8-46.5-36.5-63.7c-21.2-19.4-48.2-30.1-76-30.1c-26.5,0-52.6,9.7-73.7,27.3l-3.1,2.5 c-1.6,1.3-3.9,1.2-5.4-0.3l-18.5-18.5c-1.6-1.6-1.5-4.3,0.2-5.9l3.5-3.1c27-23.2,61.4-35.9,96.8-35.9c38,0,73.9,13.7,101.2,38.7 c23.2,21.1,40.3,55.2,45.7,90.1c0.3,1.9,1.9,3.4,3.9,3.4h41.3C451.4,239.2,453.3,243,451.4,245.6z'/%3E%3C/g%3E%3C/svg%3E`;
}
iconRetake() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Ccircle fill='%23727A87' cx='256' cy='256' r='256'/%3E%3Cg id='Icon_5_'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M394.2,142L370,117.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L147.6,117.8 c-1.6-1.6-4.1-1.6-5.7,0L117.8,142c-1.6,1.6-1.6,4.1,0,5.7l105.5,105.5c1.6,1.6,1.6,4.1,0,5.7L117.8,364.4c-1.6,1.6-1.6,4.1,0,5.7 l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l105.5-105.5c1.6-1.6,4.1-1.6,5.7,0l105.5,105.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1 c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l105.5-105.5C395.7,146.1,395.7,143.5,394.2,142z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconFlashOff() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M498,483.7L42.3,28L14,56.4l149.8,149.8L91,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9c1.6,0,2.7,1.3,2.4,2.7 L197.6,507c-1,4.4,5.8,6.9,8.9,3.2l118.6-142.8L469.6,512L498,483.7z'/%3E%3Cpath class='st0' d='M449,218.2c2.5-3,0.1-7.2-3.9-7.2H301.2c-1.6,0-2.7-1.3-2.4-2.7L342.4,5c1-4.4-5.8-6.9-8.9-3.2L214.9,144.6 l161.3,161.3L449,218.2z'/%3E%3C/g%3E%3C/svg%3E`;
}
iconFlashOn() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cpath class='st0' d='M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9 c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z'/%3E%3C/svg%3E`;
}
iconFlashAuto() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cpath class='st0' d='M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9 c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z'/%3E%3Cg%3E%3Cpath class='st0' d='M321.3,186l74-186H438l74,186h-43.5l-11.9-32.5h-80.9l-12,32.5H321.3z M415.8,47.9l-27.2,70.7h54.9l-27.2-70.7 H415.8z'/%3E%3C/g%3E%3C/svg%3E`;
}
render() {
// const acceptStyles = { transform: `rotate(${-this.rotation}deg)` };
const acceptStyles = {};
return (index.h("div", { class: "camera-wrapper" }, index.h("div", { class: "camera-header" }, index.h("section", { class: "items" }, index.h("div", { class: "item close", onClick: e => this.handleClose(e) }, index.h("img", { src: this.iconExit() })), index.h("div", { class: "item flash", onClick: e => this.handleFlashClick(e) }, this.flashModes.length > 0 && (index.h("div", null, this.flashMode == 'off' ? index.h("img", { src: this.iconFlashOff() }) : '', this.flashMode == 'auto' ? index.h("img", { src: this.iconFlashAuto() }) : '', this.flashMode == 'flash' ? index.h("img", { src: this.iconFlashOn() }) : ''))))), (this.hasCamera === false || !!this.deviceError) && (index.h("div", { class: "no-device" }, index.h("h2", null, this.noDevicesText), index.h("label", { htmlFor: "_pwa-elements-camera-input" }, this.noDevicesButtonText), index.h("input", { type: "file", id: "_pwa-elements-camera-input", onChange: this.handleFileInputChange, accept: "image/*", class: "select-file-button" }))), this.photoSrc ? (index.h("div", { class: "accept" }, index.h("div", { class: "accept-image", style: Object.assign({ backgroundImage: `url(${this.photoSrc})` }, acceptStyles) }))) : (index.h("div", { class: "camera-video" }, this.showShutterOverlay && (index.h("div", { class: "shutter-overlay" })), this.hasImageCapture() ? (index.h("video", { ref: (el) => this.videoElement = el, onLoadedMetaData: this.handleVideoMetadata, autoplay: true, playsinline: true })) : (index.h("canvas", { ref: (el) => this.canvasElement = el, width: "100%", height: "100%" })), index.h("canvas", { class: "offscreen-image-render", ref: e => this.offscreenCanvas = e, width: "100%", height: "100%" }))), this.hasCamera && (index.h("div", { class: "camera-footer" }, !this.photo ? ([
!this.hidePicker && (index.h("div", { class: "pick-image", onClick: this.handlePickFile }, index.h("label", { htmlFor: "_pwa-elements-file-pick" }, this.iconPhotos()), index.h("input", { type: "file", id: "_pwa-elements-file-pick", onChange: this.handleFileInputChange, accept: "image/*", class: "pick-image-button" }))),
index.h("div", { class: "shutter", onClick: this.handleShutterClick }, index.h("div", { class: "shutter-button" })),
index.h("div", { class: "rotate", onClick: this.handleRotateClick }, index.h("img", { src: this.iconReverseCamera() })),
]) : (index.h("section", { class: "items" }, index.h("div", { class: "item accept-cancel", onClick: e => this.handleCancelPhoto(e) }, index.h("img", { src: this.iconRetake() })), index.h("div", { class: "item accept-use", onClick: e => this.handleAcceptPhoto(e) }, index.h("img", { src: this.iconConfirm() }))))))));
}
static get assetsDirs() { return ["icons"]; }
get el() { return index.getElement(this); }
};
CameraPWA.style = cameraCss;
exports.pwa_camera = CameraPWA;

View File

@ -0,0 +1,49 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const index = require('./index-d7f36e37.js');
const toastCss = ":host{position:fixed;bottom:20px;left:0;right:0;display:-ms-flexbox;display:flex;opacity:0}:host(.in){-webkit-transition:opacity 300ms;transition:opacity 300ms;opacity:1}:host(.out){-webkit-transition:opacity 1s;transition:opacity 1s;opacity:0}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.toast{font-family:-apple-system, system-ui, \"Helvetica Neue\", Roboto, sans-serif;background-color:#eee;color:black;border-radius:5px;padding:10px 15px;font-size:14px;font-weight:500;-webkit-box-shadow:0px 1px 2px rgba(0, 0, 0, 0.20);box-shadow:0px 1px 2px rgba(0, 0, 0, 0.20)}";
const PWAToast = class {
constructor(hostRef) {
index.registerInstance(this, hostRef);
this.message = undefined;
this.duration = 2000;
this.closing = null;
}
hostData() {
const classes = {
out: !!this.closing
};
if (this.closing !== null) {
classes['in'] = !this.closing;
}
return {
class: classes
};
}
componentDidLoad() {
setTimeout(() => {
this.closing = false;
});
setTimeout(() => {
this.close();
}, this.duration);
}
close() {
this.closing = true;
setTimeout(() => {
this.el.parentNode.removeChild(this.el);
}, 1000);
}
__stencil_render() {
return (index.h("div", { class: "wrapper" }, index.h("div", { class: "toast" }, this.message)));
}
get el() { return index.getElement(this); }
render() { return index.h(index.Host, this.hostData(), this.__stencil_render()); }
};
PWAToast.style = toastCss;
exports.pwa_toast = PWAToast;

View File

@ -0,0 +1,16 @@
{
"entries": [
"./components/action-sheet/action-sheet.js",
"./components/camera/camera.js",
"./components/camera-modal/camera-modal-instance.js",
"./components/camera-modal/camera-modal.js",
"./components/toast/toast.js"
],
"compiler": {
"name": "@stencil/core",
"version": "3.4.0",
"typescriptVersion": "5.0.4"
},
"collections": [],
"bundles": []
}

View File

@ -0,0 +1,79 @@
:host {
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
contain: strict;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Roboto", sans-serif;
}
.wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0);
transition: 400ms background-color cubic-bezier(.36,.66,.04,1);
}
.wrapper.open {
background-color: rgba(0, 0, 0, 0.32);
}
.title {
color: #999;
height: 23px;
line-height: 23px;
padding-bottom: 17px;
padding-inline-end: 16px;
padding-inline-start: 16px;
padding-left: 16px;
padding-right: 16px;
padding-top: 20px;
}
.content {
width: 568px;
align-self: flex-end;
background-color:#fff;
transition: 400ms transform cubic-bezier(.36,.66,.04,1);
transform: translateY(100%);
}
.wrapper.open .content {
transform: translateY(0%);
}
@media only screen and (max-width: 568px) {
.content {
width: 100%;
}
}
.action-sheet-option {
cursor: pointer;
height: 52px;
line-height: 52px;
}
.action-sheet-button {
color: rgb(38, 38, 38);
font-size: 16px;
padding-inline-end: 16px;
padding-inline-start: 16px;
padding-left: 16px;
padding-right: 16px;
padding-top: 0px;
}
.action-sheet-button:hover {
background-color: #F6F6F6;
}

View File

@ -0,0 +1,129 @@
import { h } from '@stencil/core';
export class PWAActionSheet {
constructor() {
this.header = undefined;
this.cancelable = true;
this.options = [];
this.open = false;
}
componentDidLoad() {
requestAnimationFrame(() => {
this.open = true;
});
}
dismiss() {
if (this.cancelable) {
this.close();
}
}
close() {
this.open = false;
setTimeout(() => {
this.el.parentNode.removeChild(this.el);
}, 500);
}
handleOptionClick(e, i) {
e.stopPropagation();
this.onSelection.emit(i);
this.close();
}
render() {
return (h("div", { class: `wrapper${this.open ? ' open' : ''}`, onClick: () => this.dismiss() }, h("div", { class: "content" }, h("div", { class: "title" }, this.header), this.options.map((option, i) => h("div", { class: "action-sheet-option", onClick: (e) => this.handleOptionClick(e, i) }, h("div", { class: "action-sheet-button" }, option.title))))));
}
static get is() { return "pwa-action-sheet"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["action-sheet.css"]
};
}
static get styleUrls() {
return {
"$": ["action-sheet.css"]
};
}
static get properties() {
return {
"header": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "header",
"reflect": false
},
"cancelable": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "cancelable",
"reflect": false,
"defaultValue": "true"
},
"options": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "ActionSheetOption[]",
"resolved": "ActionSheetOption[]",
"references": {
"ActionSheetOption": {
"location": "import",
"path": "../../definitions"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"defaultValue": "[]"
}
};
}
static get states() {
return {
"open": {}
};
}
static get events() {
return [{
"method": "onSelection",
"name": "onSelection",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}];
}
static get elementRef() { return "el"; }
}

View File

@ -0,0 +1,35 @@
:host {
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
contain: strict;
--inset-width: 600px;
--inset-height: 600px;
}
.wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.15);
}
.content {
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
width: var(--inset-width);
height: var(--inset-height);
max-height: 100%;
}
@media only screen and (max-width: 600px) {
.content {
width: 100%;
height: 100%;
}
}

View File

@ -0,0 +1,162 @@
import { h } from '@stencil/core';
export class PWACameraModal {
constructor() {
this.handlePhoto = async (photo) => {
this.onPhoto.emit(photo);
};
this.handleNoDeviceError = async (photo) => {
this.noDeviceError.emit(photo);
};
this.facingMode = 'user';
this.hidePicker = false;
this.noDevicesText = 'No camera found';
this.noDevicesButtonText = 'Choose image';
}
handleBackdropClick(e) {
if (e.target !== this.el) {
this.onPhoto.emit(null);
}
}
handleComponentClick(e) {
e.stopPropagation();
}
handleBackdropKeyUp(e) {
if (e.key === "Escape") {
this.onPhoto.emit(null);
}
}
render() {
return (h("div", { class: "wrapper", onClick: e => this.handleBackdropClick(e) }, h("div", { class: "content" }, h("pwa-camera", { onClick: e => this.handleComponentClick(e), facingMode: this.facingMode, hidePicker: this.hidePicker, handlePhoto: this.handlePhoto, handleNoDeviceError: this.handleNoDeviceError, noDevicesButtonText: this.noDevicesButtonText, noDevicesText: this.noDevicesText }))));
}
static get is() { return "pwa-camera-modal-instance"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["camera-modal-instance.css"]
};
}
static get styleUrls() {
return {
"$": ["camera-modal-instance.css"]
};
}
static get properties() {
return {
"facingMode": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "facing-mode",
"reflect": false,
"defaultValue": "'user'"
},
"hidePicker": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "hide-picker",
"reflect": false,
"defaultValue": "false"
},
"noDevicesText": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "no-devices-text",
"reflect": false,
"defaultValue": "'No camera found'"
},
"noDevicesButtonText": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "no-devices-button-text",
"reflect": false,
"defaultValue": "'Choose image'"
}
};
}
static get events() {
return [{
"method": "onPhoto",
"name": "onPhoto",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}, {
"method": "noDeviceError",
"name": "noDeviceError",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}];
}
static get elementRef() { return "el"; }
static get listeners() {
return [{
"name": "keyup",
"method": "handleBackdropKeyUp",
"target": "body",
"capture": false,
"passive": false
}];
}
}

View File

@ -0,0 +1,24 @@
:host {
z-index: 1000;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
contain: strict;
}
.wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.15);
}
.content {
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
width: 600px;
height: 600px;
}

View File

@ -0,0 +1,155 @@
import { h } from '@stencil/core';
export class PWACameraModal {
constructor() {
this.facingMode = 'user';
this.hidePicker = false;
}
async present() {
const camera = document.createElement('pwa-camera-modal-instance');
camera.facingMode = this.facingMode;
camera.hidePicker = this.hidePicker;
camera.addEventListener('onPhoto', async (e) => {
if (!this._modal) {
return;
}
const photo = e.detail;
this.onPhoto.emit(photo);
});
camera.addEventListener('noDeviceError', async (e) => {
this.noDeviceError.emit(e);
});
document.body.append(camera);
this._modal = camera;
}
async dismiss() {
if (!this._modal) {
return;
}
this._modal && this._modal.parentNode.removeChild(this._modal);
this._modal = null;
}
render() {
return (h("div", null));
}
static get is() { return "pwa-camera-modal"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["camera-modal.css"]
};
}
static get styleUrls() {
return {
"$": ["camera-modal.css"]
};
}
static get properties() {
return {
"facingMode": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "facing-mode",
"reflect": false,
"defaultValue": "'user'"
},
"hidePicker": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "hide-picker",
"reflect": false,
"defaultValue": "false"
}
};
}
static get events() {
return [{
"method": "onPhoto",
"name": "onPhoto",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}, {
"method": "noDeviceError",
"name": "noDeviceError",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}];
}
static get methods() {
return {
"present": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "",
"tags": []
}
},
"dismiss": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "",
"tags": []
}
}
};
}
}

View File

@ -0,0 +1,244 @@
:host {
--header-height: 4em;
--footer-height: 9em;
--header-height-landscape: 3em;
--footer-height-landscape: 6em;
--shutter-size: 6em;
--icon-size-header: 1.5em;
--icon-size-footer: 2.5em;
--margin-size-header: 1.5em;
--margin-size-footer: 2.0em;
font-family: -apple-system, BlinkMacSystemFont,
Segoe UI, Roboto, Droid Sans, Helvetica Neue, sans-serif;
display: block;
width: 100%;
height: 100%;
}
.items {
box-sizing: border-box;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
}
.items .item {
flex: 1;
text-align: center;
}
.items .item:first-child {
text-align: left;
}
.items .item:last-child {
text-align: right;
}
.camera-wrapper {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.camera-header {
color: white;
background-color: black;
height: var(--header-height);
}
.camera-header .items {
padding: var(--margin-size-header);
}
.camera-footer {
position: relative;
color: white;
background-color: black;
height: var(--footer-height);
}
.camera-footer .items {
padding: var(--margin-size-footer);
}
@media (max-height: 375px) {
.camera-header {
--header-height: var(--header-height-landscape);
}
.camera-footer {
--footer-height: var(--footer-height-landscape);
}
.camera-footer .shutter {
--shutter-size: 4em;
}
}
.camera-video {
position: relative;
flex: 1;
overflow: hidden;
background-color: black;
}
video {
width: 100%;
height: 100%;
max-height: 100%;
min-height: 100%;
object-fit: cover;
background-color: black;
}
.pick-image {
display: flex;
align-items: center;
position: absolute;
left: var(--margin-size-footer);
top: 0;
height: 100%;
width: var(--icon-size-footer);
color: white;
}
.pick-image input {
visibility: hidden;
}
.pick-image svg {
cursor: pointer;
fill: white;
width: var(--icon-size-footer);
height: var(--icon-size-footer);
}
.shutter {
position: absolute;
left: 50%;
top: 50%;
width: var(--shutter-size);
height: var(--shutter-size);
margin-top: calc(var(--shutter-size) / -2);
margin-left: calc(var(--shutter-size) / -2);
border-radius: 100%;
background-color: #c6cdd8;
padding: 12px;
box-sizing: border-box;
}
.shutter:active .shutter-button {
background-color: #9da9bb;
}
.shutter-button {
background-color: white;
border-radius: 100%;
width: 100%;
height: 100%;
}
.rotate {
display: flex;
align-items: center;
position: absolute;
right: var(--margin-size-footer);
top: 0;
height: 100%;
width: var(--icon-size-footer);
color: white;
}
.rotate img {
width: var(--icon-size-footer);
height: var(--icon-size-footer);
}
.shutter-overlay {
z-index: 5;
position: absolute;
width: 100%;
height: 100%;
background-color: black;
}
.error {
width: 100%;
height: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
}
.no-device {
background-color: black;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
}
.no-device label {
cursor: pointer;
background: #fff;
border-radius: 6px;
padding: 6px 8px;
color: black;
}
.no-device input {
visibility: hidden;
height: 0;
margin-top: 16px;
}
.accept {
background-color: black;
flex: 1;
overflow: hidden;
}
.accept .accept-image {
width: 100%;
height: 100%;
max-height: 100%;
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
}
.close img {
cursor: pointer;
width: var(--icon-size-header);
height: var(--icon-size-header);
}
.flash img {
width: var(--icon-size-header);
height: var(--icon-size-header);
}
.accept-use img {
width: var(--icon-size-footer);
height: var(--icon-size-footer);
}
.accept-cancel img {
width: var(--icon-size-footer);
height: var(--icon-size-footer);
}
.offscreen-image-render {
top: 0;
left: 0;
visibility: hidden;
pointer-events: none;
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,455 @@
import { h, Build, forceUpdate } from '@stencil/core';
import './imagecapture';
export class CameraPWA {
constructor() {
// Whether the device has multiple cameras (front/back)
this.hasMultipleCameras = false;
// Whether the device has flash support
this.hasFlash = false;
// Flash modes for camera
this.flashModes = [];
// Current flash mode
this.flashMode = 'off';
this.handlePickFile = (_e) => {
};
this.handleShutterClick = (_e) => {
console.debug('shutter click');
this.capture();
};
this.handleRotateClick = (_e) => {
this.rotate();
};
this.handleClose = (_e) => {
this.handlePhoto && this.handlePhoto(null);
};
this.handleFlashClick = (_e) => {
this.cycleFlash();
};
this.handleCancelPhoto = (_e) => {
const track = this.stream && this.stream.getTracks()[0];
let c = track && track.getConstraints();
this.photo = null;
this.photoSrc = null;
if (c) {
this.initCamera({
video: {
facingMode: c.facingMode
}
});
}
else {
this.initCamera();
}
};
this.handleAcceptPhoto = (_e) => {
this.handlePhoto && this.handlePhoto(this.photo);
};
this.handleFileInputChange = async (e) => {
const input = e.target;
const file = input.files[0];
try {
const orientation = await this.getOrientation(file);
console.debug('Got orientation', orientation);
this.photoOrientation = orientation;
}
catch (e) {
}
this.handlePhoto && this.handlePhoto(file);
};
this.handleVideoMetadata = (e) => {
console.debug('Video metadata', e);
};
this.facingMode = 'user';
this.handlePhoto = undefined;
this.hidePicker = false;
this.handleNoDeviceError = undefined;
this.noDevicesText = 'No camera found';
this.noDevicesButtonText = 'Choose image';
this.photo = undefined;
this.photoSrc = undefined;
this.showShutterOverlay = false;
this.flashIndex = 0;
this.hasCamera = null;
this.rotation = 0;
this.deviceError = null;
}
async componentDidLoad() {
if (Build.isServer) {
return;
}
this.defaultConstraints = {
video: {
facingMode: this.facingMode
}
};
// Figure out how many cameras we have
await this.queryDevices();
// Initialize the camera
await this.initCamera();
}
disconnectedCallback() {
this.stopStream();
this.photoSrc && URL.revokeObjectURL(this.photoSrc);
}
hasImageCapture() {
return 'ImageCapture' in window;
}
/**
* Query the list of connected devices and figure out how many video inputs we have.
*/
async queryDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(d => d.kind == 'videoinput');
this.hasCamera = !!videoDevices.length;
this.hasMultipleCameras = videoDevices.length > 1;
}
catch (e) {
this.deviceError = e;
}
}
async initCamera(constraints) {
if (!constraints) {
constraints = this.defaultConstraints;
}
try {
const stream = await navigator.mediaDevices.getUserMedia(Object.assign({ video: true, audio: false }, constraints));
this.initStream(stream);
}
catch (e) {
this.deviceError = e;
this.handleNoDeviceError && this.handleNoDeviceError(e);
}
}
async initStream(stream) {
this.stream = stream;
this.videoElement.srcObject = stream;
if (this.hasImageCapture()) {
this.imageCapture = new window.ImageCapture(stream.getVideoTracks()[0]);
await this.initPhotoCapabilities(this.imageCapture);
}
else {
this.deviceError = 'No image capture';
this.handleNoDeviceError && this.handleNoDeviceError();
}
// Always re-render
forceUpdate(this.el);
}
async initPhotoCapabilities(imageCapture) {
const c = await imageCapture.getPhotoCapabilities();
if (c.fillLightMode && c.fillLightMode.length > 1) {
this.flashModes = c.fillLightMode.map(m => m);
// Try to recall the current flash mode
if (this.flashMode) {
this.flashMode = this.flashModes[this.flashModes.indexOf(this.flashMode)] || 'off';
this.flashIndex = this.flashModes.indexOf(this.flashMode) || 0;
}
else {
this.flashIndex = 0;
}
}
}
stopStream() {
if (this.videoElement) {
this.videoElement.srcObject = null;
}
this.stream && this.stream.getTracks().forEach(track => track.stop());
}
async capture() {
if (this.hasImageCapture()) {
try {
const photo = await this.imageCapture.takePhoto({
fillLightMode: this.flashModes.length > 1 ? this.flashMode : undefined
});
await this.flashScreen();
this.promptAccept(photo);
}
catch (e) {
console.error('Unable to take photo!', e);
}
}
this.stopStream();
}
async promptAccept(photo) {
this.photo = photo;
const orientation = await this.getOrientation(photo);
console.debug('Got orientation', orientation);
this.photoOrientation = orientation;
if (orientation) {
switch (orientation) {
case 1:
case 2:
this.rotation = 0;
break;
case 3:
case 4:
this.rotation = 180;
break;
case 5:
case 6:
this.rotation = 90;
break;
case 7:
case 8:
this.rotation = 270;
break;
}
}
this.photoSrc = URL.createObjectURL(photo);
}
getOrientation(file) {
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = (event) => {
const view = new DataView(event.target.result);
if (view.getUint16(0, false) !== 0xFFD8) {
return resolve(-2);
}
const length = view.byteLength;
let offset = 2;
while (offset < length) {
const marker = view.getUint16(offset, false);
offset += 2;
if (marker === 0xFFE1) {
if (view.getUint32(offset += 2, false) !== 0x45786966) {
return resolve(-1);
}
const little = view.getUint16(offset += 6, false) === 0x4949;
offset += view.getUint32(offset + 4, little);
const tags = view.getUint16(offset, little);
offset += 2;
for (let i = 0; i < tags; i++) {
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
return resolve(view.getUint16(offset + (i * 12) + 8, little));
}
}
}
else if ((marker & 0xFF00) !== 0xFF00) {
break;
}
else {
offset += view.getUint16(offset, false);
}
}
return resolve(-1);
};
reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
});
}
rotate() {
this.stopStream();
const track = this.stream && this.stream.getTracks()[0];
if (!track) {
return;
}
let c = track.getConstraints();
let facingMode = c.facingMode;
if (!facingMode) {
let c = track.getCapabilities();
if (c.facingMode) {
facingMode = c.facingMode[0];
}
}
if (facingMode === 'environment') {
this.initCamera({
video: {
facingMode: 'user'
}
});
}
else {
this.initCamera({
video: {
facingMode: 'environment'
}
});
}
}
setFlashMode(mode) {
console.debug('New flash mode: ', mode);
this.flashMode = mode;
}
cycleFlash() {
if (this.flashModes.length > 0) {
this.flashIndex = (this.flashIndex + 1) % this.flashModes.length;
this.setFlashMode(this.flashModes[this.flashIndex]);
}
}
async flashScreen() {
return new Promise((resolve, _reject) => {
this.showShutterOverlay = true;
setTimeout(() => {
this.showShutterOverlay = false;
resolve();
}, 100);
});
}
iconExit() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Cg id='Icon_5_'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M402.2,134L378,109.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L139.6,109.8 c-1.6-1.6-4.1-1.6-5.7,0L109.8,134c-1.6,1.6-1.6,4.1,0,5.7l113.5,113.5c1.6,1.6,1.6,4.1,0,5.7L109.8,372.4c-1.6,1.6-1.6,4.1,0,5.7 l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l113.5-113.5c1.6-1.6,4.1-1.6,5.7,0l113.5,113.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1 c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l113.5-113.5C403.7,138.1,403.7,135.5,402.2,134z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconPhotos() {
return (h("svg", { xmlns: 'http://www.w3.org/2000/svg', width: '512', height: '512', viewBox: '0 0 512 512' }, h("path", { d: 'M450.29,112H142c-34,0-62,27.51-62,61.33V418.67C80,452.49,108,480,142,480H450c34,0,62-26.18,62-60V173.33C512,139.51,484.32,112,450.29,112Zm-77.15,61.34a46,46,0,1,1-46.28,46A46.19,46.19,0,0,1,373.14,173.33Zm-231.55,276c-17,0-29.86-13.75-29.86-30.66V353.85l90.46-80.79a46.54,46.54,0,0,1,63.44,1.83L328.27,337l-113,112.33ZM480,418.67a30.67,30.67,0,0,1-30.71,30.66H259L376.08,333a46.24,46.24,0,0,1,59.44-.16L480,370.59Z' }), h("path", { d: 'M384,32H64A64,64,0,0,0,0,96V352a64.11,64.11,0,0,0,48,62V152a72,72,0,0,1,72-72H446A64.11,64.11,0,0,0,384,32Z' })));
}
iconConfirm() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Ccircle fill='%232CD865' cx='256' cy='256' r='256'/%3E%3Cg id='Icon_1_'%3E%3Cg%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M208,301.4l-55.4-55.5c-1.5-1.5-4-1.6-5.6-0.1l-23.4,22.3c-1.6,1.6-1.7,4.1-0.1,5.7l81.6,81.4 c3.1,3.1,8.2,3.1,11.3,0l171.8-171.7c1.6-1.6,1.6-4.2-0.1-5.7l-23.4-22.3c-1.6-1.5-4.1-1.5-5.6,0.1L213.7,301.4 C212.1,303,209.6,303,208,301.4z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconReverseCamera() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M352,0H160C72,0,0,72,0,160v192c0,88,72,160,160,160h192c88,0,160-72,160-160V160C512,72,440,0,352,0z M356.7,365.8l-3.7,3.3c-27,23.2-61.4,35.9-96.8,35.9c-72.4,0-135.8-54.7-147-125.6c-0.3-1.9-2-3.3-3.9-3.3H64 c-3.3,0-5.2-3.8-3.2-6.4l61.1-81.4c1.6-2.1,4.7-2.1,6.4-0.1l63.3,81.4c2,2.6,0.2,6.5-3.2,6.5h-40.6c-2.5,0-4.5,2.4-3.9,4.8 c11.5,51.5,59.2,90.6,112.4,90.6c26.4,0,51.8-9.7,73.7-27.9l3.1-2.5c1.6-1.3,3.9-1.1,5.3,0.3l18.5,18.6 C358.5,361.6,358.4,364.3,356.7,365.8z M451.4,245.6l-61,83.5c-1.6,2.2-4.8,2.2-6.4,0.1l-63.3-83.3c-2-2.6-0.1-6.4,3.2-6.4h40.8 c2.5,0,4.4-2.3,3.9-4.8c-5.1-24.2-17.8-46.5-36.5-63.7c-21.2-19.4-48.2-30.1-76-30.1c-26.5,0-52.6,9.7-73.7,27.3l-3.1,2.5 c-1.6,1.3-3.9,1.2-5.4-0.3l-18.5-18.5c-1.6-1.6-1.5-4.3,0.2-5.9l3.5-3.1c27-23.2,61.4-35.9,96.8-35.9c38,0,73.9,13.7,101.2,38.7 c23.2,21.1,40.3,55.2,45.7,90.1c0.3,1.9,1.9,3.4,3.9,3.4h41.3C451.4,239.2,453.3,243,451.4,245.6z'/%3E%3C/g%3E%3C/svg%3E`;
}
iconRetake() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' enable-background='new 0 0 512 512' xml:space='preserve'%3E%3Ccircle fill='%23727A87' cx='256' cy='256' r='256'/%3E%3Cg id='Icon_5_'%3E%3Cg%3E%3Cpath fill='%23FFFFFF' d='M394.2,142L370,117.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L147.6,117.8 c-1.6-1.6-4.1-1.6-5.7,0L117.8,142c-1.6,1.6-1.6,4.1,0,5.7l105.5,105.5c1.6,1.6,1.6,4.1,0,5.7L117.8,364.4c-1.6,1.6-1.6,4.1,0,5.7 l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l105.5-105.5c1.6-1.6,4.1-1.6,5.7,0l105.5,105.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1 c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l105.5-105.5C395.7,146.1,395.7,143.5,394.2,142z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E`;
}
iconFlashOff() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M498,483.7L42.3,28L14,56.4l149.8,149.8L91,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9c1.6,0,2.7,1.3,2.4,2.7 L197.6,507c-1,4.4,5.8,6.9,8.9,3.2l118.6-142.8L469.6,512L498,483.7z'/%3E%3Cpath class='st0' d='M449,218.2c2.5-3,0.1-7.2-3.9-7.2H301.2c-1.6,0-2.7-1.3-2.4-2.7L342.4,5c1-4.4-5.8-6.9-8.9-3.2L214.9,144.6 l161.3,161.3L449,218.2z'/%3E%3C/g%3E%3C/svg%3E`;
}
iconFlashOn() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cpath class='st0' d='M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9 c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z'/%3E%3C/svg%3E`;
}
iconFlashAuto() {
return `data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 512 512' style='enable-background:new 0 0 512 512;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%23FFFFFF;%7D%0A%3C/style%3E%3Cpath class='st0' d='M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9 c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z'/%3E%3Cg%3E%3Cpath class='st0' d='M321.3,186l74-186H438l74,186h-43.5l-11.9-32.5h-80.9l-12,32.5H321.3z M415.8,47.9l-27.2,70.7h54.9l-27.2-70.7 H415.8z'/%3E%3C/g%3E%3C/svg%3E`;
}
render() {
// const acceptStyles = { transform: `rotate(${-this.rotation}deg)` };
const acceptStyles = {};
return (h("div", { class: "camera-wrapper" }, h("div", { class: "camera-header" }, h("section", { class: "items" }, h("div", { class: "item close", onClick: e => this.handleClose(e) }, h("img", { src: this.iconExit() })), h("div", { class: "item flash", onClick: e => this.handleFlashClick(e) }, this.flashModes.length > 0 && (h("div", null, this.flashMode == 'off' ? h("img", { src: this.iconFlashOff() }) : '', this.flashMode == 'auto' ? h("img", { src: this.iconFlashAuto() }) : '', this.flashMode == 'flash' ? h("img", { src: this.iconFlashOn() }) : ''))))), (this.hasCamera === false || !!this.deviceError) && (h("div", { class: "no-device" }, h("h2", null, this.noDevicesText), h("label", { htmlFor: "_pwa-elements-camera-input" }, this.noDevicesButtonText), h("input", { type: "file", id: "_pwa-elements-camera-input", onChange: this.handleFileInputChange, accept: "image/*", class: "select-file-button" }))), this.photoSrc ? (h("div", { class: "accept" }, h("div", { class: "accept-image", style: Object.assign({ backgroundImage: `url(${this.photoSrc})` }, acceptStyles) }))) : (h("div", { class: "camera-video" }, this.showShutterOverlay && (h("div", { class: "shutter-overlay" })), this.hasImageCapture() ? (h("video", { ref: (el) => this.videoElement = el, onLoadedMetaData: this.handleVideoMetadata, autoplay: true, playsinline: true })) : (h("canvas", { ref: (el) => this.canvasElement = el, width: "100%", height: "100%" })), h("canvas", { class: "offscreen-image-render", ref: e => this.offscreenCanvas = e, width: "100%", height: "100%" }))), this.hasCamera && (h("div", { class: "camera-footer" }, !this.photo ? ([
!this.hidePicker && (h("div", { class: "pick-image", onClick: this.handlePickFile }, h("label", { htmlFor: "_pwa-elements-file-pick" }, this.iconPhotos()), h("input", { type: "file", id: "_pwa-elements-file-pick", onChange: this.handleFileInputChange, accept: "image/*", class: "pick-image-button" }))),
h("div", { class: "shutter", onClick: this.handleShutterClick }, h("div", { class: "shutter-button" })),
h("div", { class: "rotate", onClick: this.handleRotateClick }, h("img", { src: this.iconReverseCamera() })),
]) : (h("section", { class: "items" }, h("div", { class: "item accept-cancel", onClick: e => this.handleCancelPhoto(e) }, h("img", { src: this.iconRetake() })), h("div", { class: "item accept-use", onClick: e => this.handleAcceptPhoto(e) }, h("img", { src: this.iconConfirm() }))))))));
}
static get is() { return "pwa-camera"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["camera.css"]
};
}
static get styleUrls() {
return {
"$": ["camera.css"]
};
}
static get assetsDirs() { return ["icons"]; }
static get properties() {
return {
"facingMode": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "facing-mode",
"reflect": false,
"defaultValue": "'user'"
},
"handlePhoto": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "(photo: Blob) => void",
"resolved": "(photo: Blob) => void",
"references": {
"Blob": {
"location": "global"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
}
},
"hidePicker": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "hide-picker",
"reflect": false,
"defaultValue": "false"
},
"handleNoDeviceError": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "(e?: any) => void",
"resolved": "(e?: any) => void",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
}
},
"noDevicesText": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "no-devices-text",
"reflect": false,
"defaultValue": "'No camera found'"
},
"noDevicesButtonText": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "no-devices-button-text",
"reflect": false,
"defaultValue": "'Choose image'"
}
};
}
static get states() {
return {
"photo": {},
"photoSrc": {},
"showShutterOverlay": {},
"flashIndex": {},
"hasCamera": {},
"rotation": {},
"deviceError": {}
};
}
static get elementRef() { return "el"; }
}

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<circle fill="#2CD865" cx="256" cy="256" r="256"/>
<g id="Icon_1_">
<g>
<g>
<path fill="#FFFFFF" d="M208,301.4l-55.4-55.5c-1.5-1.5-4-1.6-5.6-0.1l-23.4,22.3c-1.6,1.6-1.7,4.1-0.1,5.7l81.6,81.4
c3.1,3.1,8.2,3.1,11.3,0l171.8-171.7c1.6-1.6,1.6-4.2-0.1-5.7l-23.4-22.3c-1.6-1.5-4.1-1.5-5.6,0.1L213.7,301.4
C212.1,303,209.6,303,208,301.4z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 715 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g id="Icon_5_">
<g>
<path fill="#FFFFFF" d="M402.2,134L378,109.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L139.6,109.8
c-1.6-1.6-4.1-1.6-5.7,0L109.8,134c-1.6,1.6-1.6,4.1,0,5.7l113.5,113.5c1.6,1.6,1.6,4.1,0,5.7L109.8,372.4c-1.6,1.6-1.6,4.1,0,5.7
l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l113.5-113.5c1.6-1.6,4.1-1.6,5.7,0l113.5,113.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1
c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l113.5-113.5C403.7,138.1,403.7,135.5,402.2,134z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 853 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9
c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z"/>
<g>
<path class="st0" d="M321.3,186l74-186H438l74,186h-43.5l-11.9-32.5h-80.9l-12,32.5H321.3z M415.8,47.9l-27.2,70.7h54.9l-27.2-70.7
H415.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 797 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M498,483.7L42.3,28L14,56.4l149.8,149.8L91,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9c1.6,0,2.7,1.3,2.4,2.7
L197.6,507c-1,4.4,5.8,6.9,8.9,3.2l118.6-142.8L469.6,512L498,483.7z"/>
<path class="st0" d="M449,218.2c2.5-3,0.1-7.2-3.9-7.2H301.2c-1.6,0-2.7-1.3-2.4-2.7L342.4,5c1-4.4-5.8-6.9-8.9-3.2L214.9,144.6
l161.3,161.3L449,218.2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 767 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M287.2,211c-1.6,0-2.7-1.3-2.4-2.7L328.4,5c1-4.4-5.8-6.9-8.9-3.2L77,293.8c-2.5,3-0.1,7.2,3.9,7.2h143.9
c1.6,0,2.7,1.3,2.4,2.7L183.6,507c-1,4.4,5.8,6.9,8.9,3.2l242.5-292c2.5-3,0.1-7.2-3.9-7.2L287.2,211L287.2,211z"/>
</svg>

After

Width:  |  Height:  |  Size: 646 B

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<circle fill="#727A87" cx="256" cy="256" r="256"/>
<g id="Icon_5_">
<g>
<path fill="#FFFFFF" d="M394.2,142L370,117.8c-1.6-1.6-4.1-1.6-5.7,0L258.8,223.4c-1.6,1.6-4.1,1.6-5.7,0L147.6,117.8
c-1.6-1.6-4.1-1.6-5.7,0L117.8,142c-1.6,1.6-1.6,4.1,0,5.7l105.5,105.5c1.6,1.6,1.6,4.1,0,5.7L117.8,364.4c-1.6,1.6-1.6,4.1,0,5.7
l24.1,24.1c1.6,1.6,4.1,1.6,5.7,0l105.5-105.5c1.6-1.6,4.1-1.6,5.7,0l105.5,105.5c1.6,1.6,4.1,1.6,5.7,0l24.1-24.1
c1.6-1.6,1.6-4.1,0-5.7L288.6,258.8c-1.6-1.6-1.6-4.1,0-5.7l105.5-105.5C395.7,146.1,395.7,143.5,394.2,142z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<path fill="#FFFFFF" d="M352,0H160C72,0,0,72,0,160v192c0,88,72,160,160,160h192c88,0,160-72,160-160V160C512,72,440,0,352,0z
M356.7,365.8l-3.7,3.3c-27,23.2-61.4,35.9-96.8,35.9c-72.4,0-135.8-54.7-147-125.6c-0.3-1.9-2-3.3-3.9-3.3H64
c-3.3,0-5.2-3.8-3.2-6.4l61.1-81.4c1.6-2.1,4.7-2.1,6.4-0.1l63.3,81.4c2,2.6,0.2,6.5-3.2,6.5h-40.6c-2.5,0-4.5,2.4-3.9,4.8
c11.5,51.5,59.2,90.6,112.4,90.6c26.4,0,51.8-9.7,73.7-27.9l3.1-2.5c1.6-1.3,3.9-1.1,5.3,0.3l18.5,18.6
C358.5,361.6,358.4,364.3,356.7,365.8z M451.4,245.6l-61,83.5c-1.6,2.2-4.8,2.2-6.4,0.1l-63.3-83.3c-2-2.6-0.1-6.4,3.2-6.4h40.8
c2.5,0,4.4-2.3,3.9-4.8c-5.1-24.2-17.8-46.5-36.5-63.7c-21.2-19.4-48.2-30.1-76-30.1c-26.5,0-52.6,9.7-73.7,27.3l-3.1,2.5
c-1.6,1.3-3.9,1.2-5.4-0.3l-18.5-18.5c-1.6-1.6-1.5-4.3,0.2-5.9l3.5-3.1c27-23.2,61.4-35.9,96.8-35.9c38,0,73.9,13.7,101.2,38.7
c23.2,21.1,40.3,55.2,45.7,90.1c0.3,1.9,1.9,3.4,3.9,3.4h41.3C451.4,239.2,453.3,243,451.4,245.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,155 @@
/**
* MediaStream ImageCapture polyfill
*
* @license
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export let ImageCapture = window.ImageCapture;
if (typeof ImageCapture === 'undefined') {
ImageCapture = class {
/**
* TODO https://www.w3.org/TR/image-capture/#constructors
*
* @param {MediaStreamTrack} videoStreamTrack - A MediaStreamTrack of the 'video' kind
*/
constructor(videoStreamTrack) {
if (videoStreamTrack.kind !== 'video')
throw new DOMException('NotSupportedError');
this._videoStreamTrack = videoStreamTrack;
if (!('readyState' in this._videoStreamTrack)) {
// Polyfill for Firefox
this._videoStreamTrack.readyState = 'live';
}
// MediaStream constructor not available until Chrome 55 - https://www.chromestatus.com/feature/5912172546752512
this._previewStream = new MediaStream([videoStreamTrack]);
this.videoElement = document.createElement('video');
this.videoElementPlaying = new Promise(resolve => {
this.videoElement.addEventListener('playing', resolve);
});
if (HTMLMediaElement) {
this.videoElement.srcObject = this._previewStream; // Safari 11 doesn't allow use of createObjectURL for MediaStream
}
else {
this.videoElement.src = URL.createObjectURL(this._previewStream);
}
this.videoElement.muted = true;
this.videoElement.setAttribute('playsinline', ''); // Required by Safari on iOS 11. See https://webkit.org/blog/6784
this.videoElement.play();
this.canvasElement = document.createElement('canvas');
// TODO Firefox has https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
this.canvas2dContext = this.canvasElement.getContext('2d');
}
/**
* https://w3c.github.io/mediacapture-image/index.html#dom-imagecapture-videostreamtrack
* @return {MediaStreamTrack} The MediaStreamTrack passed into the constructor
*/
get videoStreamTrack() {
return this._videoStreamTrack;
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-getphotocapabilities
* @return {Promise<PhotoCapabilities>} Fulfilled promise with
* [PhotoCapabilities](https://www.w3.org/TR/image-capture/#idl-def-photocapabilities)
* object on success, rejected promise on failure
*/
getPhotoCapabilities() {
return new Promise(function executorGPC(resolve, reject) {
// TODO see https://github.com/w3c/mediacapture-image/issues/97
const MediaSettingsRange = {
current: 0, min: 0, max: 0,
};
resolve({
exposureCompensation: MediaSettingsRange,
exposureMode: 'none',
fillLightMode: ['none'],
focusMode: 'none',
imageHeight: MediaSettingsRange,
imageWidth: MediaSettingsRange,
iso: MediaSettingsRange,
redEyeReduction: false,
whiteBalanceMode: 'none',
zoom: MediaSettingsRange,
});
reject(new DOMException('OperationError'));
});
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-setoptions
* @param {Object} photoSettings - Photo settings dictionary, https://www.w3.org/TR/image-capture/#idl-def-photosettings
* @return {Promise<void>} Fulfilled promise on success, rejected promise on failure
*/
setOptions(_photoSettings = {}) {
return new Promise(function executorSO(_resolve, _reject) {
// TODO
});
}
/**
* TODO
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-takephoto
* @return {Promise<Blob>} Fulfilled promise with [Blob](https://www.w3.org/TR/FileAPI/#blob)
* argument on success; rejected promise on failure
*/
takePhoto() {
const self = this;
return new Promise(function executorTP(resolve, reject) {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
self.canvasElement.toBlob(resolve);
}
catch (error) {
reject(new DOMException('UnknownError'));
}
});
});
}
/**
* Implements https://www.w3.org/TR/image-capture/#dom-imagecapture-grabframe
* @return {Promise<ImageBitmap>} Fulfilled promise with
* [ImageBitmap](https://www.w3.org/TR/html51/webappapis.html#webappapis-images)
* argument on success; rejected promise on failure
*/
grabFrame() {
const self = this;
return new Promise(function executorGF(resolve, reject) {
// `If the readyState of the MediaStreamTrack provided in the constructor is not live,
// return a promise rejected with a new DOMException whose name is "InvalidStateError".`
if (self._videoStreamTrack.readyState !== 'live') {
return reject(new DOMException('InvalidStateError'));
}
self.videoElementPlaying.then(() => {
try {
self.canvasElement.width = self.videoElement.videoWidth;
self.canvasElement.height = self.videoElement.videoHeight;
self.canvas2dContext.drawImage(self.videoElement, 0, 0);
// TODO polyfill https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapFactories/createImageBitmap for IE
resolve(window.createImageBitmap(self.canvasElement));
}
catch (error) {
reject(new DOMException('UnknownError'));
}
});
});
}
};
}
window.ImageCapture = ImageCapture;

View File

@ -0,0 +1,37 @@
:host {
position: fixed;
bottom: 20px;
left: 0;
right: 0;
display: flex;
opacity: 0;
}
:host(.in) {
transition: opacity 300ms;
opacity: 1;
}
:host(.out) {
transition: opacity 1s;
opacity: 0;
}
.wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
.toast {
font-family: -apple-system, system-ui, "Helvetica Neue", Roboto, sans-serif;
background-color: #eee;
color: black;
border-radius: 5px;
padding: 10px 15px;
font-size: 14px;
font-weight: 500;
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.20);
}

View File

@ -0,0 +1,93 @@
import { h } from '@stencil/core';
export class PWAToast {
constructor() {
this.message = undefined;
this.duration = 2000;
this.closing = null;
}
hostData() {
const classes = {
out: !!this.closing
};
if (this.closing !== null) {
classes['in'] = !this.closing;
}
return {
class: classes
};
}
componentDidLoad() {
setTimeout(() => {
this.closing = false;
});
setTimeout(() => {
this.close();
}, this.duration);
}
close() {
this.closing = true;
setTimeout(() => {
this.el.parentNode.removeChild(this.el);
}, 1000);
}
render() {
return (h("div", { class: "wrapper" }, h("div", { class: "toast" }, this.message)));
}
static get is() { return "pwa-toast"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["toast.css"]
};
}
static get styleUrls() {
return {
"$": ["toast.css"]
};
}
static get properties() {
return {
"message": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "message",
"reflect": false
},
"duration": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"attribute": "duration",
"reflect": false,
"defaultValue": "2000"
}
};
}
static get states() {
return {
"closing": {}
};
}
static get elementRef() { return "el"; }
}

View File

@ -0,0 +1 @@
export {};

View File

@ -0,0 +1 @@
export * from './components';

File diff suppressed because one or more lines are too long

View File

View File

@ -0,0 +1 @@
import{p as promiseResolve,b as bootstrapLazy}from"./index-1c5c47b4.js";export{s as setNonce}from"./index-1c5c47b4.js";var patchBrowser=function(){var e=import.meta.url;var a={};if(e!==""){a.resourcesUrl=new URL(".",e).href}return promiseResolve(a)};patchBrowser().then((function(e){return bootstrapLazy([["pwa-camera-modal",[[1,"pwa-camera-modal",{facingMode:[1,"facing-mode"],hidePicker:[4,"hide-picker"],present:[64],dismiss:[64]}]]],["pwa-action-sheet",[[1,"pwa-action-sheet",{header:[1],cancelable:[4],options:[16],open:[32]}]]],["pwa-toast",[[1,"pwa-toast",{message:[1],duration:[2],closing:[32]}]]],["pwa-camera",[[1,"pwa-camera",{facingMode:[1,"facing-mode"],handlePhoto:[16],hidePicker:[4,"hide-picker"],handleNoDeviceError:[16],noDevicesText:[1,"no-devices-text"],noDevicesButtonText:[1,"no-devices-button-text"],photo:[32],photoSrc:[32],showShutterOverlay:[32],flashIndex:[32],hasCamera:[32],rotation:[32],deviceError:[32]}]]],["pwa-camera-modal-instance",[[1,"pwa-camera-modal-instance",{facingMode:[1,"facing-mode"],hidePicker:[4,"hide-picker"],noDevicesText:[1,"no-devices-text"],noDevicesButtonText:[1,"no-devices-button-text"]},[[16,"keyup","handleBackdropKeyUp"]]]]]],e)}));

View File

@ -0,0 +1 @@
import{p as promiseResolve,b as bootstrapLazy}from"./index-1c5c47b4.js";export{s as setNonce}from"./index-1c5c47b4.js";var patchEsm=function(){return promiseResolve()};var defineCustomElements=function(e,o){if(typeof window==="undefined")return Promise.resolve();return patchEsm().then((function(){return bootstrapLazy([["pwa-camera-modal",[[1,"pwa-camera-modal",{facingMode:[1,"facing-mode"],hidePicker:[4,"hide-picker"],present:[64],dismiss:[64]}]]],["pwa-action-sheet",[[1,"pwa-action-sheet",{header:[1],cancelable:[4],options:[16],open:[32]}]]],["pwa-toast",[[1,"pwa-toast",{message:[1],duration:[2],closing:[32]}]]],["pwa-camera",[[1,"pwa-camera",{facingMode:[1,"facing-mode"],handlePhoto:[16],hidePicker:[4,"hide-picker"],handleNoDeviceError:[16],noDevicesText:[1,"no-devices-text"],noDevicesButtonText:[1,"no-devices-button-text"],photo:[32],photoSrc:[32],showShutterOverlay:[32],flashIndex:[32],hasCamera:[32],rotation:[32],deviceError:[32]}]]],["pwa-camera-modal-instance",[[1,"pwa-camera-modal-instance",{facingMode:[1,"facing-mode"],hidePicker:[4,"hide-picker"],noDevicesText:[1,"no-devices-text"],noDevicesButtonText:[1,"no-devices-button-text"]},[[16,"keyup","handleBackdropKeyUp"]]]]]],o)}))};export{defineCustomElements};

View File

@ -0,0 +1 @@
import{r as registerInstance,c as createEvent,h,g as getElement}from"./index-1c5c47b4.js";var actionSheetCss=':host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-family:-apple-system, BlinkMacSystemFont, "Helvetica Neue", "Roboto", sans-serif}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0);-webkit-transition:400ms background-color cubic-bezier(.36,.66,.04,1);transition:400ms background-color cubic-bezier(.36,.66,.04,1)}.wrapper.open{background-color:rgba(0, 0, 0, 0.32)}.title{color:#999;height:23px;line-height:23px;padding-bottom:17px;-webkit-padding-end:16px;padding-inline-end:16px;-webkit-padding-start:16px;padding-inline-start:16px;padding-left:16px;padding-right:16px;padding-top:20px}.content{width:568px;-ms-flex-item-align:end;align-self:flex-end;background-color:#fff;-webkit-transition:400ms -webkit-transform cubic-bezier(.36,.66,.04,1);transition:400ms -webkit-transform cubic-bezier(.36,.66,.04,1);transition:400ms transform cubic-bezier(.36,.66,.04,1);transition:400ms transform cubic-bezier(.36,.66,.04,1), 400ms -webkit-transform cubic-bezier(.36,.66,.04,1);-webkit-transform:translateY(100%);transform:translateY(100%)}.wrapper.open .content{-webkit-transform:translateY(0%);transform:translateY(0%)}@media only screen and (max-width: 568px){.content{width:100%}}.action-sheet-option{cursor:pointer;height:52px;line-height:52px}.action-sheet-button{color:rgb(38, 38, 38);font-size:16px;-webkit-padding-end:16px;padding-inline-end:16px;-webkit-padding-start:16px;padding-inline-start:16px;padding-left:16px;padding-right:16px;padding-top:0px}.action-sheet-button:hover{background-color:#F6F6F6}';var PWAActionSheet=function(){function e(e){registerInstance(this,e);this.onSelection=createEvent(this,"onSelection",7);this.header=undefined;this.cancelable=true;this.options=[];this.open=false}e.prototype.componentDidLoad=function(){var e=this;requestAnimationFrame((function(){e.open=true}))};e.prototype.dismiss=function(){if(this.cancelable){this.close()}};e.prototype.close=function(){var e=this;this.open=false;setTimeout((function(){e.el.parentNode.removeChild(e.el)}),500)};e.prototype.handleOptionClick=function(e,t){e.stopPropagation();this.onSelection.emit(t);this.close()};e.prototype.render=function(){var e=this;return h("div",{class:"wrapper".concat(this.open?" open":""),onClick:function(){return e.dismiss()}},h("div",{class:"content"},h("div",{class:"title"},this.header),this.options.map((function(t,n){return h("div",{class:"action-sheet-option",onClick:function(t){return e.handleOptionClick(t,n)}},h("div",{class:"action-sheet-button"},t.title))}))))};Object.defineProperty(e.prototype,"el",{get:function(){return getElement(this)},enumerable:false,configurable:true});return e}();PWAActionSheet.style=actionSheetCss;export{PWAActionSheet as pwa_action_sheet};

View File

@ -0,0 +1 @@
var __awaiter=this&&this.__awaiter||function(e,t,n,o){function r(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,i){function a(e){try{s(o.next(e))}catch(e){i(e)}}function c(e){try{s(o["throw"](e))}catch(e){i(e)}}function s(e){e.done?n(e.value):r(e.value).then(a,c)}s((o=o.apply(e,t||[])).next())}))};var __generator=this&&this.__generator||function(e,t){var n={label:0,sent:function(){if(i[0]&1)throw i[1];return i[1]},trys:[],ops:[]},o,r,i,a;return a={next:c(0),throw:c(1),return:c(2)},typeof Symbol==="function"&&(a[Symbol.iterator]=function(){return this}),a;function c(e){return function(t){return s([e,t])}}function s(c){if(o)throw new TypeError("Generator is already executing.");while(a&&(a=0,c[0]&&(n=0)),n)try{if(o=1,r&&(i=c[0]&2?r["return"]:c[0]?r["throw"]||((i=r["return"])&&i.call(r),0):r.next)&&!(i=i.call(r,c[1])).done)return i;if(r=0,i)c=[c[0]&2,i.value];switch(c[0]){case 0:case 1:i=c;break;case 4:n.label++;return{value:c[1],done:false};case 5:n.label++;r=c[1];c=[0];continue;case 7:c=n.ops.pop();n.trys.pop();continue;default:if(!(i=n.trys,i=i.length>0&&i[i.length-1])&&(c[0]===6||c[0]===2)){n=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]<i[3])){n.label=c[1];break}if(c[0]===6&&n.label<i[1]){n.label=i[1];i=c;break}if(i&&n.label<i[2]){n.label=i[2];n.ops.push(c);break}if(i[2])n.ops.pop();n.trys.pop();continue}c=t.call(e,n)}catch(e){c=[6,e];r=0}finally{o=i=0}if(c[0]&5)throw c[1];return{value:c[0]?c[1]:void 0,done:true}}};import{r as registerInstance,c as createEvent,h,g as getElement}from"./index-1c5c47b4.js";var cameraModalInstanceCss=":host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict;--inset-width:600px;--inset-height:600px}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0.15)}.content{-webkit-box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);width:var(--inset-width);height:var(--inset-height);max-height:100%}@media only screen and (max-width: 600px){.content{width:100%;height:100%}}";var PWACameraModal=function(){function e(e){var t=this;registerInstance(this,e);this.onPhoto=createEvent(this,"onPhoto",7);this.noDeviceError=createEvent(this,"noDeviceError",7);this.handlePhoto=function(e){return __awaiter(t,void 0,void 0,(function(){return __generator(this,(function(t){this.onPhoto.emit(e);return[2]}))}))};this.handleNoDeviceError=function(e){return __awaiter(t,void 0,void 0,(function(){return __generator(this,(function(t){this.noDeviceError.emit(e);return[2]}))}))};this.facingMode="user";this.hidePicker=false;this.noDevicesText="No camera found";this.noDevicesButtonText="Choose image"}e.prototype.handleBackdropClick=function(e){if(e.target!==this.el){this.onPhoto.emit(null)}};e.prototype.handleComponentClick=function(e){e.stopPropagation()};e.prototype.handleBackdropKeyUp=function(e){if(e.key==="Escape"){this.onPhoto.emit(null)}};e.prototype.render=function(){var e=this;return h("div",{class:"wrapper",onClick:function(t){return e.handleBackdropClick(t)}},h("div",{class:"content"},h("pwa-camera",{onClick:function(t){return e.handleComponentClick(t)},facingMode:this.facingMode,hidePicker:this.hidePicker,handlePhoto:this.handlePhoto,handleNoDeviceError:this.handleNoDeviceError,noDevicesButtonText:this.noDevicesButtonText,noDevicesText:this.noDevicesText})))};Object.defineProperty(e.prototype,"el",{get:function(){return getElement(this)},enumerable:false,configurable:true});return e}();PWACameraModal.style=cameraModalInstanceCss;export{PWACameraModal as pwa_camera_modal_instance};

View File

@ -0,0 +1 @@
var __awaiter=this&&this.__awaiter||function(e,t,n,r){function i(e){return e instanceof n?e:new n((function(t){t(e)}))}return new(n||(n=Promise))((function(n,o){function a(e){try{c(r.next(e))}catch(e){o(e)}}function s(e){try{c(r["throw"](e))}catch(e){o(e)}}function c(e){e.done?n(e.value):i(e.value).then(a,s)}c((r=r.apply(e,t||[])).next())}))};var __generator=this&&this.__generator||function(e,t){var n={label:0,sent:function(){if(o[0]&1)throw o[1];return o[1]},trys:[],ops:[]},r,i,o,a;return a={next:s(0),throw:s(1),return:s(2)},typeof Symbol==="function"&&(a[Symbol.iterator]=function(){return this}),a;function s(e){return function(t){return c([e,t])}}function c(s){if(r)throw new TypeError("Generator is already executing.");while(a&&(a=0,s[0]&&(n=0)),n)try{if(r=1,i&&(o=s[0]&2?i["return"]:s[0]?i["throw"]||((o=i["return"])&&o.call(i),0):i.next)&&!(o=o.call(i,s[1])).done)return o;if(i=0,o)s=[s[0]&2,o.value];switch(s[0]){case 0:case 1:o=s;break;case 4:n.label++;return{value:s[1],done:false};case 5:n.label++;i=s[1];s=[0];continue;case 7:s=n.ops.pop();n.trys.pop();continue;default:if(!(o=n.trys,o=o.length>0&&o[o.length-1])&&(s[0]===6||s[0]===2)){n=0;continue}if(s[0]===3&&(!o||s[1]>o[0]&&s[1]<o[3])){n.label=s[1];break}if(s[0]===6&&n.label<o[1]){n.label=o[1];o=s;break}if(o&&n.label<o[2]){n.label=o[2];n.ops.push(s);break}if(o[2])n.ops.pop();n.trys.pop();continue}s=t.call(e,n)}catch(e){s=[6,e];i=0}finally{r=o=0}if(s[0]&5)throw s[1];return{value:s[0]?s[1]:void 0,done:true}}};import{r as registerInstance,c as createEvent,h}from"./index-1c5c47b4.js";var cameraModalCss=":host{z-index:1000;position:fixed;top:0;left:0;width:100%;height:100%;display:-ms-flexbox;display:flex;contain:strict}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;background-color:rgba(0, 0, 0, 0.15)}.content{-webkit-box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);box-shadow:0px 0px 5px rgba(0, 0, 0, 0.2);width:600px;height:600px}";var PWACameraModal=function(){function e(e){registerInstance(this,e);this.onPhoto=createEvent(this,"onPhoto",7);this.noDeviceError=createEvent(this,"noDeviceError",7);this.facingMode="user";this.hidePicker=false}e.prototype.present=function(){return __awaiter(this,void 0,void 0,(function(){var e;var t=this;return __generator(this,(function(n){e=document.createElement("pwa-camera-modal-instance");e.facingMode=this.facingMode;e.hidePicker=this.hidePicker;e.addEventListener("onPhoto",(function(e){return __awaiter(t,void 0,void 0,(function(){var t;return __generator(this,(function(n){if(!this._modal){return[2]}t=e.detail;this.onPhoto.emit(t);return[2]}))}))}));e.addEventListener("noDeviceError",(function(e){return __awaiter(t,void 0,void 0,(function(){return __generator(this,(function(t){this.noDeviceError.emit(e);return[2]}))}))}));document.body.append(e);this._modal=e;return[2]}))}))};e.prototype.dismiss=function(){return __awaiter(this,void 0,void 0,(function(){return __generator(this,(function(e){if(!this._modal){return[2]}this._modal&&this._modal.parentNode.removeChild(this._modal);this._modal=null;return[2]}))}))};e.prototype.render=function(){return h("div",null)};return e}();PWACameraModal.style=cameraModalCss;export{PWACameraModal as pwa_camera_modal};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{r as registerInstance,h,g as getElement,H as Host}from"./index-1c5c47b4.js";var toastCss=':host{position:fixed;bottom:20px;left:0;right:0;display:-ms-flexbox;display:flex;opacity:0}:host(.in){-webkit-transition:opacity 300ms;transition:opacity 300ms;opacity:1}:host(.out){-webkit-transition:opacity 1s;transition:opacity 1s;opacity:0}.wrapper{-ms-flex:1;flex:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.toast{font-family:-apple-system, system-ui, "Helvetica Neue", Roboto, sans-serif;background-color:#eee;color:black;border-radius:5px;padding:10px 15px;font-size:14px;font-weight:500;-webkit-box-shadow:0px 1px 2px rgba(0, 0, 0, 0.20);box-shadow:0px 1px 2px rgba(0, 0, 0, 0.20)}';var PWAToast=function(){function t(t){registerInstance(this,t);this.message=undefined;this.duration=2e3;this.closing=null}t.prototype.hostData=function(){var t={out:!!this.closing};if(this.closing!==null){t["in"]=!this.closing}return{class:t}};t.prototype.componentDidLoad=function(){var t=this;setTimeout((function(){t.closing=false}));setTimeout((function(){t.close()}),this.duration)};t.prototype.close=function(){var t=this;this.closing=true;setTimeout((function(){t.el.parentNode.removeChild(t.el)}),1e3)};t.prototype.__stencil_render=function(){return h("div",{class:"wrapper"},h("div",{class:"toast"},this.message))};Object.defineProperty(t.prototype,"el",{get:function(){return getElement(this)},enumerable:false,configurable:true});t.prototype.render=function(){return h(Host,this.hostData(),this.__stencil_render())};return t}();PWAToast.style=toastCss;export{PWAToast as pwa_toast};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,21 @@
import { p as promiseResolve, b as bootstrapLazy } from './index-1c5c47b4.js';
export { s as setNonce } from './index-1c5c47b4.js';
/*
Stencil Client Patch Browser v3.4.0 | MIT Licensed | https://stenciljs.com
*/
const patchBrowser = () => {
const importMeta = import.meta.url;
const opts = {};
// TODO(STENCIL-663): Remove code related to deprecated `safari10` field.
if (importMeta !== '') {
opts.resourcesUrl = new URL('.', importMeta).href;
// TODO(STENCIL-661): Remove code related to the dynamic import shim
// TODO(STENCIL-663): Remove code related to deprecated `safari10` field.
}
return promiseResolve(opts);
};
patchBrowser().then(options => {
return bootstrapLazy([["pwa-camera-modal",[[1,"pwa-camera-modal",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"present":[64],"dismiss":[64]}]]],["pwa-action-sheet",[[1,"pwa-action-sheet",{"header":[1],"cancelable":[4],"options":[16],"open":[32]}]]],["pwa-toast",[[1,"pwa-toast",{"message":[1],"duration":[2],"closing":[32]}]]],["pwa-camera",[[1,"pwa-camera",{"facingMode":[1,"facing-mode"],"handlePhoto":[16],"hidePicker":[4,"hide-picker"],"handleNoDeviceError":[16],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"],"photo":[32],"photoSrc":[32],"showShutterOverlay":[32],"flashIndex":[32],"hasCamera":[32],"rotation":[32],"deviceError":[32]}]]],["pwa-camera-modal-instance",[[1,"pwa-camera-modal-instance",{"facingMode":[1,"facing-mode"],"hidePicker":[4,"hide-picker"],"noDevicesText":[1,"no-devices-text"],"noDevicesButtonText":[1,"no-devices-button-text"]},[[16,"keyup","handleBackdropKeyUp"]]]]]], options);
});

Some files were not shown because too many files have changed in this diff Show More