zui 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Components/Builtins/Accordion.qml +208 -0
- data/Components/Builtins/Action.qml +43 -0
- data/Components/Builtins/ActionButton.qml +19 -0
- data/Components/Builtins/ActionGroup.qml +81 -0
- data/Components/Builtins/AlertDialog.qml +398 -0
- data/Components/Builtins/AnchorAnimation.qml +3 -0
- data/Components/Builtins/AnchorChanges.qml +11 -0
- data/Components/Builtins/AnimatedImage.qml +39 -0
- data/Components/Builtins/Animation.qml +3 -0
- data/Components/Builtins/AnimationController.qml +11 -0
- data/Components/Builtins/ApplicationWindow.qml +103 -0
- data/Components/Builtins/AreaChart.qml +70 -0
- data/Components/Builtins/AspectRatio.qml +30 -0
- data/Components/Builtins/Audio.qml +85 -0
- data/Components/Builtins/AudioInput.qml +10 -0
- data/Components/Builtins/AudioOutput.qml +10 -0
- data/Components/Builtins/Avatar.qml +46 -0
- data/Components/Builtins/Badge.qml +38 -0
- data/Components/Builtins/Banner.qml +133 -0
- data/Components/Builtins/BarChart.qml +67 -0
- data/Components/Builtins/BarIconButton.qml +30 -0
- data/Components/Builtins/BarIndicator.qml +26 -0
- data/Components/Builtins/Behavior.qml +13 -0
- data/Components/Builtins/Blur.qml +14 -0
- data/Components/Builtins/BorderImage.qml +38 -0
- data/Components/Builtins/BorderOverlay.qml +25 -0
- data/Components/Builtins/BottomSheet.qml +211 -0
- data/Components/Builtins/Breadcrumb.qml +169 -0
- data/Components/Builtins/BubbleChart.qml +3 -0
- data/Components/Builtins/BusyIndicator.qml +24 -0
- data/Components/Builtins/Button.qml +55 -0
- data/Components/Builtins/ButtonGroup.qml +18 -0
- data/Components/Builtins/Calendar.qml +55 -0
- data/Components/Builtins/Camera.qml +18 -0
- data/Components/Builtins/CandlestickChart.qml +3 -0
- data/Components/Builtins/Canvas.qml +159 -0
- data/Components/Builtins/CaptureSession.qml +19 -0
- data/Components/Builtins/Card.qml +23 -0
- data/Components/Builtins/Carousel.qml +72 -0
- data/Components/Builtins/Center.qml +20 -0
- data/Components/Builtins/CheckDelegate.qml +132 -0
- data/Components/Builtins/Checkbox.qml +62 -0
- data/Components/Builtins/Chip.qml +65 -0
- data/Components/Builtins/Circle.qml +35 -0
- data/Components/Builtins/Clipboard.qml +29 -0
- data/Components/Builtins/ColorAnimation.qml +3 -0
- data/Components/Builtins/ColorPicker.qml +70 -0
- data/Components/Builtins/Colorize.qml +14 -0
- data/Components/Builtins/Column.qml +17 -0
- data/Components/Builtins/ColumnLayout.qml +13 -0
- data/Components/Builtins/ConfirmDialog.qml +20 -0
- data/Components/Builtins/ConstrainedBox.qml +28 -0
- data/Components/Builtins/Container.qml +30 -0
- data/Components/Builtins/ContextMenu.qml +140 -0
- data/Components/Builtins/CursorSurface.qml +18 -0
- data/Components/Builtins/DataTable.qml +69 -0
- data/Components/Builtins/DatePicker.qml +180 -0
- data/Components/Builtins/DayOfWeekRow.qml +11 -0
- data/Components/Builtins/DelayButton.qml +47 -0
- data/Components/Builtins/DelegateModel.qml +10 -0
- data/Components/Builtins/DelegateModelGroup.qml +10 -0
- data/Components/Builtins/Dial.qml +42 -0
- data/Components/Builtins/Dialog.qml +128 -0
- data/Components/Builtins/DialogButtonBox.qml +137 -0
- data/Components/Builtins/Divider.qml +26 -0
- data/Components/Builtins/DonutChart.qml +3 -0
- data/Components/Builtins/DoubleSpinBox.qml +58 -0
- data/Components/Builtins/DragArea.qml +43 -0
- data/Components/Builtins/Drawer.qml +107 -0
- data/Components/Builtins/DropArea.qml +19 -0
- data/Components/Builtins/DropShadow.qml +16 -0
- data/Components/Builtins/Dropdown.qml +19 -0
- data/Components/Builtins/ExpansionPanel.qml +157 -0
- data/Components/Builtins/FilePicker.qml +140 -0
- data/Components/Builtins/FittedBox.qml +40 -0
- data/Components/Builtins/Flickable.qml +40 -0
- data/Components/Builtins/Flipable.qml +45 -0
- data/Components/Builtins/Flow.qml +16 -0
- data/Components/Builtins/FocusScope.qml +29 -0
- data/Components/Builtins/FolderListModel.qml +9 -0
- data/Components/Builtins/FolderPicker.qml +93 -0
- data/Components/Builtins/FontLoader.qml +21 -0
- data/Components/Builtins/FontPicker.qml +91 -0
- data/Components/Builtins/Frame.qml +71 -0
- data/Components/Builtins/FrameAnimation.qml +13 -0
- data/Components/Builtins/Gauge.qml +3 -0
- data/Components/Builtins/Glow.qml +15 -0
- data/Components/Builtins/Gradient.qml +93 -0
- data/Components/Builtins/Grid.qml +16 -0
- data/Components/Builtins/GridLayout.qml +16 -0
- data/Components/Builtins/GridView.qml +226 -0
- data/Components/Builtins/GroupBox.qml +95 -0
- data/Components/Builtins/Heatmap.qml +3 -0
- data/Components/Builtins/Histogram.qml +3 -0
- data/Components/Builtins/HorizontalHeader.qml +23 -0
- data/Components/Builtins/HorizontalHeaderDelegate.qml +39 -0
- data/Components/Builtins/HoverArea.qml +19 -0
- data/Components/Builtins/Icon.qml +16 -0
- data/Components/Builtins/Image.qml +110 -0
- data/Components/Builtins/ImageCapture.qml +14 -0
- data/Components/Builtins/ItemDelegate.qml +152 -0
- data/Components/Builtins/KeyCatcher.qml +25 -0
- data/Components/Builtins/Label.qml +43 -0
- data/Components/Builtins/LayoutItemProxy.qml +51 -0
- data/Components/Builtins/Legend.qml +86 -0
- data/Components/Builtins/Line.qml +28 -0
- data/Components/Builtins/LineChart.qml +87 -0
- data/Components/Builtins/ListModel.qml +8 -0
- data/Components/Builtins/ListView.qml +65 -0
- data/Components/Builtins/Loader.qml +33 -0
- data/Components/Builtins/Markdown.qml +23 -0
- data/Components/Builtins/MediaDevices.qml +10 -0
- data/Components/Builtins/MediaPlayer.qml +27 -0
- data/Components/Builtins/MediaRecorder.qml +16 -0
- data/Components/Builtins/Menu.qml +119 -0
- data/Components/Builtins/MenuBar.qml +128 -0
- data/Components/Builtins/MenuItem.qml +77 -0
- data/Components/Builtins/MenuSeparator.qml +30 -0
- data/Components/Builtins/MessageDialog.qml +104 -0
- data/Components/Builtins/ModalSheet.qml +222 -0
- data/Components/Builtins/ModelView3d.qml +50 -0
- data/Components/Builtins/MonthGrid.qml +23 -0
- data/Components/Builtins/MultiEffect.qml +48 -0
- data/Components/Builtins/MultiSelect.qml +23 -0
- data/Components/Builtins/NavigationRail.qml +154 -0
- data/Components/Builtins/NumberAnimation.qml +3 -0
- data/Components/Builtins/NumberField.qml +18 -0
- data/Components/Builtins/OpacityAnimator.qml +3 -0
- data/Components/Builtins/OpacityMask.qml +21 -0
- data/Components/Builtins/OpticalGlyph.qml +16 -0
- data/Components/Builtins/Page.qml +111 -0
- data/Components/Builtins/PageIndicator.qml +63 -0
- data/Components/Builtins/Pagination.qml +158 -0
- data/Components/Builtins/Pane.qml +72 -0
- data/Components/Builtins/PanelHero.qml +15 -0
- data/Components/Builtins/ParallelAnimation.qml +3 -0
- data/Components/Builtins/ParentAnimation.qml +3 -0
- data/Components/Builtins/ParentChange.qml +10 -0
- data/Components/Builtins/ParticleSystem.qml +81 -0
- data/Components/Builtins/PasswordField.qml +58 -0
- data/Components/Builtins/Path.qml +35 -0
- data/Components/Builtins/PathAnimation.qml +3 -0
- data/Components/Builtins/PauseAnimation.qml +3 -0
- data/Components/Builtins/PieChart.qml +3 -0
- data/Components/Builtins/PinchArea.qml +23 -0
- data/Components/Builtins/Popup.qml +130 -0
- data/Components/Builtins/Progress.qml +15 -0
- data/Components/Builtins/ProgressRing.qml +120 -0
- data/Components/Builtins/PropertyAction.qml +11 -0
- data/Components/Builtins/PropertyAnimation.qml +3 -0
- data/Components/Builtins/PropertyChanges.qml +10 -0
- data/Components/Builtins/RadarChart.qml +3 -0
- data/Components/Builtins/RadialGauge.qml +3 -0
- data/Components/Builtins/RadioButton.qml +46 -0
- data/Components/Builtins/RadioDelegate.qml +115 -0
- data/Components/Builtins/RadioGroup.qml +58 -0
- data/Components/Builtins/RangeSlider.qml +39 -0
- data/Components/Builtins/Rectangle.qml +23 -0
- data/Components/Builtins/RectangularShadow.qml +23 -0
- data/Components/Builtins/ReorderableList.qml +204 -0
- data/Components/Builtins/RichText.qml +23 -0
- data/Components/Builtins/RotationAnimation.qml +3 -0
- data/Components/Builtins/RotationAnimator.qml +3 -0
- data/Components/Builtins/RoundButton.qml +42 -0
- data/Components/Builtins/Row.qml +17 -0
- data/Components/Builtins/RowLayout.qml +13 -0
- data/Components/Builtins/ScaleAnimator.qml +3 -0
- data/Components/Builtins/ScatterChart.qml +3 -0
- data/Components/Builtins/ScreenCapture.qml +78 -0
- data/Components/Builtins/ScriptAction.qml +15 -0
- data/Components/Builtins/Scroll.qml +18 -0
- data/Components/Builtins/ScrollBar.qml +18 -0
- data/Components/Builtins/ScrollIndicator.qml +15 -0
- data/Components/Builtins/SearchField.qml +178 -0
- data/Components/Builtins/SearchableDropdown.qml +21 -0
- data/Components/Builtins/SectionHeader.qml +14 -0
- data/Components/Builtins/SelectableText.qml +33 -0
- data/Components/Builtins/SelectionRectangle.qml +17 -0
- data/Components/Builtins/Separator.qml +13 -0
- data/Components/Builtins/SequentialAnimation.qml +3 -0
- data/Components/Builtins/Settings.qml +10 -0
- data/Components/Builtins/ShaderEffect.qml +115 -0
- data/Components/Builtins/ShaderEffectSource.qml +61 -0
- data/Components/Builtins/Shaders/grayscale.frag +14 -0
- data/Components/Builtins/Shaders/grayscale.frag.qsb +0 -0
- data/Components/Builtins/Shaders/passthrough.frag +11 -0
- data/Components/Builtins/Shaders/passthrough.frag.qsb +0 -0
- data/Components/Builtins/Shaders/pixelate.frag +16 -0
- data/Components/Builtins/Shaders/pixelate.frag.qsb +0 -0
- data/Components/Builtins/Shaders/vignette.frag +18 -0
- data/Components/Builtins/Shaders/vignette.frag.qsb +0 -0
- data/Components/Builtins/Shaders/wave.frag +18 -0
- data/Components/Builtins/Shaders/wave.frag.qsb +0 -0
- data/Components/Builtins/Shape.qml +53 -0
- data/Components/Builtins/Skeleton.qml +78 -0
- data/Components/Builtins/Slider.qml +21 -0
- data/Components/Builtins/SmoothedAnimation.qml +3 -0
- data/Components/Builtins/Snackbar.qml +181 -0
- data/Components/Builtins/SortFilterProxyModel.qml +16 -0
- data/Components/Builtins/SoundEffect.qml +10 -0
- data/Components/Builtins/Spacer.qml +13 -0
- data/Components/Builtins/Sparkline.qml +3 -0
- data/Components/Builtins/SpinBox.qml +44 -0
- data/Components/Builtins/SplitView.qml +31 -0
- data/Components/Builtins/SpringAnimation.qml +3 -0
- data/Components/Builtins/Stack.qml +14 -0
- data/Components/Builtins/StackLayout.qml +19 -0
- data/Components/Builtins/StackView.qml +122 -0
- data/Components/Builtins/StackedBarChart.qml +3 -0
- data/Components/Builtins/StandardPaths.qml +9 -0
- data/Components/Builtins/State.qml +13 -0
- data/Components/Builtins/StateGroup.qml +11 -0
- data/Components/Builtins/Support/AnimationDriver.qml +79 -0
- data/Components/Builtins/Support/AnimationGroupDriver.qml +32 -0
- data/Components/Builtins/Support/ChartCanvas.qml +629 -0
- data/Components/Builtins/Support/ModelView3dScene.qml +292 -0
- data/Components/Builtins/Support/OptionalModuleState.js +5 -0
- data/Components/Builtins/SwipeDelegate.qml +231 -0
- data/Components/Builtins/SwipeView.qml +69 -0
- data/Components/Builtins/SwitchDelegate.qml +140 -0
- data/Components/Builtins/TabBar.qml +117 -0
- data/Components/Builtins/TabButton.qml +105 -0
- data/Components/Builtins/TableView.qml +388 -0
- data/Components/Builtins/TableViewDelegate.qml +17 -0
- data/Components/Builtins/Tabs.qml +138 -0
- data/Components/Builtins/Text.qml +24 -0
- data/Components/Builtins/TextArea.qml +59 -0
- data/Components/Builtins/TextField.qml +21 -0
- data/Components/Builtins/TextMetrics.qml +39 -0
- data/Components/Builtins/TimePicker.qml +213 -0
- data/Components/Builtins/Timer.qml +13 -0
- data/Components/Builtins/Toast.qml +213 -0
- data/Components/Builtins/Toggle.qml +19 -0
- data/Components/Builtins/ToggleSwitch.qml +22 -0
- data/Components/Builtins/ToolBar.qml +61 -0
- data/Components/Builtins/ToolButton.qml +41 -0
- data/Components/Builtins/ToolSeparator.qml +36 -0
- data/Components/Builtins/Tooltip.qml +20 -0
- data/Components/Builtins/Transition.qml +3 -0
- data/Components/Builtins/TreeView.qml +423 -0
- data/Components/Builtins/TreeViewDelegate.qml +46 -0
- data/Components/Builtins/Tumbler.qml +18 -0
- data/Components/Builtins/UniformAnimator.qml +3 -0
- data/Components/Builtins/VectorAnimation.qml +3 -0
- data/Components/Builtins/VectorImage.qml +62 -0
- data/Components/Builtins/VerticalHeader.qml +21 -0
- data/Components/Builtins/VerticalHeaderDelegate.qml +35 -0
- data/Components/Builtins/Video.qml +54 -0
- data/Components/Builtins/VideoOutput.qml +54 -0
- data/Components/Builtins/WebView.qml +57 -0
- data/Components/Builtins/WeekNumberColumn.qml +13 -0
- data/Components/Builtins/WidgetButton.qml +26 -0
- data/Components/Builtins/Window.qml +90 -0
- data/Components/Builtins/WindowCapture.qml +11 -0
- data/Components/Builtins/Wrap.qml +18 -0
- data/Components/Builtins/XAnimator.qml +3 -0
- data/Components/Builtins/YAnimator.qml +3 -0
- data/Components/README.md +45 -0
- data/ControlNode.qml +1369 -0
- data/Controls/BarIconButton.qml +27 -0
- data/Controls/BarIndicator.qml +22 -0
- data/Controls/BorderOverlay.qml +10 -0
- data/Controls/BorderSurface.qml +10 -0
- data/Controls/Button.qml +78 -0
- data/Controls/ButtonGroup.qml +19 -0
- data/Controls/ConfirmDialog.qml +37 -0
- data/Controls/CursorSurface.qml +29 -0
- data/Controls/Dropdown.qml +59 -0
- data/Controls/MultiSelect.qml +32 -0
- data/Controls/NumberField.qml +24 -0
- data/Controls/OpticalGlyph.qml +14 -0
- data/Controls/PanelActionButton.qml +10 -0
- data/Controls/PanelHero.qml +18 -0
- data/Controls/PanelKeyCatcher.qml +25 -0
- data/Controls/PanelSectionHeader.qml +11 -0
- data/Controls/PanelSeparator.qml +10 -0
- data/Controls/PanelSlider.qml +36 -0
- data/Controls/PanelToolTip.qml +13 -0
- data/Controls/SearchableDropdown.qml +11 -0
- data/Controls/TextField.qml +25 -0
- data/Controls/Toggle.qml +22 -0
- data/Controls/ToggleSwitch.qml +26 -0
- data/Controls/WidgetButton.qml +35 -0
- data/Controls/qmldir +25 -0
- data/Desktop.qml +76 -0
- data/LICENSE +22 -0
- data/README.md +112 -0
- data/Service.qml +430 -0
- data/Theme/Border.qml +22 -0
- data/Theme/Color.qml +22 -0
- data/Theme/Style.qml +47 -0
- data/Theme/Util.qml +6 -0
- data/Theme/qmldir +5 -0
- data/bin/zui +7 -0
- data/lib/zui/animation.rb +24 -0
- data/lib/zui/application.rb +327 -0
- data/lib/zui/builder.rb +725 -0
- data/lib/zui/cli.rb +91 -0
- data/lib/zui/command.rb +101 -0
- data/lib/zui/component_registry.rb +91 -0
- data/lib/zui/components.rb +262 -0
- data/lib/zui/distribution.rb +200 -0
- data/lib/zui/generator.rb +84 -0
- data/lib/zui/host.rb +123 -0
- data/lib/zui/node.rb +30 -0
- data/lib/zui/platform.rb +40 -0
- data/lib/zui/protocol.rb +29 -0
- data/lib/zui/runner.rb +37 -0
- data/lib/zui/runtime.rb +23 -0
- data/lib/zui/scheduler.rb +136 -0
- data/lib/zui/source_bundle.rb +63 -0
- data/lib/zui/state_store.rb +100 -0
- data/lib/zui/value.rb +44 -0
- data/lib/zui.rb +33 -0
- data/native/CMakeLists.txt +45 -0
- data/native/ZuiClipboard.cpp +16 -0
- data/native/ZuiClipboard.h +16 -0
- data/native/ZuiProcess.cpp +84 -0
- data/native/ZuiProcess.h +34 -0
- data/native/main.cpp +64 -0
- data/vendor/host/linux-x86_64/PROVENANCE.md +15 -0
- data/vendor/host/linux-x86_64/zui-host +0 -0
- data/vendor/host/linux-x86_64/zui-host.sha256 +1 -0
- metadata +365 -0
data/lib/zui/builder.rb
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zui
|
|
4
|
+
class Builder
|
|
5
|
+
UNSET = Object.new.freeze
|
|
6
|
+
CONTAINERS = %i[
|
|
7
|
+
row column container grid row_layout column_layout grid_layout flow center card
|
|
8
|
+
stack scroll rectangle aspect_ratio constrained_box fitted_box wrap split_view stack_layout loader flickable focus_scope flipable border_image key_catcher
|
|
9
|
+
page pane frame group_box tabs stack_view swipe_view drawer expansion_panel accordion tool_bar popup dialog
|
|
10
|
+
shader_effect shader_effect_source multi_effect rectangular_shadow opacity_mask blur drop_shadow colorize glow
|
|
11
|
+
drag_area drop_area pinch_area hover_area capture_session cursor_surface
|
|
12
|
+
].freeze
|
|
13
|
+
VALUE_INPUTS = {
|
|
14
|
+
text_field: :text,
|
|
15
|
+
text_area: :text,
|
|
16
|
+
password_field: :text,
|
|
17
|
+
number_field: :value,
|
|
18
|
+
slider: :value,
|
|
19
|
+
dial: :value,
|
|
20
|
+
spin_box: :value,
|
|
21
|
+
double_spin_box: :value,
|
|
22
|
+
color_picker: :color,
|
|
23
|
+
date_picker: :date,
|
|
24
|
+
time_picker: :time,
|
|
25
|
+
file_picker: :path,
|
|
26
|
+
folder_picker: :path,
|
|
27
|
+
font_picker: :family,
|
|
28
|
+
dropdown: :value,
|
|
29
|
+
searchable_dropdown: :value,
|
|
30
|
+
multi_select: :values,
|
|
31
|
+
button_group: :value,
|
|
32
|
+
radio_group: :value
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
def initialize(application)
|
|
36
|
+
@application = application
|
|
37
|
+
@stack = []
|
|
38
|
+
@dynamic_scopes = []
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def component(type, id: nil, **props, &block)
|
|
42
|
+
definition = @application.components.fetch(type)
|
|
43
|
+
node = @application.build_node(type, explicit_id: id || scoped_id(type), props:)
|
|
44
|
+
append(node)
|
|
45
|
+
if block
|
|
46
|
+
raise ArgumentError, "#{type} is not a container" unless definition.container
|
|
47
|
+
within(node, &block)
|
|
48
|
+
end
|
|
49
|
+
node
|
|
50
|
+
end
|
|
51
|
+
alias widget component
|
|
52
|
+
alias qml_component component
|
|
53
|
+
|
|
54
|
+
CONTAINERS.each do |type|
|
|
55
|
+
define_method(type) { |id: nil, **props, &block| component(type, id:, **props, &block) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def register_component(name, qml:, properties: [], events: [], property_map: {}, event_map: {}, container: false, auto_bind: true)
|
|
59
|
+
unless @stack.empty? && @application.surfaces.empty?
|
|
60
|
+
raise ArgumentError, "components must be registered before a surface"
|
|
61
|
+
end
|
|
62
|
+
@application.components.register(name, qml:, properties:, events:, property_map:, event_map:, container:, auto_bind:)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def state(name = nil, initial = UNSET, target: nil, properties: nil, id: nil, **props)
|
|
66
|
+
unless target.nil?
|
|
67
|
+
state_props = props.merge(target: node_id(target), name: name.to_s)
|
|
68
|
+
state_props[:properties] = properties unless properties.nil?
|
|
69
|
+
return component(:state, id:, **state_props)
|
|
70
|
+
end
|
|
71
|
+
return @application.state if name.nil?
|
|
72
|
+
raise ArgumentError, "state requires an initial value" if initial.equal?(UNSET)
|
|
73
|
+
@application.define_state(name, initial)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
APP_OPTIONS = %i[title width height min_width min_height max_width max_height color visible maximized fullscreen].freeze
|
|
77
|
+
|
|
78
|
+
def bar_widget(&block) = surface("bar", id: "bar", &block)
|
|
79
|
+
def panel(name, &block) = surface(name.to_s, id: "panel.#{name}", &block)
|
|
80
|
+
|
|
81
|
+
def app(name = :main, **options, &block)
|
|
82
|
+
unknown = options.keys - APP_OPTIONS
|
|
83
|
+
raise ArgumentError, "unsupported app options: #{unknown.join(', ')}" unless unknown.empty?
|
|
84
|
+
surface(name.to_s, id: "app.#{name}", options:, &block)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def dynamic(type: :column, id: nil, **props, &renderer)
|
|
88
|
+
raise ArgumentError, "dynamic requires a block" unless renderer
|
|
89
|
+
definition = @application.components.fetch(type)
|
|
90
|
+
raise ArgumentError, "dynamic component must be a container: #{type}" unless definition.container
|
|
91
|
+
|
|
92
|
+
node = component(type, id:, **props)
|
|
93
|
+
within_dynamic(node, &renderer)
|
|
94
|
+
@application.register_structure(node, renderer)
|
|
95
|
+
node
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def text(value = UNSET, id: nil, **props, &reader)
|
|
99
|
+
node = component(:text, id:, **props)
|
|
100
|
+
bind_or_set(node, "text", value, reader)
|
|
101
|
+
node
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def label(value, id: nil, **props) = component(:label, id:, text: value.to_s, **props)
|
|
105
|
+
def rich_text(markup, id: nil, **props) = component(:rich_text, id:, text: markup.to_s, **props)
|
|
106
|
+
def markdown(source, id: nil, **props) = component(:markdown, id:, text: source.to_s, **props)
|
|
107
|
+
def selectable_text(value, id: nil, **props) = component(:selectable_text, id:, text: value.to_s, **props)
|
|
108
|
+
|
|
109
|
+
def icon(name, id: nil, **props) = component(:icon, id:, name: name.to_s, **props)
|
|
110
|
+
def tooltip(value, id: nil, **props) = component(:tooltip, id:, text: value.to_s, **props)
|
|
111
|
+
def image(source, id: nil, **props) = component(:image, id:, source: source.to_s, **props)
|
|
112
|
+
def vector_image(source, id: nil, **props) = component(:vector_image, id:, source: source.to_s, **props)
|
|
113
|
+
def model_view_3d(source, id: nil, **props) = component(:model_view_3d, id:, source: source.to_s, **props)
|
|
114
|
+
def font_loader(source, id: nil, **props) = component(:font_loader, id:, source: source.to_s, **props)
|
|
115
|
+
def text_metrics(value, id: nil, **props) = component(:text_metrics, id:, text: value.to_s, **props)
|
|
116
|
+
def web_view(url = "", id: nil, **props) = component(:web_view, id:, url: url.to_s, **props)
|
|
117
|
+
def animated_image(source, id: nil, **props) = component(:animated_image, id:, source: source.to_s, **props)
|
|
118
|
+
def video(source, id: nil, **props) = component(:video, id:, source: source.to_s, **props)
|
|
119
|
+
def audio(source, id: nil, **props) = component(:audio, id:, source: source.to_s, **props)
|
|
120
|
+
def avatar(source = nil, id: nil, **props)
|
|
121
|
+
source.nil? ? component(:avatar, id:, **props) : component(:avatar, id:, source: source.to_s, **props)
|
|
122
|
+
end
|
|
123
|
+
def badge(value = nil, id: nil, **props) = component(:badge, id:, value:, **props)
|
|
124
|
+
def chip(label, id: nil, **props, &handler)
|
|
125
|
+
action_component(:chip, :text, label, id:, props:, handler:)
|
|
126
|
+
end
|
|
127
|
+
def border_image(source, id: nil, **props, &block) = component(:border_image, id:, source: source.to_s, **props, &block)
|
|
128
|
+
def window(title = "", id: nil, **props, &block)
|
|
129
|
+
component(:window, id:, title: title.to_s, **props, &block)
|
|
130
|
+
end
|
|
131
|
+
def application_window(title = "", id: nil, **props, &block)
|
|
132
|
+
component(:application_window, id:, title: title.to_s, **props, &block)
|
|
133
|
+
end
|
|
134
|
+
def page(title = "", id: nil, **props, &block)
|
|
135
|
+
component(:page, id:, title: title.to_s, **props, &block)
|
|
136
|
+
end
|
|
137
|
+
def pane(id: nil, **props, &block)
|
|
138
|
+
component(:pane, id:, **props, &block)
|
|
139
|
+
end
|
|
140
|
+
def frame(id: nil, **props, &block)
|
|
141
|
+
component(:frame, id:, **props, &block)
|
|
142
|
+
end
|
|
143
|
+
def group_box(title = "", id: nil, **props, &block)
|
|
144
|
+
component(:group_box, id:, title: title.to_s, **props, &block)
|
|
145
|
+
end
|
|
146
|
+
def tabs(labels = [], id: nil, **props, &block)
|
|
147
|
+
component(:tabs, id:, labels: Array(labels), **props, &block)
|
|
148
|
+
end
|
|
149
|
+
def tab_bar(items = [], id: nil, **props)
|
|
150
|
+
component(:tab_bar, id:, items: Array(items), **props)
|
|
151
|
+
end
|
|
152
|
+
def tab_button(label = "", id: nil, **props, &handler)
|
|
153
|
+
action_component(:tab_button, :text, label, id:, props:, handler:)
|
|
154
|
+
end
|
|
155
|
+
def page_indicator(count, id: nil, **props)
|
|
156
|
+
component(:page_indicator, id:, count:, **props)
|
|
157
|
+
end
|
|
158
|
+
def stack_view(id: nil, **props, &block)
|
|
159
|
+
component(:stack_view, id:, **props, &block)
|
|
160
|
+
end
|
|
161
|
+
def swipe_view(id: nil, **props, &block)
|
|
162
|
+
component(:swipe_view, id:, **props, &block)
|
|
163
|
+
end
|
|
164
|
+
def drawer(id: nil, **props, &block)
|
|
165
|
+
component(:drawer, id:, **props, &block)
|
|
166
|
+
end
|
|
167
|
+
def navigation_rail(items = [], id: nil, **props)
|
|
168
|
+
component(:navigation_rail, id:, items: Array(items), **props)
|
|
169
|
+
end
|
|
170
|
+
def breadcrumb(items = [], id: nil, **props)
|
|
171
|
+
component(:breadcrumb, id:, items: Array(items), **props)
|
|
172
|
+
end
|
|
173
|
+
def pagination(count, id: nil, **props)
|
|
174
|
+
component(:pagination, id:, count:, **props)
|
|
175
|
+
end
|
|
176
|
+
def expansion_panel(title = "", id: nil, **props, &block)
|
|
177
|
+
component(:expansion_panel, id:, title: title.to_s, **props, &block)
|
|
178
|
+
end
|
|
179
|
+
def accordion(titles = [], id: nil, **props, &block)
|
|
180
|
+
component(:accordion, id:, titles: Array(titles), **props, &block)
|
|
181
|
+
end
|
|
182
|
+
def tool_bar(id: nil, **props, &block)
|
|
183
|
+
component(:tool_bar, id:, **props, &block)
|
|
184
|
+
end
|
|
185
|
+
def tool_separator(id: nil, **props)
|
|
186
|
+
component(:tool_separator, id:, **props)
|
|
187
|
+
end
|
|
188
|
+
def menu(items = [], id: nil, **props)
|
|
189
|
+
component(:menu, id:, items: Array(items), **props)
|
|
190
|
+
end
|
|
191
|
+
def menu_item(label = "", id: nil, value: nil, **props, &handler)
|
|
192
|
+
item_props = props.merge(text: label.to_s)
|
|
193
|
+
item_props[:value] = value unless value.nil?
|
|
194
|
+
node = component(:menu_item, id:, **item_props)
|
|
195
|
+
@application.register_handler(node.id, :trigger, handler) if handler
|
|
196
|
+
node
|
|
197
|
+
end
|
|
198
|
+
def menu_separator(id: nil, **props)
|
|
199
|
+
component(:menu_separator, id:, **props)
|
|
200
|
+
end
|
|
201
|
+
def menu_bar(menus = [], id: nil, **props)
|
|
202
|
+
component(:menu_bar, id:, menus: Array(menus), **props)
|
|
203
|
+
end
|
|
204
|
+
def context_menu(items = [], id: nil, target: nil, **props)
|
|
205
|
+
target_id = target.is_a?(Node) ? target.id : target
|
|
206
|
+
menu_props = props.merge(items: Array(items))
|
|
207
|
+
menu_props[:target] = target_id.to_s unless target_id.nil?
|
|
208
|
+
component(:context_menu, id:, **menu_props)
|
|
209
|
+
end
|
|
210
|
+
def popup(id: nil, **props, &block)
|
|
211
|
+
component(:popup, id:, **props, &block)
|
|
212
|
+
end
|
|
213
|
+
def dialog(title = "", id: nil, **props, &block)
|
|
214
|
+
component(:dialog, id:, title: title.to_s, **props, &block)
|
|
215
|
+
end
|
|
216
|
+
def alert_dialog(title = "", message = "", id: nil, **props)
|
|
217
|
+
component(:alert_dialog, id:, title: title.to_s, message: message.to_s, **props)
|
|
218
|
+
end
|
|
219
|
+
def message_dialog(title = "", message = "", id: nil, **props)
|
|
220
|
+
component(:message_dialog, id:, title: title.to_s, message: message.to_s, **props)
|
|
221
|
+
end
|
|
222
|
+
def bottom_sheet(id: nil, **props, &block)
|
|
223
|
+
component(:bottom_sheet, id:, **props, &block)
|
|
224
|
+
end
|
|
225
|
+
def modal_sheet(title = "", id: nil, **props, &block)
|
|
226
|
+
component(:modal_sheet, id:, title: title.to_s, **props, &block)
|
|
227
|
+
end
|
|
228
|
+
def snackbar(message = "", id: nil, **props)
|
|
229
|
+
component(:snackbar, id:, message: message.to_s, **props)
|
|
230
|
+
end
|
|
231
|
+
def banner(message = "", id: nil, **props)
|
|
232
|
+
component(:banner, id:, message: message.to_s, **props)
|
|
233
|
+
end
|
|
234
|
+
def toast(message = "", id: nil, **props)
|
|
235
|
+
component(:toast, id:, message: message.to_s, **props)
|
|
236
|
+
end
|
|
237
|
+
def busy_indicator(running = true, id: nil, **props)
|
|
238
|
+
component(:busy_indicator, id:, running: running, **props)
|
|
239
|
+
end
|
|
240
|
+
def progress_ring(value = UNSET, id: nil, **props)
|
|
241
|
+
ring_props = props.dup
|
|
242
|
+
if value.equal?(UNSET)
|
|
243
|
+
ring_props[:indeterminate] = true unless ring_props.key?(:indeterminate)
|
|
244
|
+
else
|
|
245
|
+
ring_props[:value] = value
|
|
246
|
+
end
|
|
247
|
+
component(:progress_ring, id:, **ring_props)
|
|
248
|
+
end
|
|
249
|
+
def skeleton(id: nil, **props)
|
|
250
|
+
component(:skeleton, id:, **props)
|
|
251
|
+
end
|
|
252
|
+
def confirm_dialog(message = "", id: nil, **props)
|
|
253
|
+
component(:confirm_dialog, id:, message: message.to_s, **props)
|
|
254
|
+
end
|
|
255
|
+
def panel_hero(title = "", id: nil, **props)
|
|
256
|
+
component(:panel_hero, id:, title: title.to_s, **props)
|
|
257
|
+
end
|
|
258
|
+
def optical_glyph(text = "", id: nil, **props)
|
|
259
|
+
component(:optical_glyph, id:, text: text.to_s, **props)
|
|
260
|
+
end
|
|
261
|
+
def widget_button(text = "", id: nil, **props, &handler)
|
|
262
|
+
action_component(:widget_button, :text, text, id:, props:, handler:)
|
|
263
|
+
end
|
|
264
|
+
def list_view(items = [], id: nil, **props, &handler)
|
|
265
|
+
node = component(:list_view, id:, items: Array(items), **props)
|
|
266
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
267
|
+
node
|
|
268
|
+
end
|
|
269
|
+
def item_delegate(text = "", id: nil, value: nil, **props, &handler)
|
|
270
|
+
item_props = props.merge(text: text.to_s)
|
|
271
|
+
item_props[:value] = value unless value.nil?
|
|
272
|
+
node = component(:item_delegate, id:, **item_props)
|
|
273
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
274
|
+
node
|
|
275
|
+
end
|
|
276
|
+
def check_delegate(text = "", id: nil, value: nil, checked: UNSET, **props, &handler)
|
|
277
|
+
item_props = props.merge(text: text.to_s)
|
|
278
|
+
item_props[:value] = value unless value.nil?
|
|
279
|
+
item_props[:checked] = checked unless checked.equal?(UNSET)
|
|
280
|
+
node = component(:check_delegate, id:, **item_props)
|
|
281
|
+
@application.register_handler(node.id, :change, handler) if handler
|
|
282
|
+
node
|
|
283
|
+
end
|
|
284
|
+
def radio_delegate(text = "", id: nil, value: nil, checked: UNSET, **props, &handler)
|
|
285
|
+
item_props = props.merge(text: text.to_s)
|
|
286
|
+
item_props[:value] = value unless value.nil?
|
|
287
|
+
item_props[:checked] = checked unless checked.equal?(UNSET)
|
|
288
|
+
node = component(:radio_delegate, id:, **item_props)
|
|
289
|
+
@application.register_handler(node.id, :select, handler) if handler
|
|
290
|
+
node
|
|
291
|
+
end
|
|
292
|
+
def switch_delegate(text = "", id: nil, value: nil, checked: UNSET, **props, &handler)
|
|
293
|
+
item_props = props.merge(text: text.to_s)
|
|
294
|
+
item_props[:value] = value unless value.nil?
|
|
295
|
+
item_props[:checked] = checked unless checked.equal?(UNSET)
|
|
296
|
+
node = component(:switch_delegate, id:, **item_props)
|
|
297
|
+
@application.register_handler(node.id, :change, handler) if handler
|
|
298
|
+
node
|
|
299
|
+
end
|
|
300
|
+
def swipe_delegate(text = "", id: nil, value: nil, **props, &handler)
|
|
301
|
+
item_props = props.merge(text: text.to_s)
|
|
302
|
+
item_props[:value] = value unless value.nil?
|
|
303
|
+
node = component(:swipe_delegate, id:, **item_props)
|
|
304
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
305
|
+
node
|
|
306
|
+
end
|
|
307
|
+
def grid_view(items = [], id: nil, **props, &handler)
|
|
308
|
+
node = component(:grid_view, id:, items: Array(items), **props)
|
|
309
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
310
|
+
node
|
|
311
|
+
end
|
|
312
|
+
def table_view(rows = [], id: nil, columns: [], **props, &handler)
|
|
313
|
+
node = component(:table_view, id:, rows: Array(rows), columns: Array(columns), **props)
|
|
314
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
315
|
+
node
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def tree_view(rows = [], id: nil, columns: [], children_field: :children, **props, &handler)
|
|
319
|
+
node = component(:tree_view, id:, rows: Array(rows), columns: Array(columns),
|
|
320
|
+
children_field: children_field.to_s, **props)
|
|
321
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
322
|
+
node
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def data_table(rows = [], id: nil, columns: [], **props, &handler)
|
|
326
|
+
node = component(:data_table, id:, rows: Array(rows), columns: Array(columns), **props)
|
|
327
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
328
|
+
node
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def horizontal_header(sections = [], id: nil, **props) = component(:horizontal_header, id:, sections: Array(sections), **props)
|
|
332
|
+
def vertical_header(sections = [], id: nil, **props) = component(:vertical_header, id:, sections: Array(sections), **props)
|
|
333
|
+
def table_view_delegate(text = "", id: nil, **props) = component(:table_view_delegate, id:, text: text.to_s, **props)
|
|
334
|
+
def tree_view_delegate(text = "", id: nil, **props) = component(:tree_view_delegate, id:, text: text.to_s, **props)
|
|
335
|
+
def horizontal_header_delegate(text = "", id: nil, **props) = component(:horizontal_header_delegate, id:, text: text.to_s, **props)
|
|
336
|
+
def vertical_header_delegate(text = "", id: nil, **props) = component(:vertical_header_delegate, id:, text: text.to_s, **props)
|
|
337
|
+
def reorderable_list(items = [], id: nil, **props) = component(:reorderable_list, id:, items: Array(items), **props)
|
|
338
|
+
def carousel(items = [], id: nil, **props) = component(:carousel, id:, items: Array(items), **props)
|
|
339
|
+
def calendar(date = nil, id: nil, **props)
|
|
340
|
+
calendar_props = props.dup
|
|
341
|
+
calendar_props[:date] = date.to_s unless date.nil?
|
|
342
|
+
component(:calendar, id:, **calendar_props)
|
|
343
|
+
end
|
|
344
|
+
def month_grid(id: nil, **props) = component(:month_grid, id:, **props)
|
|
345
|
+
def week_number_column(id: nil, **props) = component(:week_number_column, id:, **props)
|
|
346
|
+
def day_of_week_row(id: nil, **props) = component(:day_of_week_row, id:, **props)
|
|
347
|
+
def tumbler(items = [], id: nil, **props) = component(:tumbler, id:, items: Array(items), **props)
|
|
348
|
+
|
|
349
|
+
def canvas(commands = [], id: nil, **props)
|
|
350
|
+
component(:canvas, id:, commands: Array(commands), **props)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def shape(path_data = "", id: nil, **props)
|
|
354
|
+
component(:shape, id:, path: path_data.to_s, **props)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def line(x1 = 0, y1 = 0, x2 = 100, y2 = 0, id: nil, **props)
|
|
358
|
+
component(:line, id:, x1:, y1:, x2:, y2:, **props)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def path(data = "", id: nil, **props)
|
|
362
|
+
component(:path, id:, data: data.to_s, **props)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def circle(radius = 50, id: nil, **props)
|
|
366
|
+
component(:circle, id:, radius:, **props)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def gradient(colors = [], id: nil, **props)
|
|
370
|
+
component(:gradient, id:, colors: Array(colors), **props)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def shader_effect(fragment_shader = nil, id: nil, shader: nil, **props, &block)
|
|
374
|
+
effect_props = props.dup
|
|
375
|
+
effect_props[:shader] = shader.to_s unless shader.nil?
|
|
376
|
+
effect_props[:fragment_shader] = fragment_shader.to_s unless fragment_shader.nil?
|
|
377
|
+
component(:shader_effect, id:, **effect_props, &block)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def shader_effect_source(id: nil, **props, &block)
|
|
381
|
+
component(:shader_effect_source, id:, **props, &block)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def particle_system(id: nil, **props)
|
|
385
|
+
component(:particle_system, id:, **props)
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def selection_rectangle(target, id: nil, **props) = component(:selection_rectangle, id:, target: node_id(target), **props)
|
|
389
|
+
def scroll_bar(target = nil, id: nil, **props)
|
|
390
|
+
props = props.merge(target: node_id(target)) unless target.nil?
|
|
391
|
+
component(:scroll_bar, id:, **props)
|
|
392
|
+
end
|
|
393
|
+
def scroll_indicator(target = nil, id: nil, **props)
|
|
394
|
+
props = props.merge(target: node_id(target)) unless target.nil?
|
|
395
|
+
component(:scroll_indicator, id:, **props)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def layout_item_proxy(target, id: nil, **props)
|
|
399
|
+
target_id = target.is_a?(Node) ? target.id : target.to_s
|
|
400
|
+
raise ArgumentError, "layout_item_proxy target cannot be empty" if target_id.empty?
|
|
401
|
+
component(:layout_item_proxy, id:, target: target_id, **props)
|
|
402
|
+
end
|
|
403
|
+
def border_overlay(id: nil, **props) = component(:border_overlay, id:, **props)
|
|
404
|
+
def spacer(id: nil, **props) = component(:spacer, id:, **props)
|
|
405
|
+
def separator(id: nil, **props) = component(:separator, id:, **props)
|
|
406
|
+
def divider(id: nil, **props) = component(:divider, id:, **props)
|
|
407
|
+
def section_header(value, id: nil, **props) = component(:section_header, id:, text: value.to_s, **props)
|
|
408
|
+
def dialog_button_box(buttons = [], id: nil, **props)
|
|
409
|
+
component(:dialog_button_box, id:, buttons: Array(buttons), **props)
|
|
410
|
+
end
|
|
411
|
+
def action(label, id: nil, **props, &handler)
|
|
412
|
+
node = component(:action, id:, text: label.to_s, **props)
|
|
413
|
+
@application.register_handler(node.id, :trigger, handler) if handler
|
|
414
|
+
node
|
|
415
|
+
end
|
|
416
|
+
def action_group(actions, id: nil, checked: nil, **props)
|
|
417
|
+
action_ids = Array(actions).map { |action_node| action_node.is_a?(Node) ? action_node.id : action_node.to_s }
|
|
418
|
+
checked_id = checked.is_a?(Node) ? checked.id : checked&.to_s
|
|
419
|
+
component(:action_group, id:, action_ids:, checked_action: checked_id, **props)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def button(label, id: nil, **props, &handler)
|
|
423
|
+
action_component(:button, :text, label, id:, props:, handler:)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def round_button(label = "", id: nil, **props, &handler)
|
|
427
|
+
action_component(:round_button, :text, label, id:, props:, handler:)
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def tool_button(label = "", id: nil, **props, &handler)
|
|
431
|
+
action_component(:tool_button, :text, label, id:, props:, handler:)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def delay_button(label, id: nil, **props, &handler)
|
|
435
|
+
node = action_component(:delay_button, :text, label, id:, props: props, handler: nil)
|
|
436
|
+
@application.register_handler(node.id, :activate, handler) if handler
|
|
437
|
+
node
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def action_button(icon, id: nil, **props, &handler)
|
|
441
|
+
action_component(:action_button, :icon, icon, id:, props:, handler:)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def bar_icon_button(icon, id: nil, **props, &handler)
|
|
445
|
+
action_component(:bar_icon_button, :icon, icon, id:, props:, handler:)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def bar_indicator(active_icon, id: nil, **props, &handler)
|
|
449
|
+
action_component(:bar_indicator, :active_icon, active_icon, id:, props:, handler:)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
def toggle(label = "", id: nil, checked: UNSET, **props, &handler)
|
|
453
|
+
input_component(:toggle, :checked, checked, id:, props: props.merge(label: label.to_s), handler:)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def checkbox(label = "", id: nil, checked: UNSET, **props, &handler)
|
|
457
|
+
input_component(:checkbox, :checked, checked, id:, props: props.merge(label: label.to_s), handler:)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def radio_button(label = "", id: nil, checked: UNSET, **props, &handler)
|
|
461
|
+
input_component(:radio_button, :checked, checked, id:, props: props.merge(label: label.to_s), handler:)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def search_field(value = "", id: nil, **props, &handler)
|
|
465
|
+
node = component(:search_field, id:, text: value.to_s, **props)
|
|
466
|
+
@application.register_handler(node.id, :search, handler) if handler
|
|
467
|
+
node
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def toggle_switch(id: nil, checked: UNSET, **props, &handler)
|
|
471
|
+
input_component(:toggle_switch, :checked, checked, id:, props:, handler:)
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
VALUE_INPUTS.each do |type, property|
|
|
475
|
+
define_method(type) do |value = UNSET, id: nil, **props, &handler|
|
|
476
|
+
input_component(type, property, value, id:, props:, handler:)
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def progress(value = UNSET, id: nil, **props)
|
|
481
|
+
input_component(:progress, :value, value, id:, props:)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def range_slider(lower, upper, id: nil, **props, &handler)
|
|
485
|
+
node = component(:range_slider, id:, lower:, upper:, **props)
|
|
486
|
+
@application.register_handler(node.id, :change, handler) if handler
|
|
487
|
+
node
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def line_chart(values, id: nil, **props)
|
|
491
|
+
component(:line_chart, id:, values:, **props)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def area_chart(values, id: nil, **props)
|
|
495
|
+
component(:area_chart, id:, values:, **props)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def bar_chart(values, id: nil, **props)
|
|
499
|
+
component(:bar_chart, id:, values:, **props)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
CHART_COMPONENTS = %i[
|
|
503
|
+
stacked_bar_chart pie_chart donut_chart scatter_chart bubble_chart radar_chart heatmap
|
|
504
|
+
sparkline gauge radial_gauge histogram candlestick_chart
|
|
505
|
+
].freeze
|
|
506
|
+
CHART_COMPONENTS.each do |type|
|
|
507
|
+
define_method(type) do |values = [], id: nil, **props|
|
|
508
|
+
component(type, id:, values: Array(values), **props)
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def legend(items = [], id: nil, **props) = component(:legend, id:, items: Array(items), **props)
|
|
513
|
+
|
|
514
|
+
def on_click(&handler)
|
|
515
|
+
raise ArgumentError, "on_click must be inside a surface or control" if @stack.empty?
|
|
516
|
+
on(@stack.last, :click, &handler)
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def on(target_or_event, event = nil, &handler)
|
|
520
|
+
raise ArgumentError, "on requires a block" unless handler
|
|
521
|
+
if target_or_event.is_a?(Node)
|
|
522
|
+
raise ArgumentError, "on(node, event) requires an event" unless event
|
|
523
|
+
node, event_name = target_or_event, event
|
|
524
|
+
else
|
|
525
|
+
raise ArgumentError, "on must be inside a surface or control" if @stack.empty?
|
|
526
|
+
node, event_name = @stack.last, target_or_event
|
|
527
|
+
end
|
|
528
|
+
@application.register_handler(node.id, event_name, handler)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def property(name, value = UNSET, &reader)
|
|
532
|
+
raise ArgumentError, "property must be inside a control" if @stack.empty?
|
|
533
|
+
bind_or_set(@stack.last, name.to_s, value, reader)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def bind(node, property, animation: nil, &reader)
|
|
537
|
+
raise ArgumentError, "bind requires a node returned by a component method" unless node.is_a?(Node)
|
|
538
|
+
raise ArgumentError, "bind requires a block" unless reader
|
|
539
|
+
transition = normalize_animation(animation)
|
|
540
|
+
@application.register_binding(node, property.to_s, reader, animation: transition)
|
|
541
|
+
node
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
def animation(target: nil, id: nil, **options)
|
|
545
|
+
return Animation.new(**options) if target.nil?
|
|
546
|
+
component(:animation, id:, target: node_id(target), **options)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
TARGET_ANIMATION_COMPONENTS = %i[
|
|
550
|
+
number_animation color_animation rotation_animation vector_animation path_animation property_animation
|
|
551
|
+
spring_animation smoothed_animation anchor_animation parent_animation opacity_animator rotation_animator
|
|
552
|
+
scale_animator x_animator y_animator uniform_animator animation_controller behavior transition state_group
|
|
553
|
+
property_changes anchor_changes parent_change
|
|
554
|
+
].freeze
|
|
555
|
+
TARGET_ANIMATION_COMPONENTS.each do |type|
|
|
556
|
+
define_method(type) do |target, id: nil, **props|
|
|
557
|
+
component(type, id:, target: node_id(target), **props)
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def pause_animation(duration = 0, id: nil, **props) = component(:pause_animation, id:, duration:, **props)
|
|
562
|
+
def script_action(action = nil, id: nil, **props) = component(:script_action, id:, action:, **props)
|
|
563
|
+
def property_action(target, property:, value:, id: nil, **props)
|
|
564
|
+
component(:property_action, id:, target: node_id(target), property: property.to_s, value:, **props)
|
|
565
|
+
end
|
|
566
|
+
def parallel_animation(animations = [], id: nil, **props)
|
|
567
|
+
component(:parallel_animation, id:, animations: normalize_animation_specs(animations), **props)
|
|
568
|
+
end
|
|
569
|
+
def sequential_animation(animations = [], id: nil, **props)
|
|
570
|
+
component(:sequential_animation, id:, animations: normalize_animation_specs(animations), **props)
|
|
571
|
+
end
|
|
572
|
+
def frame_animation(id: nil, **props) = component(:frame_animation, id:, **props)
|
|
573
|
+
def timer(interval, id: nil, **props) = component(:timer, id:, interval:, **props)
|
|
574
|
+
|
|
575
|
+
def media_player(source = "", id: nil, **props) = component(:media_player, id:, source: source.to_s, **props)
|
|
576
|
+
def video_output(source = nil, id: nil, **props)
|
|
577
|
+
props = props.merge(source: node_id(source)) unless source.nil?
|
|
578
|
+
component(:video_output, id:, **props)
|
|
579
|
+
end
|
|
580
|
+
def sound_effect(source = "", id: nil, **props) = component(:sound_effect, id:, source: source.to_s, **props)
|
|
581
|
+
def camera(id: nil, **props) = component(:camera, id:, **props)
|
|
582
|
+
def image_capture(session = nil, id: nil, **props)
|
|
583
|
+
props = props.merge(session: node_id(session)) unless session.nil?
|
|
584
|
+
component(:image_capture, id:, **props)
|
|
585
|
+
end
|
|
586
|
+
def media_recorder(session = nil, id: nil, **props)
|
|
587
|
+
props = props.merge(session: node_id(session)) unless session.nil?
|
|
588
|
+
component(:media_recorder, id:, **props)
|
|
589
|
+
end
|
|
590
|
+
def audio_input(id: nil, **props) = component(:audio_input, id:, **props)
|
|
591
|
+
def audio_output(id: nil, **props) = component(:audio_output, id:, **props)
|
|
592
|
+
def media_devices(id: nil, **props) = component(:media_devices, id:, **props)
|
|
593
|
+
def screen_capture(id: nil, **props) = component(:screen_capture, id:, **props)
|
|
594
|
+
def window_capture(id: nil, **props) = component(:window_capture, id:, **props)
|
|
595
|
+
|
|
596
|
+
def list_model(items = [], id: nil, **props) = component(:list_model, id:, items: Array(items), **props)
|
|
597
|
+
def delegate_model(items = [], id: nil, **props) = component(:delegate_model, id:, items: Array(items), **props)
|
|
598
|
+
def delegate_model_group(items = [], id: nil, **props) = component(:delegate_model_group, id:, items: Array(items), **props)
|
|
599
|
+
def sort_filter_proxy_model(items = [], id: nil, **props) = component(:sort_filter_proxy_model, id:, items: Array(items), **props)
|
|
600
|
+
def folder_list_model(folder = "", id: nil, **props) = component(:folder_list_model, id:, folder: folder.to_s, **props)
|
|
601
|
+
def settings(values = {}, id: nil, **props) = component(:settings, id:, values:, **props)
|
|
602
|
+
def standard_paths(location = :home, id: nil, **props) = component(:standard_paths, id:, location: location.to_s, **props)
|
|
603
|
+
def clipboard(text = UNSET, id: nil, **props)
|
|
604
|
+
props = props.merge(text:) unless text.equal?(UNSET)
|
|
605
|
+
component(:clipboard, id:, **props)
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def animate(node, properties, duration: 200, easing: :in_out_quad, delay: 0)
|
|
609
|
+
transition = Animation.new(duration:, easing:, delay:)
|
|
610
|
+
@application.animate(node, properties, transition)
|
|
611
|
+
node
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
def animate_sequence(node, steps)
|
|
615
|
+
elapsed = 0
|
|
616
|
+
tracks = steps.flat_map do |step|
|
|
617
|
+
options = step.transform_keys(&:to_sym)
|
|
618
|
+
properties = options.fetch(:to)
|
|
619
|
+
transition = Animation.new(
|
|
620
|
+
duration: options.fetch(:duration, 200),
|
|
621
|
+
easing: options.fetch(:easing, :in_out_quad),
|
|
622
|
+
delay: elapsed + options.fetch(:delay, 0)
|
|
623
|
+
)
|
|
624
|
+
elapsed = transition.delay + transition.duration + options.fetch(:pause, 0)
|
|
625
|
+
@application.animation_tracks(node, properties, transition)
|
|
626
|
+
end
|
|
627
|
+
@application.emit_animation(node, tracks)
|
|
628
|
+
node
|
|
629
|
+
end
|
|
630
|
+
def transaction(&block) = @application.state.transaction { instance_eval(&block) }
|
|
631
|
+
def after(seconds, &block) = @application.schedule(:after, interval: seconds, &block)
|
|
632
|
+
def every(seconds, immediate: false, &block) = @application.schedule(:every, interval: seconds, immediate:, &block)
|
|
633
|
+
def async(&block) = @application.schedule(:async, &block)
|
|
634
|
+
def run_command(argv, **options) = Command.run(argv, **options)
|
|
635
|
+
def rebuild(node, &renderer) = within_dynamic(node, &renderer)
|
|
636
|
+
def open_panel(name) = @application.emit_effect("open_panel", "surface" => name.to_s)
|
|
637
|
+
|
|
638
|
+
def close_panel(name = nil)
|
|
639
|
+
@application.emit_effect("close_panel", name.nil? ? {} : { "surface" => name.to_s })
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
private
|
|
643
|
+
|
|
644
|
+
def node_id(value)
|
|
645
|
+
value.is_a?(Node) ? value.id : value.to_s
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def normalize_animation_specs(animations)
|
|
649
|
+
Array(animations).map do |animation_spec|
|
|
650
|
+
next animation_spec unless animation_spec.is_a?(Hash)
|
|
651
|
+
|
|
652
|
+
animation_spec.each_with_object({}) do |(key, value), normalized|
|
|
653
|
+
normalized[key] = key.to_sym == :target && value.is_a?(Node) ? value.id : value
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
def scoped_id(type)
|
|
659
|
+
return nil if @dynamic_scopes.empty?
|
|
660
|
+
scope = @dynamic_scopes.last
|
|
661
|
+
scope[:sequence] += 1
|
|
662
|
+
"#{scope.fetch(:id)}.#{type}.#{scope.fetch(:sequence)}"
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
def within_dynamic(node, &block)
|
|
666
|
+
@dynamic_scopes.push({ id: node.id, sequence: 0 })
|
|
667
|
+
within(node, &block)
|
|
668
|
+
ensure
|
|
669
|
+
@dynamic_scopes.pop
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def surface(name, id:, options: {}, &block)
|
|
673
|
+
raise ArgumentError, "surface requires a block" unless block
|
|
674
|
+
node = @application.build_node(:container, explicit_id: id)
|
|
675
|
+
@application.add_surface(name, node, options:)
|
|
676
|
+
within(node, &block)
|
|
677
|
+
node
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
def action_component(type, property, value, id:, props:, handler:)
|
|
681
|
+
node = component(type, id:, **props.merge(property => value.to_s))
|
|
682
|
+
@application.register_handler(node.id, :click, handler) if handler
|
|
683
|
+
node
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def input_component(type, property, value, id:, props:, handler: nil)
|
|
687
|
+
props = props.merge(property => value) unless value.equal?(UNSET)
|
|
688
|
+
node = component(type, id:, **props)
|
|
689
|
+
event = %i[text_field password_field].include?(type) ? :input : :change
|
|
690
|
+
@application.register_handler(node.id, event, handler) if handler
|
|
691
|
+
node
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def within(node, &block)
|
|
695
|
+
@stack.push(node)
|
|
696
|
+
instance_eval(&block)
|
|
697
|
+
ensure
|
|
698
|
+
@stack.pop
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def append(node)
|
|
702
|
+
raise ArgumentError, "#{node.type} must be inside a surface or container" if @stack.empty?
|
|
703
|
+
@stack.last.children << node
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def bind_or_set(node, property, value, reader)
|
|
707
|
+
if reader
|
|
708
|
+
raise ArgumentError, "pass a value or a reactive block, not both" unless value.equal?(UNSET)
|
|
709
|
+
@application.register_binding(node, property, reader)
|
|
710
|
+
else
|
|
711
|
+
raise ArgumentError, "#{property} requires a value or block" if value.equal?(UNSET)
|
|
712
|
+
node.props[property] = @application.normalize_value(value, property)
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def normalize_animation(animation)
|
|
717
|
+
case animation
|
|
718
|
+
when nil then nil
|
|
719
|
+
when Animation then animation
|
|
720
|
+
when Hash then Animation.new(**animation)
|
|
721
|
+
else raise ArgumentError, "animation must be a Zui::Animation or options hash"
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
end
|
|
725
|
+
end
|