Skip to content

Commit

Permalink
Merge pull request #637 from hyoo-ru/form_draft_arr
Browse files Browse the repository at this point in the history
$mol_form_draft: array support
  • Loading branch information
zerkalica authored Oct 1, 2023
2 parents c96dcc7 + 902a9f6 commit c495254
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 47 deletions.
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

0 comments on commit c495254

Please sign in to comment.