1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module ProTacts
  module Admin
    # The name fields' one rule, for the create dialog and the editor
    # both: a contact needs a name, and either half alone is one — a
    # mononym is a family name alone as often as a given one (macOS
    # writes `N:Prince;;;;`). No attribute says "one of these two", so
    # each box is required exactly while the other is blank: rendered
    # so from the values the form starts with, and kept so by Alpine as
    # they change. The server's refusal of a save blank throughout
    # (Web's contacts routes) is the backstop behind it.
    #
    # The middle box the same forms carry is not one of the halves and
    # is not here: nobody is known by a middle name alone, so it is
    # never required and has no state to share.
    #
    # Not a view, so the Steepfile checks it.
    module NamePair
      # The Alpine state the two boxes share, as properties for the
      # enclosing x-data — booleans, so no name is spliced into script.
      #: (String? first, String? last) -> String
      def self.state(first, last)
        "firstBlank: #{blank?(first)}, lastBlank: #{blank?(last)}"
      end

      #: (String? first, String? last) -> Hash[Symbol, untyped]
      def self.first(first, last)
        box("first", first, required: blank?(last), own: "firstBlank", other: "lastBlank")
      end

      #: (String? first, String? last) -> Hash[Symbol, untyped]
      def self.last(first, last)
        box("last", last, required: blank?(first), own: "lastBlank", other: "firstBlank")
      end

      #: (String name, String? value, required: bool, own: String, other: String) -> Hash[Symbol, untyped]
      def self.box(name, value, required:, own:, other:)
        {type: "text", name:, value:, required:, ":required": other, "@input": "#{own} = !$el.value.trim()"}
      end

      # The server's own test of a blank half (Web's contacts routes).
      #: (String? value) -> bool
      def self.blank?(value)
        value.to_s.strip.empty?
      end
    end
  end
end