Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$mol_form_draft: array support #637

Merged
merged 21 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions check/list/list.view.tree
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
$mol_check_list $mol_view
dictionary *
Option* $mol_check
checked? <=> option_checked*? false
label <= option_label* /
Expand Down
14 changes: 14 additions & 0 deletions check/list/list.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ namespace $.$$ {
return {}
}

override dictionary(next?: Record<string, boolean>) {
return next ?? {}
}

override option_checked(id: string, next?: boolean | null) {
const prev = this.dictionary()
if (next === undefined) return prev[id] ?? null

const next_rec = { ... prev, [id]: next } as Record<string, boolean>
if (next === null) delete next_rec[id]

return this.dictionary(next_rec)[id] ?? null
}

@ $mol_mem
keys(): readonly string[] {
return Object.keys( this.options() )
Expand Down
31 changes: 31 additions & 0 deletions form/draft/demo/demo.view.tree
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ $mol_form_draft_demo_article $mol_object2
type? \
adult? false
content? \
friends? /string
hobbies? *

$mol_form_draft_demo $mol_example
title \Article draft form demo
Expand All @@ -19,7 +21,10 @@ $mol_form_draft_demo $mol_example
submit? => publish?
submit_allowed => publish_allowed
value_str*? => value_str*?
list_string*? => list_string*?
dictionary_bool*? => dictionary_bool*?
changed => changed
reset? => reset?
form_fields /
<= Title_field $mol_form_field
name \Title
Expand Down Expand Up @@ -54,19 +59,45 @@ $mol_form_draft_demo $mol_example
Content <= Content $mol_textarea
hint \Long long story..
value? <=> value_str*content?
<= Hobbies_field $mol_form_field
name \Hobbies
Content <= Hobbies $mol_check_list
dictionary? <=> dictionary_bool*hobbies?
options *
programming \Programming
bikinkg \Biking
fishing \Fishing
<= Friends_field $mol_form_field
name \Friends
Content <= Friends $mol_select_list
dictionary *
jocker \Jocker
harley \Harley Quinn
penguin \Penguin
riddler \Riddler
bane \Bane
freeze \Mister Freeze
clay \Clayface
mask \Black Mask
value? <=> list_string*friends?
body <= form_body /
<= Title_field
<= Config $mol_form_group sub /
<= Adult_field
<= Type_field
<= Content_field
<= Friends_field
buttons /
<= Publish $mol_button_major
title \Publish
click? <=> publish?
enabled <= publish_allowed
<= Result $mol_status
message <= result? \
<= Reset $mol_button_minor
title \Сбросить
click? <=> reset?
enabled <= changed
tags /
\$mol_form_field
\$mol_button
Expand Down
4 changes: 3 additions & 1 deletion form/draft/demo/demo.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace $.$$ {
return [
this.Title_field(),
this.Config(),
this.Hobbies_field(),
... this.value_str( 'type' ) ? [ this.Content_field() ] : [],
this.Friends_field(),
]
}

Expand Down Expand Up @@ -42,6 +44,6 @@ namespace $.$$ {
super.publish()
this.result( this.message_done() )
}

}
}
114 changes: 92 additions & 22 deletions form/draft/draft.view.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,131 @@
namespace $.$$ {

type Primitive = string | number | boolean

type Value = readonly Primitive[] | Primitive | Record<string, boolean>
type Model = Record<string, (next?: Value | null) => Value>

function norm_string(val: unknown) {
return String(val ?? '')
}

function norm_number(val: unknown) {
return Number(val ?? 0)
}

function norm_bool(val: unknown) {
return Boolean(val ?? false)
}

function normalize_val(prev: Value, next: Value | null) {
switch( typeof prev ) {
case 'boolean': return String( next ) === 'true'
case 'number': return Number( next )
case 'string': return String( next )
}

return next
}

/**
* @see https://mol.hyoo.ru/#!section=demos/demo=mol_form_draft_demo
*/
export class $mol_form_draft extends $.$mol_form_draft {

@ $mol_mem_key
list_string( field: string, next? : readonly string[] | null ) {
return this.value( field, next )?.map(norm_string) ?? []
}

@ $mol_mem_key
dictionary_bool( field: string, next? : Record<string, boolean> | null ): Record<string, boolean> {
if (next) {
const prev = this.model_pick(field) as Record<string, boolean>
const normalized = {} as typeof next
for (const key in next) {
if (next[key] || key in prev ) normalized[key] = next[key]
}

return this.value( field, normalized ) ?? {}
}

return this.value( field ) ?? {}
}

@ $mol_mem_key
value_str( field: string, next? : string | null ) {
return String( this.value( field, next ) ?? '' )
return norm_string( this.value( field, next ) )
}

@ $mol_mem_key
value_numb( field: string, next? : boolean | null ) {
return Number( this.value( field, next ) ?? 0 )
return norm_number( this.value( field, next ) )
}

@ $mol_mem_key
value_bool( field: string, next? : boolean | null ) {
return Boolean( this.value( field, next ) ?? false )
return norm_bool( this.value( field, next ) )
}


model_pick(field: string, next?: Value | null) {
return (this.model() as unknown as Model)[field](next)
}

state_pick(field: string, next?: Value | null) {
return this.state( next === undefined ? next : { ... this.state(), [ field ]: next } )[ field ]
}

@ $mol_mem_key
value( field: string, next?: string | number | boolean | null ) {
return this.state( next?.valueOf && { ... this.state(), [ field ]: next } )[ field ]
?? ( this.model() as any )[ field ]()
value<T extends Value>( field: string, next?: T | null ): T {
if (Array.isArray(next) && next.length === 0 && ! this.model_pick(field)) next = null
return this.state_pick(field, next) as T ?? this.model_pick(field)
}

@ $mol_mem_key
value_changed(field: string) {
const next = this.state_pick(field)
const prev = this.model_pick(field)
const next_norm = normalize_val(prev, next)

return ! $mol_compare_deep(next_norm, prev)
}

@ $mol_mem
state( next?: Record< string, string | number | boolean | null > | null ) {
state( next?: Record< string, Value | null > | null ) {
return $mol_state_local.value( `${ this }.state()`, next ) ?? {}
}

@ $mol_mem
changed() {
return Object.keys( this.state() ).length > 0
return Object.keys(this.state()).some(field => this.value_changed(field))
}

submit_allowed() {
return this.changed() && super.submit_allowed()
}


reset(next?: unknown) {
this.state(null)
}

@ $mol_action
submit( next? : Event ) {

const model = this.model()

for( let [ field, next ] of Object.entries( this.state() ) ) {
const prev = ( model as any )[ field ]()
switch( typeof prev ) {
case 'boolean': next = String( next ) === 'true'; break
case 'number': next = Number( next ); break
case 'string': next = String( next ); break
const tasks = Object.entries( this.state() ).map(
([ field, next ]) => () => {
const prev = this.model_pick(field)

return {
field,
next: normalize_val(prev, next)
}
}
;( model as any )[ field ]( next )
}
)

const normalized = $mol_wire_race(...tasks)

$mol_wire_race(...normalized.map(({ field, next }) => () => this.model_pick( field, next )))

this.state( null )
this.reset()

}

Expand Down
2 changes: 1 addition & 1 deletion list/list.view.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
align-items: stretch;
align-content: stretch; */
transition: none;
min-height: .5rem;
min-height: 1.5rem;
}

[mol_list_gap_before] ,
Expand Down
6 changes: 6 additions & 0 deletions select/list/demo/demo.view.tree
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ $mol_select_list_demo $mol_example_small
value? <=> friends? /
dictionary <= suggestions
enabled false
<= Friends_lazy $mol_select_list
value? <=> friends_lazy? /
option_title* <= option_title* \
filter_pattern? => filter_pattern?
pick_enabled true
dictionary <= suggestions_lazy <= suggestions
tags /
\select
\tags
Expand Down
15 changes: 15 additions & 0 deletions select/list/demo/demo.view.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace $.$$ {
export class $mol_select_list_demo extends $.$mol_select_list_demo {
@ $mol_mem
override suggestions_lazy() {
this.$.$mol_wait_timeout(500)
this.filter_pattern()
return super.suggestions()
}

override option_title(id: string) {
if (! id) return ''
return this.suggestions_lazy()[id]
}
}
}
2 changes: 2 additions & 0 deletions select/list/list.view.tree
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ $mol_select_list $mol_view
enabled <= drop_enabled <= enabled true
sub /$mol_view
<= Pick $mol_select
event_select*? <=> event_select*? null
align_hor <= align_hor \right
options <= options_pickable <= options /string
value? <=> pick? \
option_label* <= option_title* \
trigger_enabled <= pick_enabled <= enabled true
hint <= pick_hint @ \Add..
filter_pattern? => filter_pattern?
Trigger_icon <= Pick_icon $mol_icon_plus
^ badges_list
28 changes: 11 additions & 17 deletions select/list/list.view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace $.$$ {
*/
export class $mol_select_list extends $.$mol_select_list {

override value( val? : string[] ) {
override value( val? : readonly string[] ) {
return super.value( val ) as readonly string[]
}

Expand All @@ -16,16 +16,14 @@ namespace $.$$ {
if( !key ) return ''
this.value([ ... this.value() , key ])

new $mol_after_frame(()=> {
if( !this.pick_enabled() ) return
this.Pick().filter_pattern( '' )
this.Pick().Trigger().focused( true )
this.Pick().open()
})

return ''
}

override event_select( id : string , event? : MouseEvent ) {
event?.preventDefault()
this.pick( id )
}

@ $mol_mem
override options() {
return Object.keys( this.dictionary() ) as readonly string[]
Expand All @@ -46,8 +44,8 @@ namespace $.$$ {
return value == null ? key : value
}

override badge_title( index: number ) {
return this.option_title( this.value()[ index ] )
override badge_title( key: string ) {
return this.option_title( key )
}

@ $mol_mem
Expand All @@ -57,7 +55,7 @@ namespace $.$$ {

override Badges() {
return this.value()
.map( ( _, index )=> this.Badge( index ) )
.map( id => this.Badge( id ) )
.reverse()
}

Expand All @@ -67,12 +65,8 @@ namespace $.$$ {
}

@ $mol_action
override remove( index: number ) {
const value = this.value()
this.value([
... value.slice( 0 , index ),
... value.slice( index + 1 ),
])
override remove( key: string ) {
this.value(this.value().filter(id => id !== key))
}

}
Expand Down
Loading