Module: Plushie::WidgetSet

Defined in:
lib/plushie/widget_set.rb

Overview

Create widget set modules that override built-in widget DSL methods.

A widget set re-exports all of Plushie::UI but replaces specific widget methods with alternatives:

MaterialUI = Plushie::WidgetSet.create( button: MyApp::MaterialButton, text_input: MyApp::MaterialTextInput )

# In view methods: include MaterialUI button("save", "Save") # uses MaterialButton

Override widget classes must respond to +new(id, *args, **opts)+ and +#build+ (returning a Node), matching the Widget.define API.

Class Method Summary collapse

Class Method Details

.create(**overrides) ⇒ Module

Create a widget set module with the given overrides.

Parameters:

  • overrides (Hash{Symbol => Class})

    widget name -> replacement class

Returns:

  • (Module)

    a module that can be included for the overridden DSL



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/plushie/widget_set.rb', line 39

def self.create(**overrides)
  overrides.each_key do |name|
    unless OVERRIDEABLE_WIDGET_METHODS.include?(name)
      raise ArgumentError,
        "#{name.inspect} is not a Plushie::UI widget method. " \
        "Available: #{OVERRIDEABLE_WIDGET_METHODS.sort.inspect}"
    end
  end

  Module.new do
    include Plushie::UI

    overrides.each do |name, widget_class|
      define_method(name) do |id, *args, **opts, &block|
        builder = widget_class.new(id, *args, **opts)
        if block
          unless builder.respond_to?(:push)
            raise ArgumentError,
              "#{name} override #{widget_class} does not accept children"
          end

          children = []
          UI::Context.push(children)
          begin
            block.call
          ensure
            UI::Context.pop
          end
          children.each do |child|
            next_builder = builder.push(child)
            builder = next_builder if next_builder
          end
        end
        node = builder.build
        ctx = UI::Context.current
        ctx << node if ctx&.none? { |child| child.equal?(node) }
        node
      end
      private name
    end
  end
end