keystone_ui-preferences 0.7.0 → 0.9.0

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.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -27
  3. data/app/controllers/concerns/keystone_ui/preferences/component_preferences.rb +2 -2
  4. data/app/javascript/keystone_ui/preferences/appearance_editor_controller.js +235 -0
  5. data/app/models/keystone_ui/preferences/appearance_choice.rb +66 -1
  6. data/app/models/keystone_ui/preferences/checked_theme.rb +109 -0
  7. data/app/models/keystone_ui/preferences/chosen_colours.rb +7 -2
  8. data/app/models/keystone_ui/preferences/pick_account_appearance.rb +12 -2
  9. data/app/models/keystone_ui/preferences/pick_appearance.rb +13 -1
  10. data/app/models/keystone_ui/preferences/sent_theme.rb +49 -0
  11. data/app/models/keystone_ui/preferences/theme_css.rb +89 -0
  12. data/app/models/keystone_ui/preferences/theme_reader.rb +34 -0
  13. data/app/models/keystone_ui/preferences/theme_roles.rb +89 -0
  14. data/app/views/keystone_ui/preferences/settings/_account_appearance.html.erb +15 -3
  15. data/app/views/keystone_ui/preferences/settings/_appearance.html.erb +30 -18
  16. data/app/views/keystone_ui/preferences/settings/_colour_choices.html.erb +40 -8
  17. data/app/views/keystone_ui/preferences/settings/_editor_layout.html.erb +29 -0
  18. data/app/views/keystone_ui/preferences/settings/_theme_preview.html.erb +34 -0
  19. data/app/views/keystone_ui/preferences/settings/_theme_settings.html.erb +52 -0
  20. data/lib/generators/keystone_ui/preferences/install/install_generator.rb +4 -4
  21. data/lib/generators/keystone_ui/preferences/update/update_generator.rb +4 -4
  22. data/lib/keystone_ui/preferences/colors_check.rb +3 -2
  23. data/lib/keystone_ui/preferences/configuration.rb +2 -1
  24. data/lib/keystone_ui/preferences/version.rb +1 -1
  25. metadata +28 -7
  26. data/app/javascript/keystone_ui/preferences/theme_settings_controller.js +0 -59
  27. data/app/models/keystone_ui/preferences/palette_css.rb +0 -32
@@ -0,0 +1,29 @@
1
+ <div data-appearance-editor data-controller="keystone-ui--preferences--appearance-editor" data-action="tab-switcher:change->keystone-ui--preferences--appearance-editor#showPanel submit->keystone-ui--preferences--appearance-editor#save turbo:before-visit@document->keystone-ui--preferences--appearance-editor#confirmLeave beforeunload@window->keystone-ui--preferences--appearance-editor#warnBeforeUnload">
2
+ <% if previews %>
3
+ <style>
4
+ [data-editor-panel]:not([data-shown]) { display: none; }
5
+ @media (min-width: 64rem) {
6
+ [data-appearance-editor] .ks-tab-bar { display: none; }
7
+ [data-editor-panels] { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 1.5rem; align-items: start; }
8
+ [data-editor-panel]:not([data-shown]) { display: block; }
9
+ }
10
+ </style>
11
+ <%= ui_tab_switcher(tabs: %w[Edit Preview]) do %>
12
+ <div data-editor-panels>
13
+ <div data-editor-panel data-shown><%= editor %></div>
14
+ <div data-editor-panel><%= render partial: "keystone_ui/preferences/settings/theme_preview" %></div>
15
+ </div>
16
+ <% end %>
17
+ <% else %>
18
+ <%= editor %>
19
+ <% end %>
20
+ <div data-leave-question>
21
+ <%= ui_modal(title: "Leave without saving?", size: :sm) do %>
22
+ <p>Your changes are not saved yet.</p>
23
+ <div style="display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem;">
24
+ <%= ui_button(label: "Leave without saving", variant: :danger, type: :button, data: { action: "keystone-ui--preferences--appearance-editor#leave" }) %>
25
+ <%= ui_button(label: "Stay", variant: :secondary, type: :button, data: { action: "modal#close" }) %>
26
+ </div>
27
+ <% end %>
28
+ </div>
29
+ </div>
@@ -0,0 +1,34 @@
1
+ <div data-theme-preview style="display: flex; flex-direction: column; gap: 1.5rem;">
2
+ <% %w[light dark].each do |side| %>
3
+ <div data-theme="<%= side %>">
4
+ <%= ui_panel do %>
5
+ <div style="display: flex; flex-direction: column; gap: 1.5rem;">
6
+ <%= ui_card(title: "Quarterly review", summary: "Revenue grew in every region this quarter.", link: "#", cta: "Open") %>
7
+ <div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
8
+ <%= ui_button(label: "Save", type: :button) %>
9
+ <%= ui_button(label: "Cancel", variant: :secondary, type: :button) %>
10
+ <%= ui_button(label: "Delete", variant: :danger, type: :button) %>
11
+ </div>
12
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
13
+ <%= ui_form_field(attribute: "preview_name", label: "Name", value: "Northwind Traders") %>
14
+ <%= ui_form_field(attribute: "preview_region", label: "Region", type: :select, options: [ [ "North", "north" ], [ "South", "south" ] ], value: "north", required: true) %>
15
+ </div>
16
+ <%= ui_data_table(
17
+ items: [ { customer: "Northwind Traders", region: "North", total: "$12,400" }, { customer: "Harbor Supply", region: "South", total: "$8,950" }, { customer: "Fern & Co", region: "North", total: "$4,120" } ],
18
+ columns: [ { customer: "Customer" }, { region: "Region" }, { total: "Total" } ]
19
+ ) %>
20
+ <%= ui_line_chart(
21
+ labels: %w[Jan Feb Mar Apr May Jun],
22
+ series: [ { name: "This year", data: [ 12, 19, 15, 24, 22, 30 ] }, { name: "Last year", data: [ 10, 14, 13, 17, 19, 21 ] } ],
23
+ height: :sm
24
+ ) %>
25
+ <nav class="ks-sidebar" aria-label="Sample navigation">
26
+ <% [ "Dashboard", "Customers", "Reports" ].each_with_index do |label, index| %>
27
+ <a href="#" class="<%= [ Keystone::Ui::NavigationComponent::SIDEBAR_TAB_CLASSES, (Keystone::Ui::NavigationComponent::ACTIVE_CLASS if index.zero?) ].compact.join(" ") %>"><%= label %></a>
28
+ <% end %>
29
+ </nav>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ <% end %>
34
+ </div>
@@ -0,0 +1,52 @@
1
+ <% saved_preset = KeystoneUi::Looks::Themes.all.find { |name, _preset| name.to_s == saved["preset_name"] }&.last %>
2
+ <% theme = saved_preset ? saved_preset[:theme] : saved["theme"].to_h %>
3
+ <% shown = local_assigns[:sent_theme] || theme %>
4
+ <% check = KeystoneUi::Preferences::CheckedTheme.new(local_assigns[:sent_paste].present? ? {} : local_assigns[:sent_theme].to_h) %>
5
+ <% id_prefix = local_assigns.fetch(:id_prefix, "appearance") %>
6
+ <% fonts_id = "#{id_prefix}-theme-fonts" %>
7
+ <% roles = KeystoneUi::Preferences::ThemeRoles::ROLES.to_h { |name, role| [ name, role.merge(hover: KeystoneUi::Preferences::ThemeCss::HOVERS[name]).compact ] } %>
8
+ <div data-action="change->keystone-ui--preferences--appearance-editor#apply input->keystone-ui--preferences--appearance-editor#apply" data-theme-roles="<%= roles.to_json %>" data-saved-theme="<%= theme.to_json %>" style="display: flex; flex-direction: column; gap: 2rem;">
9
+ <% KeystoneUi::Preferences::ThemeRoles::GROUPS.each do |group, sets| %>
10
+ <%= ui_section(title: group, spacing: :sm) do %>
11
+ <% sets.each do |set, names| %>
12
+ <fieldset style="border: 0; padding: 0; margin: 0; min-width: 0;">
13
+ <legend class="<%= Keystone::Ui::FormFieldComponent::LABEL_CLASSES %>"><%= set %></legend>
14
+ <div style="display: flex; flex-wrap: wrap; gap: 1rem;">
15
+ <% names.each do |name| %>
16
+ <% role = KeystoneUi::Preferences::ThemeRoles::ROLES.fetch(name) %>
17
+ <% if role[:kind] == :colour %><div role="group" aria-label="--<%= name %>" style="display: flex; flex-wrap: wrap; gap: 1rem;"><% end %>
18
+ <% (role[:kind] == :colour ? %w[light dark] : %w[light]).each do |side| %>
19
+ <% field_name = "theme[#{name}][#{side}]" %>
20
+ <% value = shown.dig(name, side).to_s %>
21
+ <% label = role[:kind] == :colour ? "--#{name}, #{side}" : "--#{name}" %>
22
+ <% if %i[colour shadow_colour].include?(role[:kind]) && (value.empty? || value.match?(KeystoneUi::Preferences::CheckedTheme::COLOUR)) %>
23
+ <%= ui_color_picker(name: field_name, value: value, label: label) %>
24
+ <% else %>
25
+ <% field_id = "#{id_prefix}-#{field_name.parameterize}" %>
26
+ <div class="<%= Keystone::Ui::FormFieldComponent::WRAPPER_CLASSES %>">
27
+ <%= label_tag field_id, label, class: Keystone::Ui::FormFieldComponent::LABEL_CLASSES %>
28
+ <%= text_field_tag field_name, value, id: field_id, class: Keystone::Ui::InputComponent::BASE_CLASSES, list: (fonts_id if role[:kind] == :font_list) %>
29
+ </div>
30
+ <% end %>
31
+ <% end %>
32
+ <% if name == check.failing_setting %>
33
+ <p class="<%= Keystone::Ui::FormFieldComponent::ERROR_CLASSES %>"><%= check.refusal.message %></p>
34
+ <% end %>
35
+ <% if role[:kind] == :colour %></div><% end %>
36
+ <% end %>
37
+ </div>
38
+ </fieldset>
39
+ <% end %>
40
+ <% end %>
41
+ <% end %>
42
+ <div class="<%= Keystone::Ui::FormFieldComponent::WRAPPER_CLASSES %>">
43
+ <%= label_tag "#{id_prefix}-theme-export", "Export", class: Keystone::Ui::FormFieldComponent::LABEL_CLASSES %>
44
+ <%= content_tag :textarea, nil, id: "#{id_prefix}-theme-export", readonly: true, rows: 8, class: Keystone::Ui::TextareaComponent::BASE_CLASSES, data: { theme_export: "" } %>
45
+ <div><%= ui_copy_button(text: "", error_message: "Select the text to copy it") %></div>
46
+ </div>
47
+ <datalist id="<%= fonts_id %>">
48
+ <% (KeystoneUi::Preferences.configuration.theme_fonts + KeystoneUi::Preferences::CheckedTheme::SYSTEM_FONTS).each do |font| %>
49
+ <option value="<%= font %>"></option>
50
+ <% end %>
51
+ </datalist>
52
+ </div>
@@ -11,7 +11,7 @@ module KeystoneUi
11
11
 
12
12
  source_root File.expand_path("templates", __dir__)
13
13
 
14
- desc "Installs KeystoneUi::Preferences: copies its migration and its template picker script."
14
+ desc "Installs KeystoneUi::Preferences: copies its migration and its appearance editor script."
15
15
 
16
16
  def copy_migration
17
17
  migration_template(
@@ -20,9 +20,9 @@ module KeystoneUi
20
20
  )
21
21
  end
22
22
 
23
- def copy_template_picker_script
24
- script = File.expand_path("../../../../../app/javascript/keystone_ui/preferences/theme_settings_controller.js", __dir__)
25
- create_file "app/javascript/controllers/keystone_ui/preferences/theme_settings_controller.js", File.read(script)
23
+ def copy_appearance_editor_script
24
+ script = File.expand_path("../../../../../app/javascript/keystone_ui/preferences/appearance_editor_controller.js", __dir__)
25
+ create_file "app/javascript/controllers/keystone_ui/preferences/appearance_editor_controller.js", File.read(script)
26
26
  end
27
27
  end
28
28
  end
@@ -11,7 +11,7 @@ module KeystoneUi
11
11
 
12
12
  source_root File.expand_path("templates", __dir__)
13
13
 
14
- desc "Updates KeystoneUi::Preferences: copies migrations added since it was installed and the template picker script when the app has none."
14
+ desc "Updates KeystoneUi::Preferences: copies migrations added since it was installed and the appearance editor script when the app has none."
15
15
 
16
16
  def copy_remove_extra_owner_index_migration
17
17
  migration_template(
@@ -27,11 +27,11 @@ module KeystoneUi
27
27
  )
28
28
  end
29
29
 
30
- def copy_template_picker_script
31
- copy = "app/javascript/controllers/keystone_ui/preferences/theme_settings_controller.js"
30
+ def copy_appearance_editor_script
31
+ copy = "app/javascript/controllers/keystone_ui/preferences/appearance_editor_controller.js"
32
32
  return if File.exist?(File.join(destination_root, copy))
33
33
 
34
- create_file copy, File.read(File.expand_path("../../../../../app/javascript/keystone_ui/preferences/theme_settings_controller.js", __dir__))
34
+ create_file copy, File.read(File.expand_path("../../../../../app/javascript/keystone_ui/preferences/appearance_editor_controller.js", __dir__))
35
35
  end
36
36
  end
37
37
  end
@@ -6,8 +6,9 @@ module KeystoneUi
6
6
  class Error < StandardError; end
7
7
 
8
8
  MESSAGE = "keystone_ui-preferences and keystone_ui-colors both save colours, modes and looks, so the app cannot load both. " \
9
- "Run bin/rails generate keystone_ui:preferences:update first, so the migration copying keystone_ui-colors' saved choices is in db/migrate, " \
10
- "then remove keystone_ui-colors from the Gemfile and run bin/rails db:migrate."
9
+ "Remove keystone_ui-colors from the Gemfile and change every KeystoneUi::Colors name the app uses first, " \
10
+ "then run bin/rails generate keystone_ui:preferences:update, so the migration copying keystone_ui-colors' saved choices is in db/migrate, " \
11
+ "and then run bin/rails db:migrate."
11
12
 
12
13
  def initialize(colors_loaded:)
13
14
  @colors_loaded = colors_loaded
@@ -3,7 +3,7 @@
3
3
  module KeystoneUi
4
4
  module Preferences
5
5
  class Configuration
6
- attr_accessor :current_owner_method, :authentication_method, :current_account_method, :max_value_bytes, :default_accent, :default_surface, :default_background, :default_text, :default_mode, :user_looks, :app_look_label, :account_colors, :account_looks
6
+ attr_accessor :current_owner_method, :authentication_method, :current_account_method, :max_value_bytes, :default_accent, :default_surface, :default_background, :default_text, :default_mode, :user_looks, :app_look_label, :account_colors, :account_looks, :theme_fonts
7
7
 
8
8
  def initialize
9
9
  @current_owner_method = :current_user
@@ -19,6 +19,7 @@ module KeystoneUi
19
19
  @app_look_label = "Classic"
20
20
  @account_colors = true
21
21
  @account_looks = true
22
+ @theme_fonts = []
22
23
  end
23
24
 
24
25
  def name_component(component_key, name)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module KeystoneUi
4
4
  module Preferences
5
- VERSION = "0.7.0"
5
+ VERSION = "0.9.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: keystone_ui-preferences
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Schneider
@@ -15,28 +15,42 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 0.41.0
18
+ version: 0.47.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 0.41.0
25
+ version: 0.47.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: keystone_ui-looks
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.3.0
32
+ version: 0.4.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.3.0
39
+ version: 0.4.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: keystone_ui-styles
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.23.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.23.0
40
54
  - !ruby/object:Gem::Dependency
41
55
  name: railties
42
56
  requirement: !ruby/object:Gem::Requirement
@@ -93,28 +107,35 @@ files:
93
107
  - app/controllers/concerns/keystone_ui/preferences/component_preferences.rb
94
108
  - app/controllers/keystone_ui/preferences/application_controller.rb
95
109
  - app/controllers/keystone_ui/preferences/component_preferences_controller.rb
96
- - app/javascript/keystone_ui/preferences/theme_settings_controller.js
110
+ - app/javascript/keystone_ui/preferences/appearance_editor_controller.js
97
111
  - app/models/keystone_ui/preferences/appearance_choice.rb
98
112
  - app/models/keystone_ui/preferences/application_record.rb
113
+ - app/models/keystone_ui/preferences/checked_theme.rb
99
114
  - app/models/keystone_ui/preferences/chosen_colours.rb
100
115
  - app/models/keystone_ui/preferences/component_preference.rb
101
116
  - app/models/keystone_ui/preferences/kept.rb
102
117
  - app/models/keystone_ui/preferences/layout_choice.rb
103
118
  - app/models/keystone_ui/preferences/navigation_arrangement.rb
104
- - app/models/keystone_ui/preferences/palette_css.rb
105
119
  - app/models/keystone_ui/preferences/pick_account_appearance.rb
106
120
  - app/models/keystone_ui/preferences/pick_account_layouts.rb
107
121
  - app/models/keystone_ui/preferences/pick_appearance.rb
108
122
  - app/models/keystone_ui/preferences/pick_navigation_order.rb
109
123
  - app/models/keystone_ui/preferences/pick_navigation_placement.rb
110
124
  - app/models/keystone_ui/preferences/refusal.rb
125
+ - app/models/keystone_ui/preferences/sent_theme.rb
126
+ - app/models/keystone_ui/preferences/theme_css.rb
127
+ - app/models/keystone_ui/preferences/theme_reader.rb
128
+ - app/models/keystone_ui/preferences/theme_roles.rb
111
129
  - app/views/keystone_ui/preferences/settings/_account_appearance.html.erb
112
130
  - app/views/keystone_ui/preferences/settings/_account_layouts.html.erb
113
131
  - app/views/keystone_ui/preferences/settings/_appearance.html.erb
114
132
  - app/views/keystone_ui/preferences/settings/_colour_choices.html.erb
133
+ - app/views/keystone_ui/preferences/settings/_editor_layout.html.erb
115
134
  - app/views/keystone_ui/preferences/settings/_look_choices.html.erb
116
135
  - app/views/keystone_ui/preferences/settings/_navigation_order.html.erb
117
136
  - app/views/keystone_ui/preferences/settings/_navigation_placement.html.erb
137
+ - app/views/keystone_ui/preferences/settings/_theme_preview.html.erb
138
+ - app/views/keystone_ui/preferences/settings/_theme_settings.html.erb
118
139
  - config/routes.rb
119
140
  - lib/generators/keystone_ui/preferences/install/install_generator.rb
120
141
  - lib/generators/keystone_ui/preferences/install/templates/create_keystone_ui_preferences_component_preferences.rb.erb
@@ -1,59 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus"
2
-
3
- export default class extends Controller {
4
- static targets = ["accentPicker", "surfacePicker", "textPicker", "customRadio", "templateLabel"]
5
-
6
- connect() {
7
- this.colorPickerTargets().forEach(picker => picker.addEventListener("change", () => this.customColorChanged()))
8
- }
9
-
10
- colorPickerTargets() {
11
- return [
12
- this.hasAccentPickerTarget && this.accentPickerTarget,
13
- this.hasSurfacePickerTarget && this.surfacePickerTarget,
14
- this.hasTextPickerTarget && this.textPickerTarget
15
- ].filter(Boolean)
16
- }
17
-
18
- selectTemplate(event) {
19
- const input = event.currentTarget
20
- const label = input.closest("[data-template]")
21
- const accentHex = input.dataset.accentHex
22
- const surfaceHex = input.dataset.surfaceHex
23
- const textHex = input.dataset.textHex
24
-
25
- this.setPickerValue(this.accentPickerTarget, accentHex)
26
- this.setPickerValue(this.surfacePickerTarget, surfaceHex)
27
- this.setPickerValue(this.textPickerTarget, textHex)
28
-
29
- this.highlightTemplate(label.dataset.template)
30
- }
31
-
32
- customColorChanged() {
33
- if (this.hasCustomRadioTarget) this.customRadioTarget.checked = true
34
- this.highlightTemplate("custom")
35
- }
36
-
37
- highlightTemplate(selected) {
38
- this.templateLabelTargets.forEach(label => {
39
- const border = label.querySelector("span")
40
- if (label.dataset.template === selected) {
41
- border.classList.remove("border-transparent")
42
- border.classList.add("border-accent-500")
43
- } else {
44
- border.classList.remove("border-accent-500")
45
- border.classList.add("border-transparent")
46
- }
47
- })
48
- }
49
-
50
- setPickerValue(wrapper, hex) {
51
- const input = wrapper.querySelector("input[type='hidden']")
52
- const swatch = wrapper.querySelector("[data-color-picker-target='swatch']")
53
- const hexLabel = wrapper.querySelector("[data-color-picker-target='hexLabel']")
54
-
55
- if (input) input.value = hex
56
- if (swatch) swatch.style.backgroundColor = hex
57
- if (hexLabel) hexLabel.textContent = hex
58
- }
59
- }
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module KeystoneUi
4
- module Preferences
5
- class PaletteCss
6
- def initialize(accent:, surface:, background:, text:)
7
- @accent = accent
8
- @surface = surface
9
- @background = background
10
- @text = text
11
- end
12
-
13
- def to_s
14
- lines = shade_lines("accent", shades(@accent) { KeystoneUi::Looks::Palettes.accent(@accent) }) + shade_lines("surface", shades(@surface) { KeystoneUi::Looks::Palettes.surface(@surface) })
15
- lines << " --color-custom-background: #{@background};" << " --color-custom-text: #{@text};"
16
- ":root {\n#{lines.join("\n")}\n}"
17
- end
18
-
19
- private
20
-
21
- def shades(colour)
22
- return KeystoneUi::Looks::Palettes.generate_shades(colour) if colour.start_with?("#")
23
-
24
- yield
25
- end
26
-
27
- def shade_lines(name, shades)
28
- shades.map { |shade, hex| " --color-#{name}-#{shade}: #{hex};" }
29
- end
30
- end
31
- end
32
- end