diff --git a/packages/core/src/components-config.d.ts b/packages/core/src/components-config.d.ts index 45ad483..bebeda0 100644 --- a/packages/core/src/components-config.d.ts +++ b/packages/core/src/components-config.d.ts @@ -572,12 +572,12 @@ export namespace Configuration { }; 'pop-radio-group'?: { /** - * If `true`, apply the required property to all `pop-radio`. + * If `true`, apply the required property to every `pop-radio`. * @default false */ required?: boolean; /** - * If `true`, apply the disabled property to all `pop-radio`. + * If `true`, apply the disabled property to every `pop-radio`. * @default false */ disabled?: boolean; diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index b50340d..0b22853 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -645,6 +645,7 @@ export namespace Components { "size"?: Size; /** * The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a ``, it's only used when the toggle participates in a native `
`. + * @default null */ "value"?: File | File[] | null; } @@ -907,7 +908,7 @@ export namespace Components { */ "compare"?: RadioGroupCompareFn | string | null; /** - * If `true`, apply the disabled property to all `pop-radio`. + * If `true`, apply the disabled property to every `pop-radio`. * @config * @default false */ @@ -917,7 +918,7 @@ export namespace Components { */ "name": string; /** - * If `true`, apply the required property to all `pop-radio`. + * If `true`, apply the required property to every `pop-radio`. * @config * @default false */ @@ -1277,7 +1278,7 @@ export namespace Components { * The value of the textarea. * @default "" */ - "value"?: string | null; + "value"?: string; /** * Indicates how the control wraps text. If wrap attribute is in the `hard` state, the `cols` property must be specified. * @config @@ -2645,6 +2646,7 @@ declare namespace LocalJSX { "size"?: Size; /** * The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a ``, it's only used when the toggle participates in a native ``. + * @default null */ "value"?: File | File[] | null; } @@ -2922,7 +2924,7 @@ declare namespace LocalJSX { */ "compare"?: RadioGroupCompareFn | string | null; /** - * If `true`, apply the disabled property to all `pop-radio`. + * If `true`, apply the disabled property to every `pop-radio`. * @config * @default false */ @@ -2940,7 +2942,7 @@ declare namespace LocalJSX { */ "onPopValueChange"?: (event: PopRadioGroupCustomEvent) => void; /** - * If `true`, apply the required property to all `pop-radio`. + * If `true`, apply the required property to every `pop-radio`. * @config * @default false */ @@ -3334,7 +3336,7 @@ declare namespace LocalJSX { * The value of the textarea. * @default "" */ - "value"?: string | null; + "value"?: string; /** * Indicates how the control wraps text. If wrap attribute is in the `hard` state, the `cols` property must be specified. * @config diff --git a/packages/core/src/components/checkbox/checkbox.scss b/packages/core/src/components/checkbox/checkbox.scss index b8112a7..f00c725 100644 --- a/packages/core/src/components/checkbox/checkbox.scss +++ b/packages/core/src/components/checkbox/checkbox.scss @@ -129,9 +129,9 @@ @mixin generate-color($colors...) { @each $color in $colors { - $base: use_color("#{$color}.base"); - $border: use_color("#{$color}.base", 0.2); - $text: use_color("#{$color}.content"); + $base: theme.use_color("#{$color}.base"); + $border: theme.use_color("#{$color}.base", 0.2); + $text: theme.use_color("#{$color}.content"); :host([color="#{$color}"]) { --background: #{$base}; @@ -181,7 +181,7 @@ var(--background) 57% ); background-repeat: no-repeat; - background-color: var(--background, use_color("base.content")); + background-color: var(--background, theme.use_color("base.content")); animation: checkmark var(--animation-duration, 0.2s) ease-out; } diff --git a/packages/core/src/components/checkbox/checkbox.tsx b/packages/core/src/components/checkbox/checkbox.tsx index a94b192..7379333 100644 --- a/packages/core/src/components/checkbox/checkbox.tsx +++ b/packages/core/src/components/checkbox/checkbox.tsx @@ -38,6 +38,8 @@ let checkboxIds = 0; export class Checkbox implements ComponentInterface { private inputId = `pop-cb-${checkboxIds++}`; private inheritedAttributes: Attributes; + + private initialState: boolean | 'indeterminate'; private nativeInput!: HTMLInputElement; @Element() host!: HTMLElement; @@ -83,13 +85,13 @@ export class Checkbox implements ComponentInterface { @Prop({ reflect: true, mutable: true }) checked?: boolean; @Watch('checked') onCheckedChange(newChecked: boolean): void { - this.indeterminate = false; + this.indeterminate = undefined; this.popChange.emit({ checked: newChecked, - value: this.value || '', + value: newChecked ? this.value : null, }); - this.internals.setFormValue(newChecked ? this.value : '', newChecked.toString()); + this.internals.setFormValue(newChecked ? this.value : null, newChecked.toString()); this.internals.ariaChecked = newChecked.toString(); } @@ -153,13 +155,27 @@ export class Checkbox implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.checked = false; + if (this.initialState === 'indeterminate') { + this.indeterminate = true; + return; + } + this.checked = this.initialState; } formStateRestoreCallback(state: string): void { this.checked = state === 'true'; } + connectedCallback(): void { + if (!this.checked) { + return; + } + + const data = new FormData(); + data.set(this.name, this.value); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { this.inheritedAttributes = inheritAriaAttributes(this.host); @@ -172,6 +188,8 @@ export class Checkbox implements ComponentInterface { size: config.get('defaultSize', 'md'), placement: 'start', }); + + this.initialState = this.indeterminate ? 'indeterminate' : this.checked; } /** @@ -238,7 +256,7 @@ export class Checkbox implements ComponentInterface { checked={checked} disabled={disabled} id={inputId} - indeterminate={this.indeterminate} + indeterminate={indeterminate} name={name} onBlur={this.onBlur} onChange={this.onChecked} diff --git a/packages/core/src/components/checkbox/tests/form/index.html b/packages/core/src/components/checkbox/tests/form/index.html new file mode 100644 index 0000000..1c84f53 --- /dev/null +++ b/packages/core/src/components/checkbox/tests/form/index.html @@ -0,0 +1,77 @@ + + + + + + + Checkbox | Poppy-ui + + + + + + + +
+
+

Checkbox - form

+
+ + input label + input label + input label + + submit + reset + +
+
+
+ + + + + diff --git a/packages/core/src/components/input-file/input-file.tsx b/packages/core/src/components/input-file/input-file.tsx index 352843e..168562e 100644 --- a/packages/core/src/components/input-file/input-file.tsx +++ b/packages/core/src/components/input-file/input-file.tsx @@ -11,7 +11,7 @@ import { Watch, h, } from '@stencil/core'; -import type { Size } from 'src/interface'; +import type { FormAssociatedInterface, Size } from 'src/interface'; import { componentConfig, config } from '#config'; import { type Attributes, hostContext, inheritAriaAttributes, inheritAttributes } from '#utils/helpers'; import { Show } from '../Show'; @@ -33,12 +33,11 @@ let inputIds = 0; shadow: true, formAssociated: true, }) -export class InputFile implements ComponentInterface { +export class InputFile implements ComponentInterface, FormAssociatedInterface { private inputId = `pop-input-file-${inputIds++}`; private inheritedAttributes: Attributes; private nativeInput!: HTMLInputElement; - private debounceTimer: NodeJS.Timeout; @Element() host!: HTMLElement; @@ -47,7 +46,7 @@ export class InputFile implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * The value of the toggle does not mean if it's checked or not, use the `checked` @@ -55,13 +54,17 @@ export class InputFile implements ComponentInterface { * * The value of a toggle is analogous to the value of a ``, * it's only used when the toggle participates in a native `
`. + * + * @default null */ - @Prop({ mutable: true }) value?: File | File[] | null; + @Prop({ mutable: true }) value?: File | File[] | null = new File([], ''); @Watch('value') - onValueChange(file: File): void { + onValueChange(value: File | File[]): void { const data = new FormData(); - data.set(this.name, file); - + const files = Array.isArray(value) ? value : [value]; + for (const file of files) { + data.set(this.name, file); + } this.internals.setFormValue(data, data); } @@ -72,7 +75,7 @@ export class InputFile implements ComponentInterface { * @config * @default false */ - @Prop({ mutable: true }) multiple?: boolean; + @Prop({ reflect: true, mutable: true }) multiple?: boolean; /** * If `true`, the user must fill in a value before submitting a form. @@ -158,19 +161,28 @@ export class InputFile implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.value = null; - this.nativeInput.value = null; + this.value = new File([], ''); + this.nativeInput.value = ''; } formStateRestoreCallback(state: File): void { this.value = state; } + connectedCallback(): void { + if (this.value === null) { + return; + } + const files = Array.isArray(this.value) ? this.value : [this.value]; + this.onValueChange(files); + } + componentWillLoad(): void { this.inheritedAttributes = { ...inheritAriaAttributes(this.host), ...inheritAttributes(this.host, ['tabindex', 'title', 'data-form-type']), }; + componentConfig.apply(this, 'pop-input-file', { multiple: false, required: false, @@ -182,18 +194,6 @@ export class InputFile implements ComponentInterface { }); } - // TODO: Tester si ça fonctionne - componentDidLoad(): void { - const { value } = this; - const files = Array.isArray(value) ? value : [value]; - - files.forEach((file, idx) => (this.nativeInput.files[idx] = file)); - } - - disconnectedCallback(): void { - clearTimeout(this.debounceTimer); - } - /** * Sets focus on the native `input` in `pop-input-file`. Use this method instead of the global * `input.focus()`. @@ -208,8 +208,11 @@ export class InputFile implements ComponentInterface { } private onChange = (): void => { + const files = this.getValue(); + this.value = files; + this.popChange.emit({ - value: this.getValue(), + value: files, }); }; diff --git a/packages/core/src/components/input-file/tests/form/index.html b/packages/core/src/components/input-file/tests/form/index.html new file mode 100644 index 0000000..6724243 --- /dev/null +++ b/packages/core/src/components/input-file/tests/form/index.html @@ -0,0 +1,76 @@ + + + + + + + Input Basic | Poppy-ui + + + + + + + +
+
+

Input - form

+
+ + input label + input label + + submit + reset + +
+
+
+ + + + + diff --git a/packages/core/src/components/input/input.tsx b/packages/core/src/components/input/input.tsx index 2de74a8..be53e5c 100644 --- a/packages/core/src/components/input/input.tsx +++ b/packages/core/src/components/input/input.tsx @@ -11,7 +11,7 @@ import { Watch, h, } from '@stencil/core'; -import type { AutoCapitalize, EnterKeyHint, KeyboardType, Size } from 'src/interface'; +import type { AutoCapitalize, EnterKeyHint, FormAssociatedInterface, KeyboardType, Size } from 'src/interface'; import { componentConfig, config } from '#config'; import { type Attributes, hostContext, inheritAriaAttributes, inheritAttributes } from '#utils/helpers'; import { Show } from '../Show'; @@ -36,10 +36,11 @@ let inputIds = 0; shadow: true, formAssociated: true, }) -export class Input implements ComponentInterface { +export class Input implements ComponentInterface, FormAssociatedInterface { private inputId = `pop-input-${inputIds++}`; private inheritedAttributes: Attributes; + private initialValue: string | number | null; private nativeInput!: HTMLInputElement; private isComposing = false; private debounceTimer: NodeJS.Timeout; @@ -51,7 +52,7 @@ export class Input implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * The type of control to display. The default type is text. @@ -307,13 +308,20 @@ export class Input implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.value = ''; + this.value = this.initialValue; } formStateRestoreCallback(state: string): void { this.value = state; } + connectedCallback(): void { + const value = this.getValue(); + const data = new FormData(); + data.set(this.name, value); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { this.inheritedAttributes = { ...inheritAriaAttributes(this.host), @@ -340,6 +348,8 @@ export class Input implements ComponentInterface { if (this.counter && this.maxLength === undefined) { console.warn(`The 'maxLength' attribut must be specified.`); } + + this.initialValue = this.value; } disconnectedCallback(): void { diff --git a/packages/core/src/components/input/tests/form/index.html b/packages/core/src/components/input/tests/form/index.html new file mode 100644 index 0000000..f669499 --- /dev/null +++ b/packages/core/src/components/input/tests/form/index.html @@ -0,0 +1,76 @@ + + + + + + + Input Basic | Poppy-ui + + + + + + + +
+
+

Input - form

+
+
+ input label + input label + + submit + reset +
+
+
+
+ + + + + diff --git a/packages/core/src/components/radio-group/radio-group.tsx b/packages/core/src/components/radio-group/radio-group.tsx index 758be50..ae459ac 100644 --- a/packages/core/src/components/radio-group/radio-group.tsx +++ b/packages/core/src/components/radio-group/radio-group.tsx @@ -24,7 +24,8 @@ let radioGroupIds = 0; formAssociated: true, }) export class RadioGroup implements ComponentInterface { - private inputId = `ion-rg-${radioGroupIds++}`; + private inputId = `pop-rg-${radioGroupIds++}`; + private initialValue: any; @Element() host!: HTMLElement; @@ -33,7 +34,7 @@ export class RadioGroup implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * the value of the radio group. @@ -54,7 +55,7 @@ export class RadioGroup implements ComponentInterface { } /** - * If `true`, apply the required property to all `pop-radio`. + * If `true`, apply the required property to every `pop-radio`. * * @config * @default false @@ -62,7 +63,7 @@ export class RadioGroup implements ComponentInterface { @Prop({ reflect: true, mutable: true }) required?: boolean; /** - * If `true`, apply the disabled property to all `pop-radio`. + * If `true`, apply the disabled property to every `pop-radio`. * * @config * @default false @@ -125,13 +126,22 @@ export class RadioGroup implements ComponentInterface { @Event() popValueChange!: EventEmitter; formResetCallback(): void { - this.value = undefined; + this.value = this.initialValue; } formStateRestoreCallback(state: string): void { this.value = state; } + connectedCallback(): void { + if (!this.value) { + return; + } + const data = new FormData(); + data.set(this.name, this.value.toString()); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { componentConfig.apply(this, 'pop-radio-group', { required: false, @@ -139,6 +149,8 @@ export class RadioGroup implements ComponentInterface { allowEmpty: false, size: config.get('defaultSize', 'md'), }); + + this.initialValue = this.value; } componentDidLoad(): void { @@ -176,6 +188,7 @@ export class RadioGroup implements ComponentInterface { if (!radio) return; radio.checked = true; + this.value = radio.value; } private get radios() { diff --git a/packages/core/src/components/radio-group/tests/form/index.html b/packages/core/src/components/radio-group/tests/form/index.html new file mode 100644 index 0000000..4afdfea --- /dev/null +++ b/packages/core/src/components/radio-group/tests/form/index.html @@ -0,0 +1,107 @@ + + + + + + + Radio | Poppy-ui + + + + + + + +
+
+

Toggle - form

+
+
+ + Custom + Option 1 + Option 2 + Option 3 + Option 4 + + + Custom default + Option 1 + Option 2 + Option 3 + Option 4 + + + Custom default + Option 1 + Option 2 + Option 3 + Option 4 + +
+ + + + +
+ +
+ + + + +
+ submit + reset +
+
+
+
+ + + + + diff --git a/packages/core/src/components/radio/radio.scss b/packages/core/src/components/radio/radio.scss index 7995f1f..760ca3f 100644 --- a/packages/core/src/components/radio/radio.scss +++ b/packages/core/src/components/radio/radio.scss @@ -121,8 +121,8 @@ $base: use_color("base.100"); @mixin generate-color($colors...) { @each $color in $colors { - $border: use_color("#{$color}.base", var(--border-opacity, 1)); - $background: use_color("#{$color}.base", var(--opacity, 1)); + $border: theme.use_color("#{$color}.base", var(--border-opacity, 1)); + $background: theme.use_color("#{$color}.base", var(--opacity, 1)); :host([color="#{$color}"]) { --border-color: #{$base}; @@ -133,8 +133,8 @@ $base: use_color("base.100"); @include generate-color(primary, secondary, accent, info, success, warning, error); :host(:not([color])) { - $border: use_color("base.content", var(--border-opacity, 1)); - $background: use_color("base.content", var(--opacity, 1)); + $border: theme.use_color("base.content", var(--border-opacity, 1)); + $background: theme.use_color("base.content", var(--opacity, 1)); --border-color: #{$border}; --background: #{$background}; diff --git a/packages/core/src/components/radio/radio.tsx b/packages/core/src/components/radio/radio.tsx index 3924d6c..ad2b4e8 100644 --- a/packages/core/src/components/radio/radio.tsx +++ b/packages/core/src/components/radio/radio.tsx @@ -42,7 +42,7 @@ export class Radio implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * The value of the radio does not mean if it's checked or not, use the `checked` diff --git a/packages/core/src/components/range/range.scss b/packages/core/src/components/range/range.scss index 6234378..108d4c5 100644 --- a/packages/core/src/components/range/range.scss +++ b/packages/core/src/components/range/range.scss @@ -16,7 +16,7 @@ width: 100%; height: var(--track-size); border-radius: var(--border-radius, #{theme.use_radius(xl)}); - background-color: var(--track-color, oklch(var(--base-content) / var(--track-opacity, 0.2))); + background-color: var(--track-color, oklch(var(--base-content-oklck) / var(--track-opacity, 0.2))); } @mixin thumb { width: var(--thumb-size); @@ -118,7 +118,7 @@ @mixin generate-color($colors...) { @each $color in $colors { - $base: use_color("#{$color}.base", var(--active-track-opacity, 1)); + $base: theme.use_color("#{$color}.base", var(--active-track-opacity, 1)); :host([color="#{$color}"]) { --active-track-color: #{$base}; @@ -128,8 +128,8 @@ @include generate-color(primary, secondary, accent, info, success, warning, error); :host(:not([color])) { - $color: use_color("base.content", var(--track-opacity, 0.2)); - $active: use_color("base.content", var(--active-track-opacity, 1)); + $color: theme.use_color("base.content", var(--track-opacity, 0.2)); + $active: theme.use_color("base.content", var(--active-track-opacity, 1)); --track-color: #{$color}; --active-track-color: #{$active}; @@ -139,7 +139,7 @@ // ---------------------------------------------------------------- :host([disabled]) > input { - $active: use_color("base.content", var(--active-track-opacity, 1)); + $active: theme.use_color("base.content", var(--active-track-opacity, 1)); --active-track-color: #{$active}; cursor: not-allowed; diff --git a/packages/core/src/components/range/range.tsx b/packages/core/src/components/range/range.tsx index abb8528..0d8962f 100644 --- a/packages/core/src/components/range/range.tsx +++ b/packages/core/src/components/range/range.tsx @@ -33,6 +33,7 @@ export class Range implements ComponentInterface { private inputId = `pop-range-${rangeIds++}`; private inheritedAttributes: Attributes; + private initialValue: number; private nativeInput!: HTMLInputElement; private debounceTimer: NodeJS.Timeout; @@ -43,7 +44,7 @@ export class Range implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * The value of the toggle does not mean if it's checked or not, use the `checked` @@ -52,7 +53,7 @@ export class Range implements ComponentInterface { * The value of a toggle is analogous to the value of a ``, * it's only used when the toggle participates in a native `
`. */ - @Prop({ mutable: true }) value?: number | null; + @Prop({ reflect: true, mutable: true }) value?: number | null = null; @Watch('value') onValueChange(value: number) { const data = new FormData(); @@ -149,13 +150,19 @@ export class Range implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.value = 0; + this.value = this.initialValue; } formStateRestoreCallback(state: string) { this.value = +state; } + connectedCallback(): void { + const data = new FormData(); + data.set(this.name, this.value?.toString()); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { this.inheritedAttributes = inheritAriaAttributes(this.host); @@ -168,6 +175,8 @@ export class Range implements ComponentInterface { size: config.get('defaultSize', 'md'), debounce: 0, }); + this.value ??= this.max / 2; + this.initialValue = this.value; } disconnectedCallback(): void { @@ -225,7 +234,7 @@ export class Range implements ComponentInterface { required={this.required} step={this.step} type="range" - value={this.value} + value={this.value === null ? this.max / 2 : this.value} {...this.inheritedAttributes} /> diff --git a/packages/core/src/components/range/tests/form/index.html b/packages/core/src/components/range/tests/form/index.html new file mode 100644 index 0000000..1564e12 --- /dev/null +++ b/packages/core/src/components/range/tests/form/index.html @@ -0,0 +1,76 @@ + + + + + + + Range | Poppy-ui + + + + + + + +
+
+

Toggle - form

+
+ + input label + input label + + submit + reset + +
+
+
+ + + + + diff --git a/packages/core/src/components/select/select.scss b/packages/core/src/components/select/select.scss index e2eeb52..d5c3f37 100644 --- a/packages/core/src/components/select/select.scss +++ b/packages/core/src/components/select/select.scss @@ -73,6 +73,8 @@ $minWidth: 12rem; background-color: transparent; color: var(--color); + font-family: var(--font); + user-select: none; } diff --git a/packages/core/src/components/select/select.tsx b/packages/core/src/components/select/select.tsx index ec2bee0..b807b2e 100644 --- a/packages/core/src/components/select/select.tsx +++ b/packages/core/src/components/select/select.tsx @@ -34,6 +34,7 @@ export class Select implements ComponentInterface, FormAssociatedInterface { private inputId = `pop-select-${selectIds++}`; private inheritedAttributes: Attributes; + private initialValues: any | any[] | null; private popoverRef: HTMLPopPopoverElement; private triggerRef: HTMLButtonElement; @@ -47,7 +48,7 @@ export class Select implements ComponentInterface, FormAssociatedInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * Instructional text that shows before the input has a value. @@ -60,7 +61,7 @@ export class Select implements ComponentInterface, FormAssociatedInterface { * The value of a select is analogous to the value of a ` + + + + + + + + submit + reset + + + + + + + + + diff --git a/packages/core/src/components/textarea/tests/form/index.html b/packages/core/src/components/textarea/tests/form/index.html new file mode 100644 index 0000000..5ca7054 --- /dev/null +++ b/packages/core/src/components/textarea/tests/form/index.html @@ -0,0 +1,76 @@ + + + + + + + Input Basic | Poppy-ui + + + + + + + +
+
+

Textarea - form

+
+
+ input label + input label + + submit + reset +
+
+
+
+ + + + + diff --git a/packages/core/src/components/textarea/textarea.tsx b/packages/core/src/components/textarea/textarea.tsx index c752b92..7469158 100644 --- a/packages/core/src/components/textarea/textarea.tsx +++ b/packages/core/src/components/textarea/textarea.tsx @@ -12,7 +12,7 @@ import { Watch, h, } from '@stencil/core'; -import type { AutoCapitalize, EnterKeyHint, KeyboardType, Size } from 'src/interface'; +import type { AutoCapitalize, EnterKeyHint, FormAssociatedInterface, KeyboardType, Size } from 'src/interface'; import { componentConfig, config } from '#config'; import { type Attributes, hostContext, inheritAriaAttributes } from '#utils/helpers'; import { Show } from '../Show'; @@ -34,11 +34,12 @@ let textareaIds = 0; shadow: true, formAssociated: true, }) -export class Textarea implements ComponentInterface { +export class Textarea implements ComponentInterface, FormAssociatedInterface { private inputId = `pop-textarea-${textareaIds++}`; private inheritedAttributes: Attributes; private resizeObserver?: MutationObserver; + private initialValue: string; private nativeInput!: HTMLTextAreaElement; private debounceTimer: NodeJS.Timeout; @@ -51,7 +52,7 @@ export class Textarea implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * Instructional text that shows before the input has a value. @@ -63,7 +64,7 @@ export class Textarea implements ComponentInterface { * * @default "" */ - @Prop({ mutable: true }) value?: string | null = ''; + @Prop({ mutable: true }) value?: string = ''; @Watch('value') onValueChange(value: string): void { const data = new FormData(); @@ -267,13 +268,20 @@ export class Textarea implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.value = ''; + this.value = this.initialValue; } formStateRestoreCallback(state: string): void { this.value = state; } + connectedCallback(): void { + const value = this.getValue(); + const data = new FormData(); + data.set(this.name, value); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { this.inheritedAttributes = inheritAriaAttributes(this.host); @@ -294,6 +302,8 @@ export class Textarea implements ComponentInterface { if (this.wrap === 'hard' && this.cols === undefined) { console.warn(`The 'cols' attribut must be specified.`); } + + this.initialValue = this.value; } componentDidLoad(): void { @@ -410,11 +420,10 @@ export class Textarea implements ComponentInterface { required={this.required} rows={this.rows} spellcheck={this.spellcheck} + value={value} wrap={this.wrap} {...this.inheritedAttributes} - > - {value} - + />
diff --git a/packages/core/src/components/toggle/tests/form/index.html b/packages/core/src/components/toggle/tests/form/index.html new file mode 100644 index 0000000..bc8f1e5 --- /dev/null +++ b/packages/core/src/components/toggle/tests/form/index.html @@ -0,0 +1,77 @@ + + + + + + + Input Basic | Poppy-ui + + + + + + + +
+
+

Toggle - form

+
+
+ input label + input label + input label + + submit + reset +
+
+
+
+ + + + + diff --git a/packages/core/src/components/toggle/toggle.tsx b/packages/core/src/components/toggle/toggle.tsx index dde775f..85d5556 100644 --- a/packages/core/src/components/toggle/toggle.tsx +++ b/packages/core/src/components/toggle/toggle.tsx @@ -11,7 +11,7 @@ import { Watch, h, } from '@stencil/core'; -import type { Size } from 'src/interface'; +import type { FormAssociatedInterface, Size } from 'src/interface'; import { componentConfig, config } from '#config'; import { type Attributes, inheritAriaAttributes } from '#utils/helpers'; import { Show } from '../Show'; @@ -33,9 +33,11 @@ let toggleIds = 0; shadow: true, formAssociated: true, }) -export class Toggle implements ComponentInterface { +export class Toggle implements ComponentInterface, FormAssociatedInterface { private inputId = `pop-tg-${toggleIds++}`; private inheritedAttributes: Attributes; + + private initialState: boolean | 'indeterminate'; private nativeInput!: HTMLInputElement; @Element() host!: HTMLElement; @@ -45,7 +47,7 @@ export class Toggle implements ComponentInterface { /** * The name of the control, which is submitted with the form data. */ - @Prop() name: string = this.inputId; + @Prop({ reflect: true }) name: string = this.inputId; /** * The value of the toggle does not mean if it's checked or not, use the `checked` @@ -83,12 +85,16 @@ export class Toggle implements ComponentInterface { onCheckedChange(checked: boolean): void { this.indeterminate = false; const data = new FormData(); - data.set(this.name, checked.toString()); + if (this.checked) { + data.set(this.name, this.value); + } else { + data.delete(this.name); + } this.internals.setFormValue(data, data); this.popChange.emit({ checked, - value: this.value || '', + value: checked ? this.value : null, }); } @@ -142,13 +148,27 @@ export class Toggle implements ComponentInterface { @Event() popBlur: EventEmitter; formResetCallback(): void { - this.checked = false; + if (this.initialState === 'indeterminate') { + this.indeterminate = true; + return; + } + this.checked = this.initialState; } formStateRestoreCallback(state: string): void { this.checked = state === 'true'; } + connectedCallback(): void { + if (!this.checked) { + return; + } + + const data = new FormData(); + data.set(this.name, this.value); + this.internals.setFormValue(data, data); + } + componentWillLoad(): void { this.inheritedAttributes = inheritAriaAttributes(this.host); @@ -160,6 +180,8 @@ export class Toggle implements ComponentInterface { disabled: false, size: config.get('defaultSize', 'md'), }); + + this.initialState = this.indeterminate ? 'indeterminate' : this.checked; } /** diff --git a/packages/core/src/components/toggle/toggle.type.ts b/packages/core/src/components/toggle/toggle.type.ts index 8feeecf..8c8ac9e 100644 --- a/packages/core/src/components/toggle/toggle.type.ts +++ b/packages/core/src/components/toggle/toggle.type.ts @@ -4,7 +4,7 @@ export type ToggleColor = Color | 'ghost'; export interface ToggleChangeEventDetail { checked: boolean; - value: T; + value: T | null; } export interface ToggleCustomEvent extends CustomEvent { diff --git a/packages/core/src/css/core.scss b/packages/core/src/css/core.scss index eb557fd..54ec670 100644 --- a/packages/core/src/css/core.scss +++ b/packages/core/src/css/core.scss @@ -4,7 +4,7 @@ :root { /** * Loop through each color object from the - * `poppy.theme.default.scss` file + * `theme/_generator.scss` file * and generate CSS Variables for each color. */ @each $color-name, $value in theme.$colors { @@ -21,5 +21,5 @@ body { background: var(--base-100); color: var(--base-content); - font-family: var(--font); + font-family: var(--font, #{theme.$font}); } diff --git a/packages/core/src/css/normalize.scss b/packages/core/src/css/normalize.scss index d373cbc..4edbf34 100644 --- a/packages/core/src/css/normalize.scss +++ b/packages/core/src/css/normalize.scss @@ -135,13 +135,10 @@ input[type="submit"] { a, a div, a span, -a ion-icon, -a ion-label, button, button div, button span, button pop-label, -.ion-tappable, [tappable], [tappable] div, [tappable] span, diff --git a/packages/core/src/themes/functions/poppy.shadows.scss b/packages/core/src/themes/functions/poppy.shadows.scss index ce2f7f5..d912d22 100644 --- a/packages/core/src/themes/functions/poppy.shadows.scss +++ b/packages/core/src/themes/functions/poppy.shadows.scss @@ -2,7 +2,7 @@ @function use_shadow($size: md) { $value: map-get( - $map: $shadows, + $map: theme.$shadows, $key: $size, );