input file
This commit is contained in:
@@ -80,6 +80,7 @@
|
|||||||
<input class="checkbox" type="checkbox" [formField]="loginForm.checkEmail"/>
|
<input class="checkbox" type="checkbox" [formField]="loginForm.checkEmail"/>
|
||||||
<qd-input-text [formField]="loginForm.email"/>
|
<qd-input-text [formField]="loginForm.email"/>
|
||||||
<qd-input-password [formField]="loginForm.password"/>
|
<qd-input-password [formField]="loginForm.password"/>
|
||||||
|
<qd-input-file [formField]="loginForm.file"/>
|
||||||
<qd-button variant="filled" [text]="'Confirm'" [iconName]="'checkmark'" [type]="'submit'" [disabled]="loginForm().invalid()"></qd-button>
|
<qd-button variant="filled" [text]="'Confirm'" [iconName]="'checkmark'" [type]="'submit'" [disabled]="loginForm().invalid()"></qd-button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {QdButton} from '../../../qd-design/src/lib/qd-button/qd-button';
|
|||||||
import {QdIconButton} from '../../../qd-design/src/lib/qd-icon-button/qd-icon-button';
|
import {QdIconButton} from '../../../qd-design/src/lib/qd-icon-button/qd-icon-button';
|
||||||
import {QdInputText} from '../../../qd-design/src/lib/qd-input-text/qd-input-text';
|
import {QdInputText} from '../../../qd-design/src/lib/qd-input-text/qd-input-text';
|
||||||
import {QdInputPassword} from '../../../qd-design/src/lib/qd-input-password/qd-input-password';
|
import {QdInputPassword} from '../../../qd-design/src/lib/qd-input-password/qd-input-password';
|
||||||
|
import {QdInputFile} from '../../../qd-design/src/lib/qd-input-file/qd-input-file';
|
||||||
import {QdCard} from '../../../qd-design/src/lib/qd-card/qd-card';
|
import {QdCard} from '../../../qd-design/src/lib/qd-card/qd-card';
|
||||||
import {NgOptimizedImage} from '@angular/common';
|
import {NgOptimizedImage} from '@angular/common';
|
||||||
import {QdTooltipDirective} from '../../../qd-design/src/directives/qd-tooltip-directive';
|
import {QdTooltipDirective} from '../../../qd-design/src/directives/qd-tooltip-directive';
|
||||||
@@ -11,14 +12,26 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [QdButton, QdIconButton, QdCard, NgOptimizedImage, QdTooltipDirective, QdInputText, QdInputPassword, FormField, FormsModule, ReactiveFormsModule],
|
imports: [
|
||||||
|
QdButton,
|
||||||
|
QdIconButton,
|
||||||
|
QdCard,
|
||||||
|
NgOptimizedImage,
|
||||||
|
QdTooltipDirective,
|
||||||
|
QdInputText,
|
||||||
|
QdInputPassword,
|
||||||
|
QdInputFile,
|
||||||
|
FormField,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule
|
||||||
|
],
|
||||||
templateUrl: './app.html',
|
templateUrl: './app.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
styleUrl: './app.scss'
|
styleUrl: './app.scss'
|
||||||
})
|
})
|
||||||
export class App {
|
export class App {
|
||||||
protected readonly title = signal('qd-design-demo');
|
protected readonly title = signal('qd-design-demo');
|
||||||
loginModel = signal({checkEmail: true, email: '', password: ''});
|
loginModel = signal({checkEmail: true, email: '', password: '', file: new File([], "", undefined)});
|
||||||
loginForm = form(this.loginModel, (schemaPath) => {
|
loginForm = form(this.loginModel, (schemaPath) => {
|
||||||
required(schemaPath.email, {message: 'Email is required'});
|
required(schemaPath.email, {message: 'Email is required'});
|
||||||
email(schemaPath.email, {message: 'Enter a valid email address'});
|
email(schemaPath.email, {message: 'Enter a valid email address'});
|
||||||
@@ -34,5 +47,6 @@ export class App {
|
|||||||
console.log(this.loginModel().email);
|
console.log(this.loginModel().email);
|
||||||
console.log(this.loginModel().checkEmail);
|
console.log(this.loginModel().checkEmail);
|
||||||
console.log(this.loginModel().password);
|
console.log(this.loginModel().password);
|
||||||
|
console.log(this.loginModel().file.bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
427
projects/qd-design/src/lib/qd-input-file/qd-input-file.css
Normal file
427
projects/qd-design/src/lib/qd-input-file/qd-input-file.css
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Roboto");
|
||||||
|
:root {
|
||||||
|
--font-family-title: Roboto, sans-serif;
|
||||||
|
--font-family-content: Roboto, sans-serif;
|
||||||
|
/* display - large */
|
||||||
|
--display-large-font-size: 57px;
|
||||||
|
--display-large-font-weight: 400px;
|
||||||
|
--display-large-letter-spacing: -0.25px;
|
||||||
|
--display-large-line-height: 64px;
|
||||||
|
/* display - medium */
|
||||||
|
--display-medium-font-size: 45px;
|
||||||
|
--display-medium-font-weight: 400px;
|
||||||
|
--display-medium-letter-spacing: 0px;
|
||||||
|
--display-medium-line-height: 52px;
|
||||||
|
/* display - small */
|
||||||
|
--display-small-font-size: 36px;
|
||||||
|
--display-small-font-weight: 400px;
|
||||||
|
--display-small-letter-spacing: 0px;
|
||||||
|
--display-small-line-height: 44px;
|
||||||
|
/* headline - large */
|
||||||
|
--headline-large-font-size: 32px;
|
||||||
|
--headline-large-font-weight: 400px;
|
||||||
|
--headline-large-letter-spacing: 0px;
|
||||||
|
--headline-large-line-height: 40px;
|
||||||
|
/* headline - medium */
|
||||||
|
--headline-medium-font-size: 28px;
|
||||||
|
--headline-medium-font-weight: 400px;
|
||||||
|
--headline-medium-letter-spacing: 0px;
|
||||||
|
--headline-medium-line-height: 36px;
|
||||||
|
/* headline - small */
|
||||||
|
--headline-small-font-size: 24px;
|
||||||
|
--headline-small-font-weight: 400px;
|
||||||
|
--headline-small-letter-spacing: 0px;
|
||||||
|
--headline-small-line-height: 32px;
|
||||||
|
/* title - large */
|
||||||
|
--title-large-font-size: 22px;
|
||||||
|
--title-large-font-weight: 400px;
|
||||||
|
--title-large-letter-spacing: 0px;
|
||||||
|
--title-large-line-height: 28px;
|
||||||
|
/* title - medium */
|
||||||
|
--title-medium-font-size: 18px;
|
||||||
|
--title-medium-font-weight: 500px;
|
||||||
|
--title-medium-letter-spacing: 0.15px;
|
||||||
|
--title-medium-line-height: 26px;
|
||||||
|
/* title - small */
|
||||||
|
--title-small-font-size: 16px;
|
||||||
|
--title-small-font-weight: 500px;
|
||||||
|
--title-small-letter-spacing: 0.10px;
|
||||||
|
--title-small-line-height: 24px;
|
||||||
|
/* body - large */
|
||||||
|
--body-large-font-size: 18px;
|
||||||
|
--body-large-font-weight: 400px;
|
||||||
|
--body-large-letter-spacing: 0.50px;
|
||||||
|
--body-large-line-height: 26px;
|
||||||
|
/* body - medium */
|
||||||
|
--body-medium-font-size: 16px;
|
||||||
|
--body-medium-font-weight: 400px;
|
||||||
|
--body-medium-letter-spacing: 0.25px;
|
||||||
|
--body-medium-line-height: 24px;
|
||||||
|
/* body - small */
|
||||||
|
--body-small-font-size: 14px;
|
||||||
|
--body-small-font-weight: 400px;
|
||||||
|
--body-small-letter-spacing: 0.40px;
|
||||||
|
--body-small-line-height: 20px;
|
||||||
|
/* label - large */
|
||||||
|
--label-large-font-size: 14px;
|
||||||
|
--label-large-font-weight: 500px;
|
||||||
|
--label-large-letter-spacing: 0.10px;
|
||||||
|
--label-large-line-height: 20px;
|
||||||
|
/* label - medium */
|
||||||
|
--label-medium-font-size: 12px;
|
||||||
|
--label-medium-font-weight: 500px;
|
||||||
|
--label-medium-letter-spacing: 0.50px;
|
||||||
|
--label-medium-line-height: 16px;
|
||||||
|
/* label - small */
|
||||||
|
--label-small-font-size: 11px;
|
||||||
|
--label-small-font-weight: 500px;
|
||||||
|
--label-small-letter-spacing: 0.50px;
|
||||||
|
--label-small-line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollable-y {
|
||||||
|
max-height: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-100 {
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.height-100-vh {
|
||||||
|
height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.width-100 {
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.width-100-vw {
|
||||||
|
max-width: 100vw;
|
||||||
|
min-width: 100vw;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
--md-sys-color-primary: rgb(73 93 146);
|
||||||
|
--md-sys-color-surface-tint: rgb(73 93 146);
|
||||||
|
--md-sys-color-on-primary: rgb(255 255 255);
|
||||||
|
--md-sys-color-primary-container: rgb(218 225 255);
|
||||||
|
--md-sys-color-on-primary-container: rgb(49 69 120);
|
||||||
|
--md-sys-color-secondary: rgb(88 94 113);
|
||||||
|
--md-sys-color-on-secondary: rgb(255 255 255);
|
||||||
|
--md-sys-color-secondary-container: rgb(221 226 249);
|
||||||
|
--md-sys-color-on-secondary-container: rgb(65 70 89);
|
||||||
|
--md-sys-color-tertiary: rgb(115 84 113);
|
||||||
|
--md-sys-color-on-tertiary: rgb(255 255 255);
|
||||||
|
--md-sys-color-tertiary-container: rgb(255 214 249);
|
||||||
|
--md-sys-color-on-tertiary-container: rgb(90 61 88);
|
||||||
|
--md-sys-color-error: rgb(186 26 26);
|
||||||
|
--md-sys-color-on-error: rgb(255 255 255);
|
||||||
|
--md-sys-color-error-container: rgb(255 218 214);
|
||||||
|
--md-sys-color-on-error-container: rgb(147 0 10);
|
||||||
|
--md-sys-color-background: rgb(250 248 255);
|
||||||
|
--md-sys-color-on-background: rgb(26 27 33);
|
||||||
|
--md-sys-color-surface: rgb(250 248 255);
|
||||||
|
--md-sys-color-on-surface: rgb(26 27 33);
|
||||||
|
--md-sys-color-surface-variant: rgb(225 226 236);
|
||||||
|
--md-sys-color-on-surface-variant: rgb(69 70 79);
|
||||||
|
--md-sys-color-outline: rgb(117 118 128);
|
||||||
|
--md-sys-color-outline-variant: rgb(197 198 208);
|
||||||
|
--md-sys-color-shadow: rgb(0 0 0);
|
||||||
|
--md-sys-color-scrim: rgb(0 0 0);
|
||||||
|
--md-sys-color-inverse-surface: rgb(47 48 54);
|
||||||
|
--md-sys-color-inverse-on-surface: rgb(241 240 247);
|
||||||
|
--md-sys-color-inverse-primary: rgb(179 197 255);
|
||||||
|
--md-sys-color-primary-fixed: rgb(218 225 255);
|
||||||
|
--md-sys-color-on-primary-fixed: rgb(0 24 73);
|
||||||
|
--md-sys-color-primary-fixed-dim: rgb(179 197 255);
|
||||||
|
--md-sys-color-on-primary-fixed-variant: rgb(49 69 120);
|
||||||
|
--md-sys-color-secondary-fixed: rgb(221 226 249);
|
||||||
|
--md-sys-color-on-secondary-fixed: rgb(21 27 44);
|
||||||
|
--md-sys-color-secondary-fixed-dim: rgb(193 198 221);
|
||||||
|
--md-sys-color-on-secondary-fixed-variant: rgb(65 70 89);
|
||||||
|
--md-sys-color-tertiary-fixed: rgb(255 214 249);
|
||||||
|
--md-sys-color-on-tertiary-fixed: rgb(43 18 43);
|
||||||
|
--md-sys-color-tertiary-fixed-dim: rgb(225 187 220);
|
||||||
|
--md-sys-color-on-tertiary-fixed-variant: rgb(90 61 88);
|
||||||
|
--md-sys-color-surface-dim: rgb(218 217 224);
|
||||||
|
--md-sys-color-surface-bright: rgb(250 248 255);
|
||||||
|
--md-sys-color-surface-container-lowest: rgb(255 255 255);
|
||||||
|
--md-sys-color-surface-container-low: rgb(244 243 250);
|
||||||
|
--md-sys-color-surface-container: rgb(238 237 244);
|
||||||
|
--md-sys-color-surface-container-high: rgb(232 231 239);
|
||||||
|
--md-sys-color-surface-container-highest: rgb(227 226 233);
|
||||||
|
transition: background-color 0.33s, color 0.33s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body:not(.light-theme) {
|
||||||
|
--md-sys-color-primary: rgb(179 197 255);
|
||||||
|
--md-sys-color-surface-tint: rgb(179 197 255);
|
||||||
|
--md-sys-color-on-primary: rgb(25 46 96);
|
||||||
|
--md-sys-color-primary-container: rgb(49 69 120);
|
||||||
|
--md-sys-color-on-primary-container: rgb(218 225 255);
|
||||||
|
--md-sys-color-secondary: rgb(193 198 221);
|
||||||
|
--md-sys-color-on-secondary: rgb(42 48 66);
|
||||||
|
--md-sys-color-secondary-container: rgb(65 70 89);
|
||||||
|
--md-sys-color-on-secondary-container: rgb(221 226 249);
|
||||||
|
--md-sys-color-tertiary: rgb(225 187 220);
|
||||||
|
--md-sys-color-on-tertiary: rgb(66 39 65);
|
||||||
|
--md-sys-color-tertiary-container: rgb(90 61 88);
|
||||||
|
--md-sys-color-on-tertiary-container: rgb(255 214 249);
|
||||||
|
--md-sys-color-error: rgb(255 180 171);
|
||||||
|
--md-sys-color-on-error: rgb(105 0 5);
|
||||||
|
--md-sys-color-error-container: rgb(147 0 10);
|
||||||
|
--md-sys-color-on-error-container: rgb(255 218 214);
|
||||||
|
--md-sys-color-background: rgb(18 19 24);
|
||||||
|
--md-sys-color-on-background: rgb(227 226 233);
|
||||||
|
--md-sys-color-surface: rgb(18 19 24);
|
||||||
|
--md-sys-color-on-surface: rgb(227 226 233);
|
||||||
|
--md-sys-color-surface-variant: rgb(69 70 79);
|
||||||
|
--md-sys-color-on-surface-variant: rgb(197 198 208);
|
||||||
|
--md-sys-color-outline: rgb(143 144 154);
|
||||||
|
--md-sys-color-outline-variant: rgb(69 70 79);
|
||||||
|
--md-sys-color-shadow: rgb(0 0 0);
|
||||||
|
--md-sys-color-scrim: rgb(0 0 0);
|
||||||
|
--md-sys-color-inverse-surface: rgb(227 226 233);
|
||||||
|
--md-sys-color-inverse-on-surface: rgb(47 48 54);
|
||||||
|
--md-sys-color-inverse-primary: rgb(73 93 146);
|
||||||
|
--md-sys-color-primary-fixed: rgb(218 225 255);
|
||||||
|
--md-sys-color-on-primary-fixed: rgb(0 24 73);
|
||||||
|
--md-sys-color-primary-fixed-dim: rgb(179 197 255);
|
||||||
|
--md-sys-color-on-primary-fixed-variant: rgb(49 69 120);
|
||||||
|
--md-sys-color-secondary-fixed: rgb(221 226 249);
|
||||||
|
--md-sys-color-on-secondary-fixed: rgb(21 27 44);
|
||||||
|
--md-sys-color-secondary-fixed-dim: rgb(193 198 221);
|
||||||
|
--md-sys-color-on-secondary-fixed-variant: rgb(65 70 89);
|
||||||
|
--md-sys-color-tertiary-fixed: rgb(255 214 249);
|
||||||
|
--md-sys-color-on-tertiary-fixed: rgb(43 18 43);
|
||||||
|
--md-sys-color-tertiary-fixed-dim: rgb(225 187 220);
|
||||||
|
--md-sys-color-on-tertiary-fixed-variant: rgb(90 61 88);
|
||||||
|
--md-sys-color-surface-dim: rgb(18 19 24);
|
||||||
|
--md-sys-color-surface-bright: rgb(56 57 63);
|
||||||
|
--md-sys-color-surface-container-lowest: rgb(13 14 19);
|
||||||
|
--md-sys-color-surface-container-low: rgb(26 27 33);
|
||||||
|
--md-sys-color-surface-container: rgb(30 31 37);
|
||||||
|
--md-sys-color-surface-container-high: rgb(41 42 47);
|
||||||
|
--md-sys-color-surface-container-highest: rgb(51 52 58);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body.dark-theme {
|
||||||
|
--md-sys-color-primary: rgb(179 197 255);
|
||||||
|
--md-sys-color-surface-tint: rgb(179 197 255);
|
||||||
|
--md-sys-color-on-primary: rgb(25 46 96);
|
||||||
|
--md-sys-color-primary-container: rgb(49 69 120);
|
||||||
|
--md-sys-color-on-primary-container: rgb(218 225 255);
|
||||||
|
--md-sys-color-secondary: rgb(193 198 221);
|
||||||
|
--md-sys-color-on-secondary: rgb(42 48 66);
|
||||||
|
--md-sys-color-secondary-container: rgb(65 70 89);
|
||||||
|
--md-sys-color-on-secondary-container: rgb(221 226 249);
|
||||||
|
--md-sys-color-tertiary: rgb(225 187 220);
|
||||||
|
--md-sys-color-on-tertiary: rgb(66 39 65);
|
||||||
|
--md-sys-color-tertiary-container: rgb(90 61 88);
|
||||||
|
--md-sys-color-on-tertiary-container: rgb(255 214 249);
|
||||||
|
--md-sys-color-error: rgb(255 180 171);
|
||||||
|
--md-sys-color-on-error: rgb(105 0 5);
|
||||||
|
--md-sys-color-error-container: rgb(147 0 10);
|
||||||
|
--md-sys-color-on-error-container: rgb(255 218 214);
|
||||||
|
--md-sys-color-background: rgb(18 19 24);
|
||||||
|
--md-sys-color-on-background: rgb(227 226 233);
|
||||||
|
--md-sys-color-surface: rgb(18 19 24);
|
||||||
|
--md-sys-color-on-surface: rgb(227 226 233);
|
||||||
|
--md-sys-color-surface-variant: rgb(69 70 79);
|
||||||
|
--md-sys-color-on-surface-variant: rgb(197 198 208);
|
||||||
|
--md-sys-color-outline: rgb(143 144 154);
|
||||||
|
--md-sys-color-outline-variant: rgb(69 70 79);
|
||||||
|
--md-sys-color-shadow: rgb(0 0 0);
|
||||||
|
--md-sys-color-scrim: rgb(0 0 0);
|
||||||
|
--md-sys-color-inverse-surface: rgb(227 226 233);
|
||||||
|
--md-sys-color-inverse-on-surface: rgb(47 48 54);
|
||||||
|
--md-sys-color-inverse-primary: rgb(73 93 146);
|
||||||
|
--md-sys-color-primary-fixed: rgb(218 225 255);
|
||||||
|
--md-sys-color-on-primary-fixed: rgb(0 24 73);
|
||||||
|
--md-sys-color-primary-fixed-dim: rgb(179 197 255);
|
||||||
|
--md-sys-color-on-primary-fixed-variant: rgb(49 69 120);
|
||||||
|
--md-sys-color-secondary-fixed: rgb(221 226 249);
|
||||||
|
--md-sys-color-on-secondary-fixed: rgb(21 27 44);
|
||||||
|
--md-sys-color-secondary-fixed-dim: rgb(193 198 221);
|
||||||
|
--md-sys-color-on-secondary-fixed-variant: rgb(65 70 89);
|
||||||
|
--md-sys-color-tertiary-fixed: rgb(255 214 249);
|
||||||
|
--md-sys-color-on-tertiary-fixed: rgb(43 18 43);
|
||||||
|
--md-sys-color-tertiary-fixed-dim: rgb(225 187 220);
|
||||||
|
--md-sys-color-on-tertiary-fixed-variant: rgb(90 61 88);
|
||||||
|
--md-sys-color-surface-dim: rgb(18 19 24);
|
||||||
|
--md-sys-color-surface-bright: rgb(56 57 63);
|
||||||
|
--md-sys-color-surface-container-lowest: rgb(13 14 19);
|
||||||
|
--md-sys-color-surface-container-low: rgb(26 27 33);
|
||||||
|
--md-sys-color-surface-container: rgb(30 31 37);
|
||||||
|
--md-sys-color-surface-container-high: rgb(41 42 47);
|
||||||
|
--md-sys-color-surface-container-highest: rgb(51 52 58);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputTextContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 56px;
|
||||||
|
}
|
||||||
|
.inputTextContainer .input {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--body-medium-font-size);
|
||||||
|
font-weight: var(--body-medium-font-weight);
|
||||||
|
letter-spacing: var(--body-medium-letter-spacing);
|
||||||
|
line-height: var(--body-medium-line-height);
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
background: transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.inputTextContainer .input:-webkit-autofill, .inputTextContainer .input:-webkit-autofill:hover, .inputTextContainer .input:-webkit-autofill:focus, .inputTextContainer .input:-webkit-autofill:active {
|
||||||
|
-webkit-text-fill-color: var(--md-sys-color-on-surface);
|
||||||
|
transition: background-color 5000s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
.inputTextContainer .input::placeholder {
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.inputTextContainer .input:disabled {
|
||||||
|
color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 38%, transparent);
|
||||||
|
}
|
||||||
|
.inputTextContainer .label {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--body-medium-font-size);
|
||||||
|
font-weight: var(--body-medium-font-weight);
|
||||||
|
letter-spacing: var(--body-medium-letter-spacing);
|
||||||
|
line-height: var(--body-medium-line-height);
|
||||||
|
position: absolute;
|
||||||
|
left: 16px;
|
||||||
|
top: 16px;
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1), color 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transform-origin: left top;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.inputTextContainer .outline {
|
||||||
|
position: absolute;
|
||||||
|
inset: -5px 0 0 0;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 1px solid var(--md-sys-color-outline);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1), border-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
.inputTextContainer .notch {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--label-medium-font-size);
|
||||||
|
font-weight: var(--label-medium-font-weight);
|
||||||
|
letter-spacing: var(--label-medium-letter-spacing);
|
||||||
|
line-height: var(--label-medium-line-height);
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
max-width: 0.01px;
|
||||||
|
padding: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: max-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
.inputTextContainer .notch .notch-text {
|
||||||
|
padding-inline: 4px;
|
||||||
|
}
|
||||||
|
.inputTextContainer.focused .label, .inputTextContainer.populated .label {
|
||||||
|
transform: translateY(-24px) translateX(4px) scale(0.75);
|
||||||
|
}
|
||||||
|
.inputTextContainer.focused .notch, .inputTextContainer.populated .notch {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
.inputTextContainer:hover:not(.disabled):not(.focused) .outline {
|
||||||
|
border-color: var(--md-sys-color-on-surface);
|
||||||
|
}
|
||||||
|
.inputTextContainer.focused .label {
|
||||||
|
color: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
.inputTextContainer.focused .outline {
|
||||||
|
border: 2px solid var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
.inputTextContainer.disabled .outline {
|
||||||
|
border-color: color-mix(in srgb, var(--md-sys-color-outline) 12%, transparent);
|
||||||
|
}
|
||||||
|
.inputTextContainer.disabled .label {
|
||||||
|
color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 38%, transparent);
|
||||||
|
}
|
||||||
|
.inputTextContainer.error .outline {
|
||||||
|
border-color: var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
.inputTextContainer.error .label {
|
||||||
|
color: var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
.inputTextContainer.error:hover:not(.disabled) .outline {
|
||||||
|
border-color: var(--md-sys-color-on-error-container);
|
||||||
|
}
|
||||||
|
.inputTextContainer.error.focused .outline {
|
||||||
|
border: 2px solid var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-messages {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--body-small-font-size);
|
||||||
|
font-weight: var(--body-small-font-weight);
|
||||||
|
letter-spacing: var(--body-small-letter-spacing);
|
||||||
|
line-height: var(--body-small-line-height);
|
||||||
|
color: var(--md-sys-color-error);
|
||||||
|
margin: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-reasons {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--body-small-font-size);
|
||||||
|
font-weight: var(--body-small-font-weight);
|
||||||
|
letter-spacing: var(--body-small-letter-spacing);
|
||||||
|
line-height: var(--body-small-line-height);
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
margin: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filename {
|
||||||
|
font-family: var(--font-family-content);
|
||||||
|
font-size: var(--body-large-font-size);
|
||||||
|
font-weight: var(--body-large-font-weight);
|
||||||
|
letter-spacing: var(--body-large-letter-spacing);
|
||||||
|
line-height: var(--body-large-line-height);
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin: auto 16px;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 4px 0;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=file] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=qd-input-file.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":["../../styles/qd_design_system.scss","../../styles/typography/typography.token.scss","../../styles/design/look_and_feel.scss","../../styles/colors/light-theme.scss","../../styles/colors/dark-theme.scss","qd-input-file.scss","../../styles/typography/typography.module.scss"],"names":[],"mappings":"AAQQ;AACA;ACTR;EAEE;EAEA;AAEA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;AACA;EACA;EACA;EACA;EACA;;;AChDF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AFhDF;EGVE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EHnCA;;;AAGF;EACE;IIjBA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;;;AJ1BF;EItBE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AC/CF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAEA;EC2CA;EACA;EACA;EACA;EACA;ED7CE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAIE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;AAIJ;ECcA;EACA;EACA;EACA;EACA;EDhBE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;ECiBA;EACA;EACA;EACA;EACA;EDnBE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAKF;EACE;;AAGF;EACE;;AAKF;EACE;;AAKF;EACE;;AAEF;EACE;;AAKF;EACE;;AAEF;EACE;;AAKF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;;AAKN;EClEE;EACA;EACA;EACA;EACA;EDgEA;EACA;;;AAGF;ECxEE;EACA;EACA;EACA;EACA;EDsEA;EACA;;;AAGF;EC9FE;EACA;EACA;EACA;EACA;ED4FA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE","file":"qd-input-file.css"}
|
||||||
48
projects/qd-design/src/lib/qd-input-file/qd-input-file.html
Normal file
48
projects/qd-design/src/lib/qd-input-file/qd-input-file.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<div class="inputTextContainer"
|
||||||
|
[class.focused]="isFocused()"
|
||||||
|
[class.populated]="isPopulated()"
|
||||||
|
[class.disabled]="disabled()"
|
||||||
|
[class.error]="invalid()"
|
||||||
|
(click)="file.click()">
|
||||||
|
|
||||||
|
<p class="filename">{{ value().name }}</p>
|
||||||
|
|
||||||
|
<input
|
||||||
|
#file
|
||||||
|
class="input"
|
||||||
|
type="file"
|
||||||
|
(input)="onInput($event)"
|
||||||
|
(focus)="onFocus()"
|
||||||
|
[placeholder]="currentPlaceholder()"
|
||||||
|
[disabled]="disabled()"
|
||||||
|
[readonly]="readonly()"
|
||||||
|
[class.invalid]="invalid()"
|
||||||
|
[attr.aria-invalid]="invalid()"
|
||||||
|
(blur)="onBlur()"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label class="label">{{ placeholder() }}</label>
|
||||||
|
|
||||||
|
<fieldset class="outline" aria-hidden="true">
|
||||||
|
<legend class="notch">
|
||||||
|
<span class="notch-text">{{ placeholder() }}</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (invalid()) {
|
||||||
|
<div class="error-messages" role="alert">
|
||||||
|
@for (error of errors(); track error) {
|
||||||
|
<span class="error">{{ error.message }}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (disabled() && disabledReasons().length > 0) {
|
||||||
|
<div class="disabled-reasons">
|
||||||
|
@for (reason of disabledReasons(); track reason) {
|
||||||
|
<span>{{ reason.message }}</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
163
projects/qd-design/src/lib/qd-input-file/qd-input-file.scss
Normal file
163
projects/qd-design/src/lib/qd-input-file/qd-input-file.scss
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
@use '../../styles/qd_design_system' as qd;
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputTextContainer {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 56px;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
@include qd.body-medium;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
background: transparent;
|
||||||
|
overflow: hidden;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:-webkit-autofill,
|
||||||
|
&:-webkit-autofill:hover,
|
||||||
|
&:-webkit-autofill:focus,
|
||||||
|
&:-webkit-autofill:active {
|
||||||
|
-webkit-text-fill-color: var(--md-sys-color-on-surface);
|
||||||
|
transition: background-color 5000s ease-in-out 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 38%, transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
@include qd.body-medium;
|
||||||
|
position: absolute;
|
||||||
|
left: 16px;
|
||||||
|
top: 16px;
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
pointer-events: none;
|
||||||
|
transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1), color 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
transform-origin: left top;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline {
|
||||||
|
position: absolute;
|
||||||
|
inset: -5px 0 0 0;
|
||||||
|
pointer-events: none;
|
||||||
|
border: 1px solid var(--md-sys-color-outline);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1), border-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notch {
|
||||||
|
@include qd.label-medium;
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
max-width: 0.01px;
|
||||||
|
padding: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: max-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
.notch-text {
|
||||||
|
padding-inline: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.focused, &.populated {
|
||||||
|
.label {
|
||||||
|
transform: translateY(-24px) translateX(4px) scale(0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.notch {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.disabled):not(.focused) {
|
||||||
|
.outline {
|
||||||
|
border-color: var(--md-sys-color-on-surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.focused {
|
||||||
|
.label {
|
||||||
|
color: var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
.outline {
|
||||||
|
border: 2px solid var(--md-sys-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
.outline {
|
||||||
|
border-color: color-mix(in srgb, var(--md-sys-color-outline) 12%, transparent);;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
color: color-mix(in srgb, var(--md-sys-color-on-surface-variant) 38%, transparent);;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
.outline {
|
||||||
|
border-color: var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.disabled) .outline {
|
||||||
|
border-color: var(--md-sys-color-on-error-container);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.focused .outline {
|
||||||
|
border: 2px solid var(--md-sys-color-error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-messages {
|
||||||
|
@include qd.body-small;
|
||||||
|
color: var(--md-sys-color-error);
|
||||||
|
margin: 4px 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-reasons {
|
||||||
|
@include qd.body-small;
|
||||||
|
color: var(--md-sys-color-on-surface-variant);
|
||||||
|
margin: 4px 16px
|
||||||
|
}
|
||||||
|
|
||||||
|
.filename {
|
||||||
|
@include qd.body-large;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--md-sys-color-on-surface);
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
margin: auto 16px;
|
||||||
|
max-width: 100%;
|
||||||
|
min-width: 40px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 4px 0;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='file'] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { QdInputFile } from './qd-input-file';
|
||||||
|
|
||||||
|
describe('QdInputFile', () => {
|
||||||
|
let component: QdInputFile;
|
||||||
|
let fixture: ComponentFixture<QdInputFile>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [QdInputFile]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(QdInputFile);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
48
projects/qd-design/src/lib/qd-input-file/qd-input-file.ts
Normal file
48
projects/qd-design/src/lib/qd-input-file/qd-input-file.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import {ChangeDetectionStrategy, Component, computed, input, model, signal} from '@angular/core';
|
||||||
|
import {DisabledReason, FormValueControl, ValidationError, WithOptionalField} from '@angular/forms/signals';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'qd-input-file',
|
||||||
|
templateUrl: './qd-input-file.html',
|
||||||
|
styleUrl: './qd-input-file.scss',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class QdInputFile implements FormValueControl<File> {
|
||||||
|
value = model<File>(new File([], "", undefined));
|
||||||
|
placeholder = input<string>('Placeholder');
|
||||||
|
|
||||||
|
// Writable interaction state - control updates these
|
||||||
|
touched = model<boolean>(false);
|
||||||
|
// Read-only state - form system manages these
|
||||||
|
disabled = input<boolean>(false);
|
||||||
|
disabledReasons = input<readonly WithOptionalField<DisabledReason>[]>([]);
|
||||||
|
readonly = input<boolean>(false);
|
||||||
|
hidden = input<boolean>(false);
|
||||||
|
invalid = input<boolean>(false);
|
||||||
|
errors = input<readonly WithOptionalField<ValidationError>[]>([]);
|
||||||
|
isFocused = signal<boolean>(false);
|
||||||
|
|
||||||
|
isPopulated = computed(() => this.value().name.length > 0);
|
||||||
|
currentPlaceholder = computed(() => this.isFocused() ? this.placeholder() : '');
|
||||||
|
|
||||||
|
onInput(event: Event) {
|
||||||
|
const inputElement: HTMLInputElement = event.target as HTMLInputElement;
|
||||||
|
const files: FileList | null = inputElement.files;
|
||||||
|
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
this.value.set(files[0]);
|
||||||
|
} else {
|
||||||
|
this.value.set(new File([], "", undefined));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocus() {
|
||||||
|
this.isFocused.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
|
this.isFocused.set(false);
|
||||||
|
this.touched.set(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user