@@ -6,18 +6,21 @@ import { html } from 'lit';
66import { property , state } from 'lit/decorators.js' ;
77import { addThemingController } from '../../theming/theming-controller.js' ;
88import IgcButtonComponent from '../button/button.js' ;
9+ import { addSlotController , setSlots } from '../common/controllers/slot.js' ;
910import { registerComponent } from '../common/definitions/register.js' ;
1011import { addI18nController } from '../common/i18n/i18n-controller.js' ;
1112import type { AbstractConstructor } from '../common/mixins/constructor.js' ;
1213import { EventEmitterMixin } from '../common/mixins/event-emitter.js' ;
1314import { FormValueFileListTransformers } from '../common/mixins/forms/form-transformers.js' ;
1415import { createFormValueState } from '../common/mixins/forms/form-value.js' ;
1516import { partMap } from '../common/part-map.js' ;
16- import { bindIf , hasFiles , isEmpty } from '../common/util.js' ;
17+ import { bindIf , hasFiles } from '../common/util.js' ;
1718import {
1819 IgcInputBaseComponent ,
1920 type IgcInputComponentEventMap ,
2021} from '../input/input-base.js' ;
22+ import { styles as baseStyle } from '../input/themes/input.base.css.js' ;
23+ import { styles as shared } from '../input/themes/shared/input.common.css.js' ;
2124import IgcValidationContainerComponent from '../validation-container/validation-container.js' ;
2225import { styles } from './themes/file-input.base.css.js' ;
2326import { all } from './themes/themes.js' ;
@@ -31,6 +34,17 @@ export interface IgcFileInputComponentEventMap extends Omit<
3134 igcChange : CustomEvent < FileList > ;
3235}
3336
37+ const Slots = setSlots (
38+ 'prefix' ,
39+ 'suffix' ,
40+ 'helper-text' ,
41+ 'file-selector-text' ,
42+ 'file-missing-text' ,
43+ 'value-missing' ,
44+ 'custom-error' ,
45+ 'invalid'
46+ ) ;
47+
3448/* blazorSuppress */
3549/**
3650 * @element igc-file-input
@@ -61,7 +75,7 @@ export default class IgcFileInputComponent extends EventEmitterMixin<
6175 AbstractConstructor < IgcInputBaseComponent >
6276> ( IgcInputBaseComponent ) {
6377 public static readonly tagName = 'igc-file-input' ;
64- public static styles = [ ... IgcInputBaseComponent . styles , styles ] ;
78+ public static styles = [ baseStyle , shared , styles ] ;
6579
6680 /* blazorSuppress */
6781 public static register ( ) : void {
@@ -72,45 +86,63 @@ export default class IgcFileInputComponent extends EventEmitterMixin<
7286 ) ;
7387 }
7488
75- protected readonly _i18nController =
76- addI18nController < IFileInputResourceStrings > ( this , {
77- defaultEN : FileInputResourceStringsEN ,
78- } ) ;
89+ //#region Internal attributes and properties
7990
80- protected override get __validators ( ) {
81- return fileValidators ;
82- }
91+ protected override readonly _themes = addThemingController ( this , all ) ;
92+
93+ protected override readonly _slots = addSlotController ( this , {
94+ slots : Slots ,
95+ } ) ;
8396
8497 protected override readonly _formValue = createFormValueState ( this , {
8598 initialValue : null ,
8699 transformers : FormValueFileListTransformers ,
87100 } ) ;
88101
89- @state ( )
90- private _hasActivation = false ;
102+ protected readonly _i18nController = addI18nController ( this , {
103+ defaultEN : FileInputResourceStringsEN ,
104+ } ) ;
105+
106+ protected override get __validators ( ) {
107+ return fileValidators ;
108+ }
91109
92110 private get _fileNames ( ) : string | null {
93- return hasFiles ( this )
94- ? Array . from ( this . files ! )
95- . map ( ( file ) => file . name )
96- . join ( ', ' )
97- : null ;
111+ if ( ! hasFiles ( this ) ) {
112+ return null ;
113+ }
114+
115+ return Array . from ( this . files )
116+ . map ( ( file ) => file . name || 'unnamed' )
117+ . join ( ', ' ) ;
98118 }
99119
120+ /**
121+ * Indicates whether the file picker dialog is currently active.
122+ * Used to manage validation on blur.
123+ */
124+ @state ( )
125+ private _filePickerActive = false ;
126+
127+ //#endregion
128+
129+ //#region Public attributes and properties
130+
100131 /* @tsTwoWayProperty (true, "igcChange", "detail", false) */
101132 /**
102133 * The value of the control.
134+ * Similar to native file input, this property is read-only and cannot be set programmatically.
103135 * @attr
104136 */
105137 @property ( )
106138 public set value ( value : string ) {
107- if ( value === '' && this . input ) {
108- this . input . value = value ;
139+ if ( value === '' && this . _input ) {
140+ this . _input . value = value ;
109141 }
110142 }
111143
112144 public get value ( ) : string {
113- return this . input ?. value ?? '' ;
145+ return this . _input ?. value ?? '' ;
114146 }
115147
116148 /**
@@ -134,24 +166,26 @@ export default class IgcFileInputComponent extends EventEmitterMixin<
134166 this . _i18nController . locale = value ;
135167 }
136168
137- public get locale ( ) {
169+ public get locale ( ) : string {
138170 return this . _i18nController . locale ;
139171 }
140172
141173 /**
142174 * The multiple attribute of the control.
143175 * Used to indicate that a file input allows the user to select more than one file.
176+ *
144177 * @attr
178+ * @default false
145179 */
146- @property ( { type : Boolean } )
180+ @property ( { type : Boolean , reflect : true } )
147181 public multiple = false ;
148182
149183 /**
150184 * The accept attribute of the control.
151185 * Defines the file types as a list of comma-separated values that the file input should accept.
152186 * @attr
153187 */
154- @property ( { type : String } )
188+ @property ( )
155189 public accept = '' ;
156190
157191 /**
@@ -161,64 +195,57 @@ export default class IgcFileInputComponent extends EventEmitterMixin<
161195 @property ( { type : Boolean } )
162196 public override autofocus ! : boolean ;
163197
164- /** @hidden */
165- @property ( { type : Boolean , attribute : false , noAccessor : true } )
166- public override readonly readOnly = false ;
167-
168- /** Returns the selected files, if any; otherwise returns null. */
169- public get files ( ) : FileList | null {
170- return this . input ?. files ?? null ;
198+ /** Returns the list of selected files. */
199+ public get files ( ) : FileList {
200+ return this . _input ?. files ?? new DataTransfer ( ) . files ;
171201 }
172202
173- constructor ( ) {
174- super ( ) ;
175- addThemingController ( this , all ) ;
176- }
203+ //#endregion
204+
205+ //#region Internal methods
177206
178207 protected override _restoreDefaultValue ( ) : void {
179- this . input . value = '' ;
208+ if ( this . _input ) {
209+ this . _input . value = '' ;
210+ }
180211 super . _restoreDefaultValue ( ) ;
181212 }
182213
183- /* c8 ignore next 2 */
184- /** @hidden */
185- public override setSelectionRange ( ) : void { }
214+ //#endregion
186215
187- /* c8 ignore next 2 */
188- /** @hidden */
189- public override setRangeText ( ) : void { }
216+ //#region Event Handlers
190217
191218 private _handleChange ( ) : void {
192- this . _hasActivation = false ;
219+ this . _filePickerActive = false ;
193220 this . _setTouchedState ( ) ;
194221 this . _formValue . setValueAndFormState ( this . files ) ;
195222
196223 this . requestUpdate ( ) ;
197- this . emitEvent ( 'igcChange' , { detail : this . files ! } ) ;
224+ this . emitEvent ( 'igcChange' , { detail : this . files } ) ;
198225 }
199226
200227 private _handleCancel ( ) : void {
201- this . _hasActivation = false ;
228+ this . _filePickerActive = false ;
202229 this . _setTouchedState ( ) ;
203230 this . _validate ( ) ;
204231
205- this . emitEvent ( 'igcCancel' , {
206- detail : this . files ! ,
207- } ) ;
232+ this . emitEvent ( 'igcCancel' , { detail : this . files } ) ;
208233 }
209234
210235 protected override _handleBlur ( ) : void {
211- this . _hasActivation ? this . _validate ( ) : super . _handleBlur ( ) ;
236+ this . _filePickerActive ? this . _validate ( ) : super . _handleBlur ( ) ;
212237 }
213238
214239 /* c8 ignore next 3 */
215240 protected _handleClick ( ) : void {
216- this . _hasActivation = true ;
241+ this . _filePickerActive = true ;
217242 }
218243
219- protected override renderFileParts ( ) {
244+ //#endregion
245+
246+ protected override _renderFileParts ( ) {
220247 const emptyText =
221- this . placeholder ?? this . resourceStrings . file_input_placeholder ! ;
248+ this . placeholder ?? this . resourceStrings . file_input_placeholder ;
222249
223250 return html `
224251 < div part ="file-parts ">
@@ -239,14 +266,14 @@ export default class IgcFileInputComponent extends EventEmitterMixin<
239266 ` ;
240267 }
241268
242- protected renderInput ( ) {
269+ protected override _renderInput ( ) {
243270 const hasNegativeTabIndex = this . getAttribute ( 'tabindex' ) === '-1' ;
244- const hasHelperText = ! isEmpty ( this . _helperText ) ;
271+ const hasHelperText = this . _slots . hasAssignedElements ( 'helper-text' ) ;
245272
246273 return html `
247274 < input
248- id =${ this . inputId }
249- part =${ partMap ( this . resolvePartNames ( 'input' ) ) }
275+ id =${ this . _inputId }
276+ part =${ partMap ( this . _resolvePartNames ( 'input' ) ) }
250277 type="file"
251278 ?disabled=${ this . disabled }
252279 ?required=${ this . required }
0 commit comments