avo 4.0.0.beta.2 → 4.0.0.beta.4

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/assets/builds/avo/application.css +355 -51
  4. data/app/assets/builds/avo/application.js +166 -166
  5. data/app/assets/builds/avo/application.js.map +4 -4
  6. data/app/assets/stylesheets/application.css +2 -0
  7. data/app/assets/stylesheets/css/components/hotkey.css +50 -0
  8. data/app/assets/stylesheets/css/components/input.css +0 -6
  9. data/app/assets/stylesheets/css/components/ui/state.css +129 -0
  10. data/app/assets/stylesheets/css/layout.css +3 -4
  11. data/app/assets/stylesheets/css/pagination.css +12 -6
  12. data/app/assets/stylesheets/css/typography.css +18 -1
  13. data/app/assets/svgs/avo/circle-minus.svg +3 -0
  14. data/app/components/avo/alert_component.rb +4 -4
  15. data/app/components/avo/backtrace_alert_component.html.erb +1 -1
  16. data/app/components/avo/base_component.rb +9 -0
  17. data/app/components/avo/button_component.rb +2 -1
  18. data/app/components/avo/debug/status_component.html.erb +2 -2
  19. data/app/components/avo/empty_state_component.html.erb +15 -4
  20. data/app/components/avo/empty_state_component.rb +9 -0
  21. data/app/components/avo/fields/common/files/view_type/grid_item_component.html.erb +1 -1
  22. data/app/components/avo/fields/common/key_value_component.html.erb +1 -1
  23. data/app/components/avo/fields/common/stars_component.html.erb +1 -1
  24. data/app/components/avo/fields/common/status_viewer_component.html.erb +3 -3
  25. data/app/components/avo/fields/preview_field/index_component.rb +1 -1
  26. data/app/components/avo/fields/stars_field/edit_component.html.erb +1 -1
  27. data/app/components/avo/filters_component.html.erb +1 -1
  28. data/app/components/avo/items/switcher_component.html.erb +1 -1
  29. data/app/components/avo/keyboard_shortcuts_component.html.erb +29 -0
  30. data/app/components/avo/keyboard_shortcuts_component.rb +127 -0
  31. data/app/components/avo/media_library/item_details_component.html.erb +2 -2
  32. data/app/components/avo/media_library/list_component.html.erb +1 -1
  33. data/app/components/avo/media_library/list_item_component.html.erb +2 -2
  34. data/app/components/avo/modal_component.html.erb +39 -15
  35. data/app/components/avo/modal_component.rb +10 -0
  36. data/app/components/avo/paginator_component.html.erb +23 -17
  37. data/app/components/avo/paginator_component.rb +18 -0
  38. data/app/components/avo/resource_component.rb +14 -7
  39. data/app/components/avo/sidebar/group_component.html.erb +1 -1
  40. data/app/components/avo/sidebar/link_component.html.erb +13 -5
  41. data/app/components/avo/sidebar/link_component.rb +17 -0
  42. data/app/components/avo/sidebar/section_component.html.erb +1 -1
  43. data/app/components/avo/sidebar_component.html.erb +2 -2
  44. data/app/components/avo/sidebar_profile_component.html.erb +1 -1
  45. data/app/components/avo/u_i/search_input_component.html.erb +2 -2
  46. data/app/components/avo/views/resource_index_component.rb +1 -1
  47. data/app/javascript/application.js +12 -28
  48. data/app/javascript/js/controllers/base_modal_controller.js +65 -0
  49. data/app/javascript/js/controllers/confirm_dialog_controller.js +18 -0
  50. data/app/javascript/js/controllers/modal_controller.js +18 -26
  51. data/app/javascript/js/controllers/persistent_modal_controller.js +50 -0
  52. data/app/javascript/js/controllers/search_controller.js +0 -4
  53. data/app/javascript/js/controllers/sidebar_controller.js +22 -0
  54. data/app/javascript/js/controllers.js +4 -0
  55. data/app/javascript/js/global_hotkeys.js +77 -0
  56. data/app/javascript/js/helpers/toggle_hidden.js +7 -0
  57. data/app/views/avo/actions/show.html.erb +1 -1
  58. data/app/views/avo/base/_date_time_filter.html.erb +1 -1
  59. data/app/views/avo/base/preview.html.erb +1 -1
  60. data/app/views/avo/debug/_valid_indicator.html.erb +2 -2
  61. data/app/views/avo/home/failed_to_load.html.erb +40 -13
  62. data/app/views/avo/media_library/_form.html.erb +1 -1
  63. data/app/views/avo/partials/_color_scheme_switcher.html.erb +4 -4
  64. data/app/views/avo/partials/_confirm_dialog.html.erb +3 -3
  65. data/app/views/avo/partials/_custom_tools_alert.html.erb +3 -3
  66. data/app/views/avo/partials/_sortable_component.html.erb +3 -3
  67. data/app/views/avo/partials/_table_header.html.erb +1 -1
  68. data/app/views/avo/private/_links_and_buttons.html.erb +2 -2
  69. data/app/views/avo/private/design.html.erb +4 -4
  70. data/app/views/avo/sidebar/_license_warning.html.erb +2 -2
  71. data/app/views/layouts/avo/application.html.erb +1 -0
  72. data/lib/avo/resources/base.rb +1 -0
  73. data/lib/avo/version.rb +1 -1
  74. data/lib/generators/avo/resource_generator.rb +3 -3
  75. data/lib/generators/avo/templates/initializer/avo.tt +4 -4
  76. data/lib/generators/avo/templates/resource_tools/partial.tt +1 -1
  77. metadata +11 -15
  78. data/app/assets/svgs/avo/arrow-circle-right.svg +0 -1
  79. data/app/assets/svgs/avo/bell.svg +0 -3
  80. data/app/assets/svgs/avo/color-swatch.svg +0 -1
  81. data/app/assets/svgs/avo/dashboards.svg +0 -6
  82. data/app/assets/svgs/avo/exclamation.svg +0 -1
  83. data/app/assets/svgs/avo/filter.svg +0 -1
  84. data/app/assets/svgs/avo/logout.svg +0 -3
  85. data/app/assets/svgs/avo/resources.svg +0 -13
  86. data/app/assets/svgs/avo/save.svg +0 -8
  87. data/app/assets/svgs/avo/selector.svg +0 -1
  88. data/app/assets/svgs/avo/sort-ascending.svg +0 -1
  89. data/app/assets/svgs/avo/sort-descending.svg +0 -1
  90. data/app/assets/svgs/avo/times.svg +0 -3
  91. data/app/assets/svgs/avo/tools.svg +0 -3
@@ -0,0 +1,65 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ /**
4
+ * Shared behaviour for both modal strategies (destroy & toggle).
5
+ * Not registered with Stimulus directly — subclasses are.
6
+ */
7
+ export default class extends Controller {
8
+ static targets = ['modal', 'backdrop']
9
+
10
+ static values = {
11
+ closeModalOnBackdropClick: { type: Boolean, default: true },
12
+ }
13
+
14
+ // -- lifecycle ------------------------------------------------------------
15
+
16
+ connectModal() {
17
+ this.handleKeydown = this.handleKeydown.bind(this)
18
+ document.addEventListener('keydown', this.handleKeydown)
19
+ }
20
+
21
+ disconnectModal() {
22
+ document.removeEventListener('keydown', this.handleKeydown)
23
+ }
24
+
25
+ // -- shared actions -------------------------------------------------------
26
+
27
+ handleKeydown(event) {
28
+ if (event.key === 'Escape' && this.isOpen() && this.closeModalOnBackdropClickValue) {
29
+ this.closeModal()
30
+ }
31
+ }
32
+
33
+ /** Backdrop click action — wired via data-action="click->…#close" */
34
+ close(event) {
35
+ if (event.target === this.backdropTarget) return
36
+
37
+ this.closeModal()
38
+ }
39
+
40
+ // -- helpers --------------------------------------------------------------
41
+
42
+ addModalOpen() {
43
+ document.body.classList.add('modal-open')
44
+ }
45
+
46
+ removeModalOpen() {
47
+ document.body.classList.remove('modal-open')
48
+ }
49
+
50
+ dispatchClose() {
51
+ document.dispatchEvent(new Event('modal-controller:close'))
52
+ }
53
+
54
+ // -- subclass contract (override in each strategy) ------------------------
55
+
56
+ /** @abstract */
57
+ closeModal() {
58
+ throw new Error('Subclass must implement closeModal()')
59
+ }
60
+
61
+ /** @abstract */
62
+ isOpen() {
63
+ throw new Error('Subclass must implement isOpen()')
64
+ }
65
+ }
@@ -0,0 +1,18 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['option']
5
+
6
+ navigate(event) {
7
+ if (!['ArrowDown', 'ArrowUp'].includes(event.key)) return
8
+ event.preventDefault()
9
+
10
+ const options = this.optionTargets
11
+ const index = options.indexOf(document.activeElement)
12
+ const next = event.key === 'ArrowDown'
13
+ ? (index + 1) % options.length
14
+ : (index - 1 + options.length) % options.length
15
+
16
+ options[next]?.focus()
17
+ }
18
+ }
@@ -1,39 +1,31 @@
1
- import { Controller } from '@hotwired/stimulus'
2
-
3
- // Connects to data-controller="modal"
4
- export default class extends Controller {
5
- static targets = ['modal', 'backdrop']
6
-
7
- static values = {
8
- closeModalOnBackdropClick: true,
9
- }
10
-
1
+ import BaseModalController from './base_modal_controller'
2
+
3
+ /**
4
+ * Inject / destroy strategy.
5
+ *
6
+ * The modal is added to the DOM (usually via Turbo) when it should appear
7
+ * and removed entirely when it is closed.
8
+ */
9
+ export default class extends BaseModalController {
11
10
  connect() {
12
- document.body.classList.add('modal-open')
13
- this.handleKeydown = this.handleKeydown.bind(this)
14
- document.addEventListener('keydown', this.handleKeydown)
11
+ this.connectModal()
12
+ this.addModalOpen()
13
+ this.modalTarget.focus()
15
14
  }
16
15
 
17
16
  disconnect() {
18
- document.removeEventListener('keydown', this.handleKeydown)
17
+ this.disconnectModal()
19
18
  }
20
19
 
21
- handleKeydown(event) {
22
- if (event.key === 'Escape' && this.closeModalOnBackdropClickValue) {
23
- this.closeModal()
24
- }
25
- }
26
-
27
- close(event) {
28
- if (event.target === this.backdropTarget && !this.closeModalOnBackdropClickValue) return
20
+ // -- strategy implementation ----------------------------------------------
29
21
 
30
- this.closeModal()
22
+ isOpen() {
23
+ return true // if the element is in the DOM it's open
31
24
  }
32
25
 
33
- // May be invoked by the other controllers
34
26
  closeModal() {
35
27
  this.modalTarget.remove()
36
-
37
- document.dispatchEvent(new Event('modal-controller:close'))
28
+ this.removeModalOpen()
29
+ this.dispatchClose()
38
30
  }
39
31
  }
@@ -0,0 +1,50 @@
1
+ import BaseModalController from './base_modal_controller'
2
+
3
+ /**
4
+ * Persistent / show-hide strategy.
5
+ *
6
+ * The modal lives in the DOM at all times. It starts with the `hidden`
7
+ * attribute and is revealed / hidden by toggling that attribute.
8
+ */
9
+ export default class extends BaseModalController {
10
+ connect() {
11
+ this.connectModal()
12
+
13
+ this.handleOpen = this.openModal.bind(this)
14
+ this.handleToggle = this.toggleModal.bind(this)
15
+ document.addEventListener('persistent-modal:open', this.handleOpen)
16
+ document.addEventListener('persistent-modal:toggle', this.handleToggle)
17
+ }
18
+
19
+ disconnect() {
20
+ this.disconnectModal()
21
+ document.removeEventListener('persistent-modal:open', this.handleOpen)
22
+ document.removeEventListener('persistent-modal:toggle', this.handleToggle)
23
+ }
24
+
25
+ // -- strategy implementation ----------------------------------------------
26
+
27
+ isOpen() {
28
+ return !this.modalTarget.hasAttribute('hidden')
29
+ }
30
+
31
+ closeModal() {
32
+ this.modalTarget.setAttribute('hidden', '')
33
+ this.removeModalOpen()
34
+ this.dispatchClose()
35
+ }
36
+
37
+ openModal() {
38
+ this.modalTarget.removeAttribute('hidden')
39
+ this.addModalOpen()
40
+ this.modalTarget.focus()
41
+ }
42
+
43
+ toggleModal() {
44
+ if (this.isOpen()) {
45
+ this.closeModal()
46
+ } else {
47
+ this.openModal()
48
+ }
49
+ }
50
+ }
@@ -117,10 +117,6 @@ export default class extends Controller {
117
117
  }
118
118
 
119
119
  disconnect() {
120
- if (this.isGlobalSearch) {
121
- Mousetrap.unbind(['command+k', 'ctrl+k'])
122
- }
123
-
124
120
  // Don't leave open autocompletes around when disconnected. Otherwise it will still
125
121
  // be visible when navigating back to this page.
126
122
  if (this.destroyMethod) {
@@ -69,6 +69,20 @@ export default class extends Controller {
69
69
  ? this.mainAreaTarget.classList.contains('sidebar-mobile-open')
70
70
  : this.mainAreaTarget.classList.contains('sidebar-open')
71
71
  this.setToggleButtonsState(isOpen ? 'open' : 'closed')
72
+
73
+ this.handleToggleShortcut = (event) => {
74
+ if (event.repeat || event.defaultPrevented) return
75
+ if (event.target?.closest('input, textarea, select, [contenteditable]')) return
76
+ if (!(event.metaKey || event.ctrlKey) || event.altKey || event.key !== '\\') return
77
+
78
+ event.preventDefault()
79
+ this.toggleSidebarForViewport()
80
+ }
81
+ document.addEventListener('keydown', this.handleToggleShortcut)
82
+ }
83
+
84
+ disconnect() {
85
+ document.removeEventListener('keydown', this.handleToggleShortcut)
72
86
  }
73
87
 
74
88
  rememberScrollPosition() {
@@ -96,6 +110,14 @@ export default class extends Controller {
96
110
  })
97
111
  }
98
112
 
113
+ toggleSidebarForViewport() {
114
+ if (window.innerWidth < 1024) {
115
+ this.toggleSidebarOnMobile()
116
+ } else {
117
+ this.toggleSidebar()
118
+ }
119
+ }
120
+
99
121
  toggleSidebar() {
100
122
  if (this.sidebarTarget.classList.contains('hidden')) {
101
123
  this.sidebarTarget.classList.remove('hidden')
@@ -10,6 +10,7 @@ import CardFiltersController from './controllers/card_filters_controller'
10
10
  import ClearInputController from './controllers/fields/clear_input_controller'
11
11
  import CodeFieldController from './controllers/fields/code_field_controller'
12
12
  import ColorSchemeSwitcherController from './controllers/color_scheme_switcher_controller'
13
+ import ConfirmDialogController from './controllers/confirm_dialog_controller'
13
14
  import CopyToClipboardController from './controllers/copy_to_clipboard_controller'
14
15
  import DashboardCardController from './controllers/dashboard_card_controller'
15
16
  import DateFieldController from './controllers/fields/date_field_controller'
@@ -35,6 +36,7 @@ import MultipleSelectFilterController from './controllers/multiple_select_filter
35
36
  import NestedFormController from './controllers/nested_form_controller'
36
37
  import PanelRefreshController from './controllers/fields/panel_refresh_controller'
37
38
  import PerPageController from './controllers/per_page_controller'
39
+ import PersistentModalController from './controllers/persistent_modal_controller'
38
40
  import PreviewController from './controllers/preview_controller'
39
41
  import ProgressBarFieldController from './controllers/fields/progress_bar_field_controller'
40
42
  import RecordSelectorController from './controllers/record_selector_controller'
@@ -61,6 +63,7 @@ import TrixBodyController from './controllers/trix_body_controller'
61
63
  import TrixFieldController from './controllers/fields/trix_field_controller'
62
64
 
63
65
  application.register('action', ActionController)
66
+ application.register('confirm-dialog', ConfirmDialogController)
64
67
  application.register('actions-overflow', ActionsOverflowController)
65
68
  application.register('actions-picker', ActionsPickerController)
66
69
  application.register('attachments', AttachmentsController)
@@ -85,6 +88,7 @@ application.register('media-library', MediaLibraryController)
85
88
  application.register('menu', MenuController)
86
89
  application.register('modal', ModalController)
87
90
  application.register('modal-size', ModalSizeController)
91
+ application.register('persistent-modal', PersistentModalController)
88
92
  application.register('multiple-select-filter', MultipleSelectFilterController)
89
93
  application.register('avo-nested-form', NestedFormController)
90
94
  application.register('panel-refresh', PanelRefreshController)
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Global (non-element) keyboard shortcuts.
3
+ * Add new entries to HOTKEYS to register more.
4
+ *
5
+ * Two registries:
6
+ * - ELEMENT_HOTKEYS: handled via @github/hotkey (supports sequences like "r r r")
7
+ * - DIRECT_HOTKEYS: custom keydown listeners for keys needing cross-browser normalization
8
+ */
9
+
10
+ import { install } from '@github/hotkey'
11
+
12
+ // Use @github/hotkey for sequences and standard combos.
13
+ const ELEMENT_HOTKEYS = [
14
+ {
15
+ hotkey: 'r r r',
16
+ handle: () => {
17
+ window.reloadScrollTop = document.querySelector('.scrollable-wrapper')?.scrollTop
18
+ window.StreamActions.turbo_reload()
19
+ },
20
+ },
21
+ ]
22
+
23
+ // Use direct listeners for keys where event.key varies across browsers/layouts.
24
+ const DIRECT_HOTKEYS = [
25
+ {
26
+ // Shift+/ → "?" on US layouts, but some browsers expose key:"Slash"+shiftKey instead.
27
+ match: (e) => e.key === '?' || (e.shiftKey && e.code === 'Slash'),
28
+ handle: () => document.dispatchEvent(new Event('persistent-modal:toggle')),
29
+ },
30
+ ]
31
+
32
+ const TYPING_SELECTOR = 'input, textarea, select, [contenteditable]'
33
+
34
+ export function installGlobalHotkeys() {
35
+ // When a hotkey fires on a DOM element that contains a <kbd>, mark it cold
36
+ // before letting the click proceed. preventDefault + requestAnimationFrame
37
+ // gives the browser one frame to paint the cold state before navigation starts.
38
+ document.addEventListener('hotkey-fire', (event) => {
39
+ const kbd = event.target.querySelector('kbd')
40
+ if (!kbd) return
41
+
42
+ event.preventDefault()
43
+ kbd.classList.add('kbd--called')
44
+ requestAnimationFrame(() => event.target.click())
45
+ })
46
+
47
+ document.addEventListener('turbo:load', () => {
48
+ document.querySelectorAll('kbd.kbd--called').forEach((kbd) => kbd.classList.remove('kbd--called'))
49
+
50
+ if (window.reloadScrollTop) {
51
+ setTimeout(() => {
52
+ document.querySelector('.scrollable-wrapper')?.scrollTo(0, window.reloadScrollTop)
53
+ window.reloadScrollTop = null
54
+ }, 50)
55
+ }
56
+ })
57
+
58
+ ELEMENT_HOTKEYS.forEach(({ hotkey, handle }) => {
59
+ const el = document.createElement('span')
60
+ el.addEventListener('hotkey-fire', (event) => {
61
+ event.preventDefault()
62
+ handle()
63
+ })
64
+ install(el, hotkey)
65
+ })
66
+
67
+ document.addEventListener('keydown', (event) => {
68
+ if (event.defaultPrevented || event.repeat) return
69
+ if (event.target instanceof Element && event.target.closest(TYPING_SELECTOR)) return
70
+
71
+ const entry = DIRECT_HOTKEYS.find(({ match }) => match(event))
72
+ if (entry) {
73
+ event.preventDefault()
74
+ entry.handle()
75
+ }
76
+ })
77
+ }
@@ -0,0 +1,7 @@
1
+ export function toggleHidden(element) {
2
+ if (element.hasAttribute('hidden')) {
3
+ element.removeAttribute('hidden')
4
+ } else {
5
+ element.setAttribute('hidden', '')
6
+ }
7
+ }
@@ -25,7 +25,7 @@
25
25
  <% c.with_heading do %>
26
26
  <%= @action.action_name %>
27
27
  <% if Rails.env.development? %>
28
- <%= link_to editor_file_path(@action), target: "_blank", class: "inline-block align-middle ms-1", title: "Open action in your editor", data: { tippy: "tooltip" } do %>
28
+ <%= link_to editor_file_path(@action), target: "_blank", class: "inline-block align-middle ms-1", title: "Open action in your editor", data: { tippy: "tooltip" }, tabindex: "-1" do %>
29
29
  <%= svg "tabler/outline/code", class: "size-4 text-content-secondary cursor-pointer" %>
30
30
  <% end %>
31
31
  <% end %>
@@ -24,7 +24,7 @@
24
24
  action: "click->date-time-filter#clear",
25
25
  tippy: :tooltip
26
26
  } do %>
27
- <%= svg "avo/times", class: "h-4" %>
27
+ <%= svg "tabler/outline/x", class: "h-4" %>
28
28
  <% end %>
29
29
  </div>
30
30
  <div class="flex justify-end">
@@ -27,7 +27,7 @@
27
27
  <div class="shadow-lg rounded px-4 py-3 relative border text-white pointer-events-auto bg-blue-400 border-blue-600 m-2">
28
28
  <div class="flex px-2">
29
29
  <div class="shrink-0">
30
- <%= svg "heroicons/solid/exclamation-circle", class: "h-6" %>
30
+ <%= svg "tabler/filled/alert-circle", class: "h-6" %>
31
31
  </div>
32
32
  <div class="ms-3 flex-1 pt-0.5">
33
33
  <p class="text-sm leading-5 font-semibold"><%= t "avo.not_authorized" %></p>
@@ -1,9 +1,9 @@
1
1
  <% if valid %>
2
2
  <span class="text-green-700 text-xl">
3
- <%= svg "heroicons/outline/check-badge", class: "h-5 inline -mt-1 relative" %> <span>Valid</span>
3
+ <%= svg "tabler/outline/rosette-discount-check", class: "h-5 inline -mt-1 relative" %> <span>Valid</span>
4
4
  </span>
5
5
  <% else %>
6
6
  <span class="text-orange-700 text-xl">
7
- <%= svg "heroicons/outline/exclamation-circle", class: "h-5 inline -mt-1 relative" %> <span>Invalid</span>
7
+ <%= svg "tabler/outline/exclamation-circle", class: "h-5 inline -mt-1 relative" %> <span>Invalid</span>
8
8
  </span>
9
9
  <% end %>
@@ -1,18 +1,45 @@
1
- <%= render Avo::TurboFrameWrapperComponent.new(params[:turbo_frame]) do %>
2
- <%
3
- classes = 'absolute inset-auto start-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2'
4
- label = t 'avo.failed_to_load'
5
- src_url = params[:src].present? && !params[:src].starts_with?('http://') ? CGI.escapeHTML(params[:src]) : nil
1
+ <%
2
+ src_url = params[:src].present? && !params[:src].starts_with?("http://") ? CGI.escapeHTML(params[:src]) : nil
3
+ frame_label = if params[:turbo_frame].present?
4
+ params[:turbo_frame].to_s.humanize.downcase
5
+ else
6
+ "this frame"
7
+ end
6
8
  %>
7
- <div class="relative flex-1 py-4">
8
- <div class="relative block text-gray-300 h-64 w-full">
9
- <%= svg "avo/failed_to_load", class: "#{classes} h-52 text-gray-400" %>
10
- </div>
11
- <div class="relative block text-center text-lg text-gray-400 font-semibold pb-6"><%= label %> <span class="border-b-2 border-gray-200 border-dashed"><%= params[:turbo_frame].to_s.humanize.downcase if params[:turbo_frame].present? %></span> frame</div>
12
- <% if Rails.env.development? && src_url %>
13
- <div class="text-center text-sm w-full pb-3">
14
- This is not an issue with Avo. Use <%= link_to 'this page', src_url, target: :_blank %> to see why this frame failed to load.
9
+
10
+ <%= render Avo::TurboFrameWrapperComponent.new(params[:turbo_frame]) do %>
11
+ <div class="state state--frame-load-failed">
12
+ <div class="state__illustration" aria-hidden="true">
13
+ <% %i[start center end].each do |position| %>
14
+ <div class="state__document state__document--<%= position %>">
15
+ <div class="state__document-body">
16
+ <div class="state__document-lines">
17
+ <span class="state__document-line"></span>
18
+ <span class="state__document-line"></span>
19
+ <span class="state__document-line"></span>
20
+ <span class="state__document-line"></span>
21
+ <span class="state__document-line state__document-line--short"></span>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+
27
+ <div class="state__magnifier">
28
+ <%= svg "tabler/outline/zoom", class: "state__magnifier-icon" %>
15
29
  </div>
30
+ </div>
31
+
32
+ <p class="state__message text-center font-normal">
33
+ Failed to load:
34
+ <span class="font-bold"><%= frame_label %></span>
35
+ </p>
36
+
37
+ <% if Rails.env.development? && src_url.present? %>
38
+ <p class="state__note text-center font-normal">
39
+ Follow
40
+ <%= link_to "this link", src_url, target: "_blank", rel: "noopener", class: "state__link font-normal" %>
41
+ for more details about the issue and how to fix it.
42
+ </p>
16
43
  <% end %>
17
44
  </div>
18
45
  <% end %>
@@ -44,7 +44,7 @@
44
44
 
45
45
  <div class="w-full flex justify-end pe-6">
46
46
  <%= a_button type: :submit,
47
- icon: "avo/save",
47
+ icon: "tabler/outline/device-floppy",
48
48
  size: :sm,
49
49
  style: :outline,
50
50
  data: {
@@ -37,7 +37,7 @@
37
37
  data-action="click->toggle#togglePanel"
38
38
  class="color-scheme-switcher__button color-scheme-switcher__button--accent"
39
39
  title="Accent color">
40
- <%= svg "avo/color-swatch", class: "color-scheme-switcher__icon" %>
40
+ <%= svg "tabler/outline/color-swatch", class: "color-scheme-switcher__icon" %>
41
41
  <span class="color-scheme-switcher__accent-badge">
42
42
  <span class="color-scheme-switcher__accent-badge-preview color-scheme-switcher__accent-badge-preview--neutral"></span>
43
43
  <% accent_colors.each do |accent| %>
@@ -82,7 +82,7 @@
82
82
  data-scheme="auto"
83
83
  class="color-scheme-switcher__button"
84
84
  title="Auto (system preference)">
85
- <%= svg "heroicons/outline/computer-desktop", class: "color-scheme-switcher__icon" %>
85
+ <%= svg "tabler/outline/device-desktop", class: "color-scheme-switcher__icon" %>
86
86
  <span class="sr-only">Auto</span>
87
87
  </button>
88
88
  <button type="button"
@@ -91,7 +91,7 @@
91
91
  data-scheme="light"
92
92
  class="color-scheme-switcher__button"
93
93
  title="Light mode">
94
- <%= svg "heroicons/outline/sun", class: "color-scheme-switcher__icon" %>
94
+ <%= svg "tabler/outline/sun", class: "color-scheme-switcher__icon" %>
95
95
  <span class="sr-only">Light</span>
96
96
  </button>
97
97
  <button type="button"
@@ -100,7 +100,7 @@
100
100
  data-scheme="dark"
101
101
  class="color-scheme-switcher__button"
102
102
  title="Dark mode">
103
- <%= svg "heroicons/outline/moon", class: "color-scheme-switcher__icon" %>
103
+ <%= svg "tabler/outline/moon", class: "color-scheme-switcher__icon" %>
104
104
  <span class="sr-only">Dark</span>
105
105
  </button>
106
106
  </div>
@@ -1,4 +1,4 @@
1
- <dialog id="turbo-confirm" class="mx-auto my-auto w-72">
1
+ <dialog id="turbo-confirm" class="mx-auto my-auto w-72" data-controller="confirm-dialog" data-action="keydown->confirm-dialog#navigate">
2
2
  <form method="dialog">
3
3
  <div class="card relative">
4
4
  <button value="cancel" class="absolute top-3 inset-e-2.5 text-content-secondary bg-transparent hover:bg-tertiary hover:text-content rounded-lg text-sm size-8 ms-auto inline-flex justify-center items-center" data-modal-hide="popup-modal" tabindex="3">
@@ -16,10 +16,10 @@
16
16
  </p>
17
17
  </div>
18
18
  <div class="flex flex-col gap-2 w-full p-4 pt-0 mt-6">
19
- <button value="confirm" class="text-danger border border-danger hover:bg-danger/10 focus:ring-4 focus:outline-hidden focus:ring-danger/30 font-medium rounded-lg text-sm px-4 py-2 text-center cursor-pointer" tabindex="1" autofocus>
19
+ <button value="confirm" class="text-danger border border-danger hover:bg-danger/10 focus:ring-4 focus:outline-hidden focus:ring-danger/30 font-medium rounded-lg text-sm px-4 py-2 text-center cursor-pointer" tabindex="1" autofocus data-confirm-dialog-target="option">
20
20
  <%= t "avo.yes_confirm" %>
21
21
  </button>
22
- <button value="cancel" class="px-4 py-2 text-sm font-medium text-content focus:outline-hidden bg-primary rounded-lg border border-tertiary hover:bg-secondary hover:text-content focus:z-10 focus:ring-4 focus:ring-tertiary cursor-pointer" tabindex="2">
22
+ <button value="cancel" class="px-4 py-2 text-sm font-medium text-content focus:outline-hidden bg-primary rounded-lg border border-tertiary hover:bg-secondary hover:text-content focus:z-10 focus:ring-4 focus:ring-tertiary cursor-pointer" tabindex="2" data-confirm-dialog-target="option">
23
23
  <%= t "avo.no_cancel" %>
24
24
  </button>
25
25
  </div>
@@ -1,7 +1,7 @@
1
1
  <% if @custom_tools_alert_visible %>
2
2
  <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
3
3
  <a href="https://avohq.io/pricing" target="_blank" class="rounded-sm bg-orange-700 text-white py-2 px-4 text-sm block items-center flex leading-tight">
4
- <%= svg "avo/exclamation", class: "h-6 inline me-2 text-bold shrink-0 me-1" %> Warning. <%= @custom_tools_alert_visible %> This page will not be visible in a production environment.
4
+ <%= svg "tabler/outline/alert-triangle", class: "h-6 inline me-2 text-bold shrink-0 me-1" %> Warning. <%= @custom_tools_alert_visible %> This page will not be visible in a production environment.
5
5
  </a>
6
6
  </div>
7
7
  <% end %>
@@ -13,7 +13,7 @@
13
13
  %>
14
14
  <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
15
15
  <a href="<%= url %>" target="<%= target %>" class="rounded-sm bg-orange-700 text-white py-2 px-4 text-sm items-center flex leading-tight space-x-2 rtl:space-x-reverse">
16
- <%= svg "avo/exclamation", class: "h-6 inline me-2 text-bold shrink-0 me-1" %>
16
+ <%= svg "tabler/outline/alert-triangle", class: "h-6 inline me-2 text-bold shrink-0 me-1" %>
17
17
  <div>
18
18
  <%= simple_format message %>
19
19
  </div>
@@ -22,7 +22,7 @@
22
22
  <% elsif error.is_a? String %>
23
23
  <div class="w-full inset-auto bottom-0 z-50 mb-4 opacity-75 hover:opacity-100 transition-opacity duration-150">
24
24
  <div class="rounded-sm bg-orange-700 text-white py-2 px-4 text-sm items-center flex leading-tight space-x-2 rtl:space-x-reverse">
25
- <%= svg "avo/exclamation", class: "h-6 inline me-2 text-bold shrink-0 me-1" %>
25
+ <%= svg "tabler/outline/alert-triangle", class: "h-6 inline me-2 text-bold shrink-0 me-1" %>
26
26
  <div><%= simple_format error %></div>
27
27
  </div>
28
28
  </div>
@@ -1,12 +1,12 @@
1
1
  <%
2
- icon = "avo/selector"
2
+ icon = "tabler/outline/selector"
3
3
 
4
4
  if params[:sort_by] == field.id.to_s
5
5
  case params[:sort_direction]
6
6
  when 'asc'
7
- icon = "avo/sort-ascending"
7
+ icon = "tabler/outline/sort-ascending"
8
8
  when 'desc'
9
- icon = "avo/sort-descending"
9
+ icon = "tabler/outline/sort-descending"
10
10
  end
11
11
  end
12
12
  %>
@@ -87,7 +87,7 @@
87
87
  class="cursor-pointer <%= text_classes %>"
88
88
  data-action="click->toggle#togglePanel"
89
89
  >
90
- <%= svg 'heroicons/solid/chart-bar', class: 'h-3 ms-1' %>
90
+ <%= svg 'tabler/filled/chart-pie-4', class: 'h-3 ms-1' %>
91
91
  </div>
92
92
  <turbo-frame
93
93
  id="summary-frame-<%= field.id %>"
@@ -28,7 +28,7 @@
28
28
  %>
29
29
  <%
30
30
  a_button_or_link = "a_#{entity}"
31
- args = {icon: "heroicons/outline/arrow-left", style: style, color: color, size: size, class: extra_classes, disabled: state == :disabled}
31
+ args = {icon: "tabler/outline/arrow-left", style: style, color: color, size: size, class: extra_classes, disabled: state == :disabled}
32
32
  %>
33
33
  <% if entity == :link %>
34
34
  <div>
@@ -43,7 +43,7 @@
43
43
  <% end %>
44
44
  </div>
45
45
  <div>
46
- <%= a_button icon: "avo/bell", style: style, color: color, size: size, class: extra_classes, disabled: state == :disabled %>
46
+ <%= a_button icon: "tabler/outline/bell", style: style, color: color, size: size, class: extra_classes, disabled: state == :disabled %>
47
47
  </div>
48
48
  <% end %>
49
49
  <% end %>
@@ -1,19 +1,19 @@
1
1
  <div class="flex flex-col">
2
2
  <%= render ui.panel(title: 'Welcome to Avo', description: 'This page is visible only in development. It will be hidden in other environments.') do |panel| %>
3
3
  <% panel.with_controls do %>
4
- <%= a_link('/admin', icon: "heroicons/outline/arrow-left", color: :green, style: :primary, is_link: true) do %>
4
+ <%= a_link('/admin', icon: "tabler/outline/arrow-left", color: :green, style: :primary, is_link: true) do %>
5
5
  Primary
6
6
  <% end %>
7
7
 
8
- <%= a_link('/admin', icon: "heroicons/outline/arrow-left", style: :outline, is_link: true) do %>
8
+ <%= a_link('/admin', icon: "tabler/outline/arrow-left", style: :outline, is_link: true) do %>
9
9
  Outline
10
10
  <% end %>
11
11
 
12
- <%= a_link('/admin', icon: "heroicons/outline/arrow-left", style: :outline, color: :red, is_link: true) do %>
12
+ <%= a_link('/admin', icon: "tabler/outline/arrow-left", style: :outline, color: :red, is_link: true) do %>
13
13
  Red
14
14
  <% end %>
15
15
 
16
- <%= a_link('/admin', icon: "heroicons/outline/arrow-left", style: :text, color: :orange, is_link: true) do %>
16
+ <%= a_link('/admin', icon: "tabler/outline/arrow-left", style: :text, color: :orange, is_link: true) do %>
17
17
  Text
18
18
  <% end %>
19
19
 
@@ -1,8 +1,8 @@
1
1
  <div class="w-10/12 ms-1/12 inset-auto bottom-0 z-50 my-4 opacity-50 hover:opacity-100 transition-opacity duration-150 group">
2
2
  <a href="https://avohq.io/pricing" target="_blank" class="rounded-sm bg-green-700 text-white py-2 px-4 text-sm block group-hover:pt-4">
3
- <%= svg "avo/exclamation", class: 'h-6 inline me-1 text-bold' %> <%= title %>
3
+ <%= svg "tabler/outline/alert-triangle", class: 'h-6 inline me-1 text-bold' %> <%= title %>
4
4
  <div class="group-hover:block hidden py-2">
5
- <%= message %> <%= svg "avo/arrow-circle-right", class: "h-6 inline float-right" %>
5
+ <%= message %> <%= svg "tabler/outline/circle-arrow-right", class: "h-6 inline float-right" %>
6
6
  </div>
7
7
  </a>
8
8
  </div>