|
|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
import { context, type WithRefOpts } from '../../provider';
|
|
|
|
|
import { boolToEmptyStrOrUndef, boolToStr } from '$adom';
|
|
|
|
|
import { boolToStr } from '$adom';
|
|
|
|
|
import { readableActive, state, type ActiveProps, type StateProps } from '$libs/reactive';
|
|
|
|
|
import type { OnChangeFn, SomaEvent } from '../../types';
|
|
|
|
|
import type { FieldInputType, FieldState } from './types';
|
|
|
|
|
@ -56,12 +56,32 @@ export class FieldProvider {
|
|
|
|
|
private constructor(opts: FieldOpts) {
|
|
|
|
|
this.opts = opts;
|
|
|
|
|
this.soma = Soma.require();
|
|
|
|
|
this.runtime = this.soma.runtime(fieldMorfo, {});
|
|
|
|
|
this.runtime = this.soma.runtime(fieldMorfo, {
|
|
|
|
|
states: {
|
|
|
|
|
state: () => this.effectiveState
|
|
|
|
|
},
|
|
|
|
|
props: {
|
|
|
|
|
name: () => this.opts.name.current,
|
|
|
|
|
disabled: () => this.isDisabled,
|
|
|
|
|
readonly: () => this.isReadonly,
|
|
|
|
|
required: () => this.isRequired,
|
|
|
|
|
invalid: () => this.isInvalid,
|
|
|
|
|
dirty: () => this.isDirty,
|
|
|
|
|
touched: () => this.isTouched
|
|
|
|
|
},
|
|
|
|
|
parts: {
|
|
|
|
|
input: () => this.inputId.current || undefined,
|
|
|
|
|
label: () => this.labelId.current || undefined,
|
|
|
|
|
'helper-text': () => this.helperId.current || undefined,
|
|
|
|
|
'error-text': () => this.errorId.current || undefined
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.runtimePart = this.runtime.part('provider', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this,
|
|
|
|
|
context: FieldProvider.ctx
|
|
|
|
|
context: FieldProvider.ctx,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Auto-register / unregister with the parent Form (if `name` is set).
|
|
|
|
|
@ -153,14 +173,6 @@ export class FieldProvider {
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'data-field-name': this.opts.name.current,
|
|
|
|
|
'data-state': this.effectiveState,
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
|
|
|
|
|
'data-readonly': boolToEmptyStrOrUndef(this.isReadonly),
|
|
|
|
|
'data-required': boolToEmptyStrOrUndef(this.isRequired),
|
|
|
|
|
'data-invalid': boolToEmptyStrOrUndef(this.isInvalid),
|
|
|
|
|
'data-dirty': boolToEmptyStrOrUndef(this.isDirty),
|
|
|
|
|
'data-touched': boolToEmptyStrOrUndef(this.isTouched),
|
|
|
|
|
onfocusout: this.onfocusout
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
@ -185,18 +197,15 @@ export class FieldLabelProvider {
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('label', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
owner: this,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
this.provider.labelId.current = opts.id.current;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
for: this.provider.inputId.current || undefined,
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled),
|
|
|
|
|
'data-invalid': boolToEmptyStrOrUndef(this.provider.isInvalid),
|
|
|
|
|
'data-required': boolToEmptyStrOrUndef(this.provider.isRequired)
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -220,16 +229,14 @@ export class FieldControlProvider {
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('control', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
owner: this,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'data-state': this.provider.effectiveState,
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled),
|
|
|
|
|
'data-invalid': boolToEmptyStrOrUndef(this.provider.isInvalid)
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -320,7 +327,8 @@ export class FieldHelperTextProvider {
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('helper-text', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
owner: this,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
this.provider.helperId.current = opts.id.current;
|
|
|
|
|
|
|
|
|
|
@ -334,8 +342,7 @@ export class FieldHelperTextProvider {
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled)
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@ -359,7 +366,8 @@ export class FieldErrorTextProvider {
|
|
|
|
|
this.runtimePart = this.provider.runtime.part('error-text', {
|
|
|
|
|
id: opts.id,
|
|
|
|
|
ref: opts.ref,
|
|
|
|
|
owner: this
|
|
|
|
|
owner: this,
|
|
|
|
|
syncAttrs: true
|
|
|
|
|
});
|
|
|
|
|
this.provider.errorId.current = opts.id.current;
|
|
|
|
|
|
|
|
|
|
@ -373,9 +381,7 @@ export class FieldErrorTextProvider {
|
|
|
|
|
|
|
|
|
|
readonly props = $derived.by(() =>
|
|
|
|
|
this.runtimePart.assert({
|
|
|
|
|
...this.runtimePart.props,
|
|
|
|
|
role: 'alert' as const,
|
|
|
|
|
'aria-live': 'polite' as const
|
|
|
|
|
...this.runtimePart.props
|
|
|
|
|
} as const)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|