@@ -24,8 +24,12 @@ const (
2424)
2525
2626type Field struct {
27- name string
28- kind fieldKind
27+ name string
28+ kind fieldKind
29+ optional bool
30+ present bool
31+ optionalControlFocused bool
32+ width int
2933
3034 textInput textinput.Model
3135 enumPicker enumPicker
@@ -37,6 +41,8 @@ type Field struct {
3741 validate func (string ) error
3842}
3943
44+ const optionalControlWidth = 6
45+
4046func NewTextField (name string , placeholder string , charLimit int , validate func (string ) error ) * Field {
4147 ti := textinput .New ()
4248 ti .Placeholder = placeholder
@@ -180,7 +186,14 @@ func validateTimestamp(s string) error {
180186}
181187
182188func NewFieldFromProto (field protoreflect.FieldDescriptor ) * Field {
183- return newFieldFromProto (field , "" )
189+ f := newFieldFromProto (field , "" )
190+ if f != nil {
191+ f .optional = field .HasOptionalKeyword ()
192+ if f .optional && field .Kind () == protoreflect .StringKind {
193+ f .textInput .Placeholder = "Enter string..."
194+ }
195+ }
196+ return f
184197}
185198
186199func newFieldFromProto (field protoreflect.FieldDescriptor , inputRole string ) * Field {
@@ -309,7 +322,32 @@ func (f *Field) Name() string {
309322 return f .name
310323}
311324
325+ func (f * Field ) checkbox (focused bool ) string {
326+ if ! f .optional {
327+ return ""
328+ }
329+ mark := unselectedStyle .Render (" " )
330+ if f .present {
331+ mark = selectedStyle .Render ("x" )
332+ }
333+ box := bracketStyle .Render ("[" ) + mark + bracketStyle .Render ("]" )
334+ if focused {
335+ return " " + focusedLabelStyle .Render ("> " ) + box
336+ }
337+ return " " + box
338+ }
339+
340+ func (f * Field ) inputView (prefix string ) string {
341+ if f .optional && f .kind == FieldText && f .width > 0 {
342+ f .textInput .Width = max (1 , f .width - len (prefix )- len (f .name )- 2 - optionalControlWidth )
343+ }
344+ return f .View ()
345+ }
346+
312347func (f * Field ) AcceptsTextInput () bool {
348+ if f .optionalControlFocused {
349+ return false
350+ }
313351 switch f .kind {
314352 case FieldText :
315353 return true
@@ -330,6 +368,7 @@ func (f *Field) AcceptsTextInput() bool {
330368}
331369
332370func (f * Field ) Focus () tea.Cmd {
371+ f .optionalControlFocused = false
333372 switch f .kind {
334373 case FieldText :
335374 return f .textInput .Focus ()
@@ -354,6 +393,16 @@ func (f *Field) Focus() tea.Cmd {
354393}
355394
356395func (f * Field ) FocusFromEnd () tea.Cmd {
396+ if f .optional {
397+ f .Blur ()
398+ f .optionalControlFocused = true
399+ return nil
400+ }
401+ return f .focusValueFromEnd ()
402+ }
403+
404+ func (f * Field ) focusValueFromEnd () tea.Cmd {
405+ f .optionalControlFocused = false
357406 switch f .kind {
358407 case FieldText :
359408 return f .textInput .Focus ()
@@ -378,6 +427,7 @@ func (f *Field) FocusFromEnd() tea.Cmd {
378427}
379428
380429func (f * Field ) Blur () {
430+ f .optionalControlFocused = false
381431 switch f .kind {
382432 case FieldText :
383433 f .textInput .Blur ()
@@ -445,6 +495,11 @@ func (f *Field) Prev() bool {
445495}
446496
447497func (f * Field ) HandleKey (msg tea.KeyMsg ) (tea.Cmd , bool ) {
498+ if f .optionalControlFocused && msg .String () == "enter" {
499+ f .present = ! f .present
500+ return nil , true
501+ }
502+
448503 switch f .kind {
449504 case FieldEnum , FieldBool :
450505 switch msg .String () {
@@ -473,10 +528,16 @@ func (f *Field) HandleKey(msg tea.KeyMsg) (tea.Cmd, bool) {
473528}
474529
475530func (f * Field ) Update (msg tea.Msg ) tea.Cmd {
531+ if f .optionalControlFocused {
532+ return nil
533+ }
476534 switch f .kind {
477535 case FieldText :
478536 var cmd tea.Cmd
479537 f .textInput , cmd = f .textInput .Update (msg )
538+ if f .optional && f .textInput .Value () != "" {
539+ f .present = true
540+ }
480541 return cmd
481542 case FieldGroup :
482543 if f .fieldGroup != nil {
@@ -499,6 +560,7 @@ func (f *Field) Update(msg tea.Msg) tea.Cmd {
499560}
500561
501562func (f * Field ) SetWidth (width int ) {
563+ f .width = width
502564 switch f .kind {
503565 case FieldText :
504566 f .textInput .Width = width
@@ -524,52 +586,54 @@ func (f *Field) RenderWithFocus(focused bool, depth int) string {
524586 indent := strings .Repeat (" " , depth )
525587
526588 var prefix string
527- if focused {
589+ inputFocused := focused && ! f .optionalControlFocused
590+ if inputFocused {
528591 prefix = indent + "> "
529592 } else {
530593 prefix = indent + " "
531594 }
532595
533596 switch f .kind {
534597 case FieldGroup :
535- if focused {
598+ if inputFocused {
536599 b .WriteString (focusedLabelStyle .Render (prefix + f .name + ":" ))
537600 } else {
538601 b .WriteString (labelStyle .Render (prefix + f .name + ":" ))
539602 }
540603 b .WriteString ("\n " )
541604 b .WriteString (f .fieldGroup .ViewWithDepth (depth + 1 ))
542605 case FieldList :
543- if focused {
606+ if inputFocused {
544607 b .WriteString (focusedLabelStyle .Render (prefix + f .name + ":" ))
545608 } else {
546609 b .WriteString (labelStyle .Render (prefix + f .name + ":" ))
547610 }
548611 b .WriteString ("\n " )
549612 b .WriteString (f .listField .ViewWithDepth (depth + 1 ))
550613 case FieldMap :
551- if focused {
614+ if inputFocused {
552615 b .WriteString (focusedLabelStyle .Render (prefix + f .name + ":" ))
553616 } else {
554617 b .WriteString (labelStyle .Render (prefix + f .name + ":" ))
555618 }
556619 b .WriteString ("\n " )
557620 b .WriteString (f .mapField .ViewWithDepth (depth + 1 ))
558621 case FieldOneof :
559- if focused {
622+ if inputFocused {
560623 b .WriteString (focusedLabelStyle .Render (prefix + f .name + ":" ))
561624 } else {
562625 b .WriteString (labelStyle .Render (prefix + f .name + ":" ))
563626 }
564627 b .WriteString ("\n " )
565628 b .WriteString (f .oneofField .ViewWithDepth (depth + 1 ))
566629 default :
567- if focused {
630+ if inputFocused {
568631 b .WriteString (focusedLabelStyle .Render (prefix + f .name + ": " ))
569632 } else {
570633 b .WriteString (labelStyle .Render (prefix + f .name + ": " ))
571634 }
572- b .WriteString (f .View ())
635+ b .WriteString (f .inputView (prefix ))
636+ b .WriteString (f .checkbox (focused && f .optionalControlFocused ))
573637 b .WriteString ("\n " )
574638 }
575639
0 commit comments