form_components 0.1.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/CHANGELOG.md +29 -0
- data/MIT-LICENSE +20 -0
- data/README.md +194 -0
- data/app/assets/stylesheets/form_components/application.css +9 -0
- data/app/assets/tailwind/form_components/engine.css +6 -0
- data/app/components/form_components/base/component.rb +76 -0
- data/app/components/form_components/check_box/component.html.slim +13 -0
- data/app/components/form_components/check_box/component.rb +26 -0
- data/app/components/form_components/collection/component.html.slim +10 -0
- data/app/components/form_components/collection/component.rb +76 -0
- data/app/components/form_components/collection_check_boxes/component.rb +12 -0
- data/app/components/form_components/collection_radio_buttons/component.rb +12 -0
- data/app/components/form_components/color_field/component.css +69 -0
- data/app/components/form_components/color_field/component.html.slim +8 -0
- data/app/components/form_components/color_field/component.rb +26 -0
- data/app/components/form_components/color_field/component_controller.js +25 -0
- data/app/components/form_components/combobox/component.html.slim +8 -0
- data/app/components/form_components/combobox/component.rb +28 -0
- data/app/components/form_components/field/component.rb +58 -0
- data/app/components/form_components/file_field/component.html.slim +10 -0
- data/app/components/form_components/file_field/component.rb +25 -0
- data/app/components/form_components/label/component.rb +31 -0
- data/app/components/form_components/nested_form/component.html.slim +20 -0
- data/app/components/form_components/nested_form/component.rb +71 -0
- data/app/components/form_components/nested_form/component_controller.js +36 -0
- data/app/components/form_components/radio_button/component.html.slim +12 -0
- data/app/components/form_components/radio_button/component.rb +24 -0
- data/app/components/form_components/rating_field/component.rb +39 -0
- data/app/components/form_components/scale_field/component.rb +44 -0
- data/app/components/form_components/select/component.html.slim +8 -0
- data/app/components/form_components/select/component.rb +35 -0
- data/app/components/form_components/submit/component.rb +21 -0
- data/app/components/form_components/text_area/component.html.slim +14 -0
- data/app/components/form_components/text_area/component.rb +22 -0
- data/app/components/form_components/text_field/component.html.slim +16 -0
- data/app/components/form_components/text_field/component.rb +33 -0
- data/app/components/form_components/validation.rb +41 -0
- data/app/form_builders/form_components/form_builder.rb +133 -0
- data/app/form_builders/form_components/validation_context.rb +17 -0
- data/app/javascript/form_components/application.js +12 -0
- data/config/importmap.rb +6 -0
- data/lib/form_components/engine.rb +23 -0
- data/lib/form_components/version.rb +5 -0
- data/lib/form_components.rb +13 -0
- data/vendor/javascript/form_components/jscolorpicker/LICENSE +21 -0
- data/vendor/javascript/form_components/jscolorpicker/colorpicker.css +409 -0
- data/vendor/javascript/form_components/jscolorpicker/colorpicker.js +2410 -0
- metadata +145 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FormComponents
|
|
4
|
+
# A form builder whose fields are components. Each helper takes the
|
|
5
|
+
# arguments Rails' takes, and its input is Rails' own (see
|
|
6
|
+
# Base::Component#rails), so names, ids, values and options are Rails'; the
|
|
7
|
+
# components add a label, a description, errors and the like around it,
|
|
8
|
+
# with options of their own (label:, description:, …) that Rails doesn't
|
|
9
|
+
# get. Helpers without a component here, such as hidden_field or date_select,
|
|
10
|
+
# are Rails'.
|
|
11
|
+
#
|
|
12
|
+
# = form_with model: @post, builder: FormComponents::FormBuilder do |f|
|
|
13
|
+
# = f.text_field :title
|
|
14
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
|
15
|
+
include FormComponents::ValidationContext
|
|
16
|
+
|
|
17
|
+
# Rails' text fields of every kind, as TextField
|
|
18
|
+
TEXT_FIELDS = %i[text_field password_field search_field telephone_field phone_field date_field time_field
|
|
19
|
+
datetime_field datetime_local_field month_field week_field url_field email_field number_field].freeze
|
|
20
|
+
|
|
21
|
+
def label(method, text = nil, options = {}, &block)
|
|
22
|
+
component(:label, method, text, options, &block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
TEXT_FIELDS.each do |helper|
|
|
26
|
+
define_method(helper) do |method, options = {}|
|
|
27
|
+
component(:text_field, method, options, helper: helper)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def text_area(method, options = {})
|
|
32
|
+
component(:text_area, method, options)
|
|
33
|
+
end
|
|
34
|
+
alias_method :textarea, :text_area
|
|
35
|
+
|
|
36
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
37
|
+
component(:check_box, method, options, checked_value, unchecked_value)
|
|
38
|
+
end
|
|
39
|
+
alias_method :checkbox, :check_box
|
|
40
|
+
|
|
41
|
+
def radio_button(method, tag_value, options = {})
|
|
42
|
+
component(:radio_button, method, tag_value, options)
|
|
43
|
+
end
|
|
44
|
+
alias_method :radiobutton, :radio_button
|
|
45
|
+
|
|
46
|
+
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
|
47
|
+
component(:collection_check_boxes, method, collection, value_method, text_method, options, html_options, &block)
|
|
48
|
+
end
|
|
49
|
+
alias_method :collection_checkboxes, :collection_check_boxes
|
|
50
|
+
|
|
51
|
+
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
|
52
|
+
component(:collection_radio_buttons, method, collection, value_method, text_method, options, html_options, &block)
|
|
53
|
+
end
|
|
54
|
+
alias_method :collection_radiobuttons, :collection_radio_buttons
|
|
55
|
+
|
|
56
|
+
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
|
57
|
+
component(:select, method, [choices], options, html_options, &block)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
|
61
|
+
component(:select, method, [collection, value_method, text_method], options, html_options, helper: :collection_select)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
|
65
|
+
component(:select, method, [collection, group_method, group_label_method, option_key_method, option_value_method], options, html_options, helper: :grouped_collection_select)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
|
|
69
|
+
component(:select, method, [priority_zones], options, html_options, helper: :time_zone_select)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def weekday_select(method, options = {}, html_options = {})
|
|
73
|
+
component(:select, method, [], options, html_options, helper: :weekday_select)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# hotwire_combobox's combobox, when the host has that gem
|
|
77
|
+
def combobox(method, options_or_src = [], **options, &block)
|
|
78
|
+
component(:combobox, method, options_or_src, options, &block)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Stars to rate with, from 1 to max: (5)
|
|
82
|
+
def rating_field(method, options = {})
|
|
83
|
+
component(:rating_field, method, options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# A number to pick on a scale, in: (1..5), with min_label: and max_label:
|
|
87
|
+
# at its ends
|
|
88
|
+
def scale_field(method, options = {})
|
|
89
|
+
component(:scale_field, method, options)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# A text field for a color picker, see ColorField: with swatches: to pick
|
|
93
|
+
# from, and default: shown while it's empty
|
|
94
|
+
def color_field(method, options = {})
|
|
95
|
+
component(:color_field, method, options)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# With preview: an attachment to show above the field
|
|
99
|
+
def file_field(method, options = {})
|
|
100
|
+
component(:file_field, method, options)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def submit(value = nil, options = {})
|
|
104
|
+
FormComponents::Submit::Component.new(self, @object, value, options).render_in(@template)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Renders a Stimulus-driven nested form for a has_many association.
|
|
108
|
+
# The block receives the component (nf) so you can declare slots:
|
|
109
|
+
#
|
|
110
|
+
# = f.nested_form :sections do |nf|
|
|
111
|
+
# - nf.with_item do |sf|
|
|
112
|
+
# .section-wrapper data-new-record=sf.object.new_record?.to_s
|
|
113
|
+
# = sf.hidden_field :_destroy
|
|
114
|
+
# = sf.text_field :title
|
|
115
|
+
# - nf.with_add_button
|
|
116
|
+
# button type="button" data-action="form-components--nested-form#add" Add section
|
|
117
|
+
#
|
|
118
|
+
# Options (keyword arguments):
|
|
119
|
+
# placeholder: – Stimulus token replaced on clone, default "NEW_<SINGULAR>"
|
|
120
|
+
# wrapper_selector: – CSS selector for the remove handler, default ".<singular>-wrapper"
|
|
121
|
+
# new_record: – template record object, default association_class.new
|
|
122
|
+
# add_label: – label for the default add button
|
|
123
|
+
def nested_form(method, **options, &block)
|
|
124
|
+
FormComponents::NestedForm::Component.new(self, @object, method, options).render_in(@template, &block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def component(name, method, *, **, &block)
|
|
130
|
+
FormComponents.const_get("#{name.to_s.camelize}::Component").new(self, @object, method, *, **, &block).render_in(@template)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FormComponents
|
|
4
|
+
module ValidationContext
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
attr_reader :validation_context
|
|
9
|
+
alias_method :original_initialize, :initialize
|
|
10
|
+
|
|
11
|
+
def initialize(*args, &block)
|
|
12
|
+
original_initialize(*args, &block)
|
|
13
|
+
@validation_context = options[:validation_context]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Registers the components' Stimulus controllers with the host's
|
|
2
|
+
// application:
|
|
3
|
+
//
|
|
4
|
+
// import { registerFormComponents } from "form_components"
|
|
5
|
+
// registerFormComponents(window.Stimulus)
|
|
6
|
+
import NestedFormController from "form_components/components/nested_form/component_controller"
|
|
7
|
+
import ColorFieldController from "form_components/components/color_field/component_controller"
|
|
8
|
+
|
|
9
|
+
export function registerFormComponents(application) {
|
|
10
|
+
application.register("form-components--nested-form", NestedFormController)
|
|
11
|
+
application.register("form-components--color-field", ColorFieldController)
|
|
12
|
+
}
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Pins for form_components. The host app registers the controllers with:
|
|
2
|
+
# import { registerFormComponents } from "form_components"
|
|
3
|
+
|
|
4
|
+
pin "form_components", to: "form_components/application.js"
|
|
5
|
+
pin_all_from FormComponents::Engine.root.join("app/components/form_components"), under: "form_components/components", to: "form_components"
|
|
6
|
+
pin "form_components/jscolorpicker", to: "form_components/jscolorpicker/colorpicker.js"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FormComponents
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
engine_name "form_components"
|
|
6
|
+
|
|
7
|
+
initializer "form_components.assets" do |app|
|
|
8
|
+
if app.config.respond_to?(:assets)
|
|
9
|
+
app.config.assets.paths << root.join("app/javascript")
|
|
10
|
+
# The components' Stimulus controllers
|
|
11
|
+
app.config.assets.paths << root.join("app/components")
|
|
12
|
+
# JS Color Picker, for the color field
|
|
13
|
+
app.config.assets.paths << root.join("vendor/javascript")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
initializer "form_components.importmap", before: "importmap" do |app|
|
|
18
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
19
|
+
app.config.importmap.cache_sweepers << root.join("app/javascript")
|
|
20
|
+
app.config.importmap.cache_sweepers << root.join("app/components")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "view_component"
|
|
4
|
+
require "slim-rails"
|
|
5
|
+
require "importmap-rails"
|
|
6
|
+
require "form_components/version"
|
|
7
|
+
require "form_components/engine"
|
|
8
|
+
|
|
9
|
+
# A form builder whose fields are view components, each in
|
|
10
|
+
# app/components/form_components/<name>/ with its template and, for some, a
|
|
11
|
+
# Stimulus controller and styles. See the README.
|
|
12
|
+
module FormComponents
|
|
13
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 Zygomatic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
[data-bs-theme='dark'],
|
|
2
|
+
[data-cp-theme='dark'] {
|
|
3
|
+
--cp-body-bg: #212529 !important;
|
|
4
|
+
--cp-body-color: #dee2e6 !important;
|
|
5
|
+
--cp-body-color-rgb: 222, 226, 230 !important;
|
|
6
|
+
|
|
7
|
+
--cp-border-color: #666 !important;
|
|
8
|
+
--cp-hover-color: #777 !important;
|
|
9
|
+
--cp-button-color: #666 !important;
|
|
10
|
+
--cp-border-color-translucent: rgba(255, 255, 255, 0.15) !important;
|
|
11
|
+
--cp-tertiary-color: rgba(222, 226, 230, 0.5) !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:root,
|
|
15
|
+
[data-bs-theme='light'],
|
|
16
|
+
[data-cp-theme='light'] {
|
|
17
|
+
--cp-body-bg: #fff;
|
|
18
|
+
--cp-body-color: #212529;
|
|
19
|
+
--cp-body-color-rgb: 33, 37, 41;
|
|
20
|
+
|
|
21
|
+
--cp-border-color: #ccc;
|
|
22
|
+
--cp-hover-color: #999;
|
|
23
|
+
--cp-button-color: #ccc;
|
|
24
|
+
--cp-border-color-translucent: rgba(0, 0, 0, 0.175);
|
|
25
|
+
--cp-tertiary-color: rgba(33, 37, 41, 0.5);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:root {
|
|
29
|
+
--cp-size: 2.375rem;
|
|
30
|
+
--cp-delay: 150ms;
|
|
31
|
+
|
|
32
|
+
--cp-border-radius-sm: 0.25rem;
|
|
33
|
+
--cp-border-radius-lg: 0.5rem;
|
|
34
|
+
--cp-swatch-width: 1.5rem;
|
|
35
|
+
|
|
36
|
+
--cp-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
|
37
|
+
--cp-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
38
|
+
|
|
39
|
+
--cp-bg-checker: repeating-conic-gradient(#808080 0% 25%, transparent 0% 50%) 50% / 0.5rem 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.color-picker, .cp_dialog {
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Picker toggle button */
|
|
47
|
+
.color-picker {
|
|
48
|
+
display: inline-flex;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
padding: 0;
|
|
51
|
+
|
|
52
|
+
border: 1px solid var(--cp-border-color);
|
|
53
|
+
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
border-radius: var(--cp-border-radius-sm);
|
|
56
|
+
background-color: transparent !important; /* CSS reset */
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.color-picker:not(.cp_wide) {
|
|
60
|
+
padding: 1px;
|
|
61
|
+
height: var(--cp-size);
|
|
62
|
+
border: 2px solid var(--cp-border-color);
|
|
63
|
+
border-style: solid;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.color-picker:hover {
|
|
67
|
+
border-color: var(--cp-hover-color) !important;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.cp_wide {
|
|
71
|
+
border: 1px solid var(--cp-border-color);
|
|
72
|
+
height: var(--cp-size);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.cp_dialog input,
|
|
76
|
+
.cp_dialog button {
|
|
77
|
+
margin: 0; /* CSS reset */
|
|
78
|
+
border-radius: 0; /* CSS reset */
|
|
79
|
+
}
|
|
80
|
+
.cp_dialog button:hover {
|
|
81
|
+
background-color: var(--cp-border-color-translucent) !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.cp_input {
|
|
85
|
+
display: none !important;
|
|
86
|
+
pointer-events: none;
|
|
87
|
+
margin: 0;
|
|
88
|
+
border: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.cp_wide .cp_input {
|
|
92
|
+
font-family: inherit;
|
|
93
|
+
display: block !important;
|
|
94
|
+
flex-grow: 1;
|
|
95
|
+
padding-left: 0.75rem;
|
|
96
|
+
height: inherit; /* or: var(--cp-size) */
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.cp_wide .cp_input + .cp_button {
|
|
100
|
+
border-left: 1px solid var(--cp-border-color);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.cp_wrap {
|
|
104
|
+
display: flex;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cp_button {
|
|
109
|
+
height: 100%;
|
|
110
|
+
aspect-ratio: 1;
|
|
111
|
+
|
|
112
|
+
text-align: start;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
|
|
116
|
+
display: flex;
|
|
117
|
+
justify-content: end;
|
|
118
|
+
align-items: end;
|
|
119
|
+
|
|
120
|
+
background: linear-gradient(var(--cp-current-color), var(--cp-current-color)), var(--cp-bg-checker);
|
|
121
|
+
|
|
122
|
+
border-radius: 3px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.cp_button.cp_unset {
|
|
126
|
+
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PHBhdGggZmlsbD0iIzM4M2E0YyIgZD0iTTIwOC40OSAxOTEuNTFhMTIgMTIgMCAwIDEtMTcgMTdMMTI4IDE0NWwtNjMuNTEgNjMuNDlhMTIgMTIgMCAwIDEtMTctMTdMMTExIDEyOEw0Ny41MSA2NC40OWExMiAxMiAwIDAgMSAxNy0xN0wxMjggMTExbDYzLjUxLTYzLjUyYTEyIDEyIDAgMCAxIDE3IDE3TDE0NSAxMjhaIi8+PC9zdmc+),
|
|
127
|
+
var(--cp-border-color);
|
|
128
|
+
background-size: 100% 100%;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.cp_caret {
|
|
132
|
+
background-color: var(--cp-border-color);
|
|
133
|
+
color: var(--cp-body);
|
|
134
|
+
font-size: calc(var(--cp-size) / 3);
|
|
135
|
+
|
|
136
|
+
padding: 0.1rem 0 0 0.1rem;
|
|
137
|
+
border-top-left-radius: var(--cp-border-radius-sm);
|
|
138
|
+
border-top: 1px solid var(--cp-border-color-translucent);
|
|
139
|
+
border-left: 1px solid var(--cp-border-color-translucent);
|
|
140
|
+
|
|
141
|
+
display: grid;
|
|
142
|
+
place-items: center;
|
|
143
|
+
color: var(--cp-body-color);
|
|
144
|
+
}
|
|
145
|
+
.cp_caret svg {
|
|
146
|
+
transition: transform var(--cp-delay) ease-in-out;
|
|
147
|
+
}
|
|
148
|
+
.cp_open .cp_caret svg {
|
|
149
|
+
transform: rotate(180deg);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Picker dialog */
|
|
153
|
+
.cp_dialog {
|
|
154
|
+
--cp-base-color: #000;
|
|
155
|
+
--cp-current-color: transparent;
|
|
156
|
+
--cp-color: transparent;
|
|
157
|
+
--cp-hue: 0;
|
|
158
|
+
--cp-alpha: 0;
|
|
159
|
+
--cp-primary: hsl(var(--cp-hue), 50%, 60%);
|
|
160
|
+
|
|
161
|
+
position: absolute;
|
|
162
|
+
z-index: 1055;
|
|
163
|
+
width: 17.2rem;
|
|
164
|
+
|
|
165
|
+
background-color: var(--cp-body-bg);
|
|
166
|
+
|
|
167
|
+
border: 1px solid var(--cp-border-color-translucent);
|
|
168
|
+
border-radius: var(--cp-border-radius-lg);
|
|
169
|
+
box-shadow: var(--cp-box-shadow);
|
|
170
|
+
|
|
171
|
+
opacity: 0;
|
|
172
|
+
pointer-events: none;
|
|
173
|
+
|
|
174
|
+
transition: opacity var(--cp-delay) ease-in-out;
|
|
175
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
|
176
|
+
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.cp_dialog-inner {
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
|
|
183
|
+
gap: 0.75rem;
|
|
184
|
+
padding: 0.75rem;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.cp_dialog.cp_open {
|
|
188
|
+
opacity: 1;
|
|
189
|
+
pointer-events: all;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Preview */
|
|
193
|
+
.cp_sample {
|
|
194
|
+
width: 2rem;
|
|
195
|
+
position: relative;
|
|
196
|
+
background: linear-gradient(var(--cp-color), var(--cp-color)), var(--cp-bg-checker);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Areas & Sliders */
|
|
200
|
+
.cp_thumb {
|
|
201
|
+
position: absolute;
|
|
202
|
+
transform: translate(-50%, -50%);
|
|
203
|
+
pointer-events: none;
|
|
204
|
+
|
|
205
|
+
width: 1.65rem; /* For Safari */
|
|
206
|
+
height: 1.65rem;
|
|
207
|
+
aspect-ratio: 1;
|
|
208
|
+
|
|
209
|
+
border: 2px solid var(--cp-body-color);
|
|
210
|
+
border-radius: 9999px;
|
|
211
|
+
|
|
212
|
+
box-shadow: var(--cp-box-shadow-sm);
|
|
213
|
+
z-index: 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.cp_area {
|
|
217
|
+
position: relative;
|
|
218
|
+
height: 8rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.cp_slider {
|
|
222
|
+
position: relative;
|
|
223
|
+
height: 1.25rem;
|
|
224
|
+
border-radius: 9999px;
|
|
225
|
+
}
|
|
226
|
+
.cp_slider .cp_thumb {
|
|
227
|
+
top: 50%;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.cp_area,
|
|
231
|
+
.cp_slider,
|
|
232
|
+
.cp_thumb {
|
|
233
|
+
cursor: grab;
|
|
234
|
+
overscroll-behavior: none;
|
|
235
|
+
touch-action: none;
|
|
236
|
+
}
|
|
237
|
+
.cp_area:active,
|
|
238
|
+
.cp_slider:active,
|
|
239
|
+
.cp_thumb:active {
|
|
240
|
+
cursor: grabbing;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* HSV Area */
|
|
244
|
+
.cp_area-hsv {
|
|
245
|
+
border-radius: var(--cp-border-radius-lg) var(--cp-border-radius-lg) 0px 0px;
|
|
246
|
+
background: linear-gradient(to top, rgba(0, 0, 0, var(--cp-alpha)), transparent),
|
|
247
|
+
linear-gradient(
|
|
248
|
+
to left,
|
|
249
|
+
hsla(var(--cp-hue), 100%, 50%, var(--cp-alpha)),
|
|
250
|
+
rgba(255, 255, 255, var(--cp-alpha))
|
|
251
|
+
),
|
|
252
|
+
var(--cp-bg-checker);
|
|
253
|
+
}
|
|
254
|
+
.cp_area-hsv .cp_thumb {
|
|
255
|
+
background-color: var(--cp-color);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* Hue Slider */
|
|
259
|
+
.cp_slider-hue {
|
|
260
|
+
background: linear-gradient(
|
|
261
|
+
to right,
|
|
262
|
+
hsl(0, 100%, 50%),
|
|
263
|
+
hsl(60, 100%, 50%),
|
|
264
|
+
hsl(120, 100%, 50%),
|
|
265
|
+
hsl(180, 100%, 50%),
|
|
266
|
+
hsl(240, 100%, 50%),
|
|
267
|
+
hsl(300, 100%, 50%),
|
|
268
|
+
hsl(360, 100%, 50%)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
.cp_slider-hue .cp_thumb {
|
|
272
|
+
background-color: hsl(var(--cp-hue), 100%, 50%);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* Alpha Slider */
|
|
276
|
+
.cp_slider-alpha {
|
|
277
|
+
background: linear-gradient(to right, transparent, var(--cp-base-color)), var(--cp-bg-checker);
|
|
278
|
+
}
|
|
279
|
+
.cp_slider-alpha .cp_thumb {
|
|
280
|
+
background-color: var(--cp-color);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* Form styles */
|
|
284
|
+
.color-picker:focus-visible,
|
|
285
|
+
.cp_dialog *:focus-visible {
|
|
286
|
+
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
287
|
+
border-color: var(--cp-body-bg);
|
|
288
|
+
box-shadow: 0 0 0 2px var(--cp-tertiary-color);
|
|
289
|
+
outline: 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Swatches */
|
|
293
|
+
.cp_swatches {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-wrap: wrap;
|
|
296
|
+
gap: 0.25rem;
|
|
297
|
+
justify-content: center;
|
|
298
|
+
}
|
|
299
|
+
.cp_swatches:empty {
|
|
300
|
+
display: none;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.cp_swatch {
|
|
304
|
+
--cp-color: transparent;
|
|
305
|
+
|
|
306
|
+
width: var(--cp-swatch-width);
|
|
307
|
+
aspect-ratio: 1;
|
|
308
|
+
padding: 0;
|
|
309
|
+
border: 1px solid rgba(0, 0, 0, 0.2) !important;
|
|
310
|
+
border-radius: var(--cp-border-radius-sm) !important;
|
|
311
|
+
overflow: hidden;
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
background: linear-gradient(var(--cp-color), var(--cp-color)), var(--cp-bg-checker);
|
|
314
|
+
transition: all 0.2s ease-in-out;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.cp_swatch:hover {
|
|
318
|
+
transform: scale(1.15);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* Formats */
|
|
322
|
+
.cp_formats {
|
|
323
|
+
display: flex;
|
|
324
|
+
gap: 0.25rem;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.cp_formats:empty {
|
|
328
|
+
display: none;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.cp_format {
|
|
332
|
+
flex: 1 1 auto;
|
|
333
|
+
border: none;
|
|
334
|
+
border-radius: 9999px !important;
|
|
335
|
+
padding: 0.125rem 0.25rem;
|
|
336
|
+
|
|
337
|
+
background: none;
|
|
338
|
+
color: var(--cp-body-color);
|
|
339
|
+
font-size: 1rem;
|
|
340
|
+
|
|
341
|
+
cursor: pointer;
|
|
342
|
+
transition: color 100ms ease-out, background-color 100ms ease-out;
|
|
343
|
+
}
|
|
344
|
+
.cp_format:hover {
|
|
345
|
+
background-color: var(--cp-border-color-translucent) !important;
|
|
346
|
+
}
|
|
347
|
+
.cp_format[aria-checked='true'] {
|
|
348
|
+
color: var(--cp-body-color);
|
|
349
|
+
background-color: var(--cp-button-color);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* Input */
|
|
353
|
+
.cp_input-group {
|
|
354
|
+
display: flex;
|
|
355
|
+
gap: 1px;
|
|
356
|
+
box-shadow: var(--cp-box-shadow-sm);
|
|
357
|
+
}
|
|
358
|
+
.cp_input-group > :first-child {
|
|
359
|
+
border-radius: var(--cp-border-radius-sm) 0 0 var(--cp-border-radius-sm);
|
|
360
|
+
}
|
|
361
|
+
.cp_input-group > :last-child {
|
|
362
|
+
border-radius: 0 var(--cp-border-radius-sm) var(--cp-border-radius-sm) 0;
|
|
363
|
+
}
|
|
364
|
+
.cp_input-group > :focus-visible {
|
|
365
|
+
z-index: 1;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.cp_value {
|
|
369
|
+
flex: 1 1 auto;
|
|
370
|
+
min-width: 0;
|
|
371
|
+
width: 0;
|
|
372
|
+
text-align: center;
|
|
373
|
+
|
|
374
|
+
border: none;
|
|
375
|
+
border-radius: 0;
|
|
376
|
+
padding: 0.25rem 0.375rem;
|
|
377
|
+
font-size: 0.9rem;
|
|
378
|
+
font-family: inherit;
|
|
379
|
+
|
|
380
|
+
color: var(--cp-body-color);
|
|
381
|
+
background-color: var(--cp-border-color-translucent);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* Action Buttons */
|
|
385
|
+
.cp_action {
|
|
386
|
+
border: none;
|
|
387
|
+
padding: 0.35rem 0.5rem;
|
|
388
|
+
|
|
389
|
+
color: var(--cp-body-color);
|
|
390
|
+
background-color: var(--cp-button-color);
|
|
391
|
+
|
|
392
|
+
cursor: pointer;
|
|
393
|
+
transition: background-color 100ms ease-out;
|
|
394
|
+
|
|
395
|
+
display: flex;
|
|
396
|
+
place-items: center;
|
|
397
|
+
}
|
|
398
|
+
.cp_action:hover {
|
|
399
|
+
background-color: var(--cp-border-color-translucent);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/* Icons */
|
|
403
|
+
.cp_icon {
|
|
404
|
+
font-size: 1.15rem;
|
|
405
|
+
/* Use
|
|
406
|
+
color: var(--cp-primary);
|
|
407
|
+
to color buttons to picked color
|
|
408
|
+
*/
|
|
409
|
+
}
|