avo 1.3.0 → 1.3.5.pre.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +1 -1
  4. data/app/components/avo/common/key_value_component.html.erb +53 -0
  5. data/app/components/avo/common/key_value_component.rb +11 -0
  6. data/app/components/avo/edit/fields/key_value_field_component.html.erb +3 -0
  7. data/app/components/avo/edit/fields/key_value_field_component.rb +4 -0
  8. data/app/components/avo/edit/fields/markdown_field_component.html.erb +1 -0
  9. data/app/components/avo/edit/fields/trix_field_component.html.erb +19 -10
  10. data/app/components/avo/panel_component.html.erb +5 -3
  11. data/app/components/avo/panel_component.rb +6 -1
  12. data/app/components/avo/show/fields/key_value_field_component.html.erb +3 -0
  13. data/app/components/avo/show/fields/key_value_field_component.rb +4 -0
  14. data/app/components/avo/show/fields/markdown_field_component.html.erb +1 -0
  15. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  16. data/app/components/avo/views/resource_show_component.html.erb +1 -1
  17. data/app/controllers/avo/application_controller.rb +5 -1
  18. data/app/controllers/avo/attachments_controller.rb +14 -3
  19. data/app/packs/entrypoints/application.js +14 -11
  20. data/app/packs/js/controllers/fields/code_field_controller.js +1 -0
  21. data/app/packs/js/controllers/fields/date_field_controller.js +1 -1
  22. data/app/packs/js/controllers/fields/key_value_controller.js +137 -0
  23. data/app/packs/js/controllers/fields/simple_mde_controller.js +9 -0
  24. data/app/packs/js/controllers/fields/trix_field_controller.js +121 -0
  25. data/app/views/avo/partials/_javascript.html.erb +3 -1
  26. data/config/routes.rb +4 -0
  27. data/lib/avo.rb +1 -1
  28. data/lib/avo/fields/base_field.rb +3 -1
  29. data/lib/avo/fields/key_value_field.rb +24 -1
  30. data/lib/avo/fields/markdown_field.rb +3 -4
  31. data/lib/avo/fields/trix_field.rb +4 -0
  32. data/lib/avo/version.rb +1 -1
  33. data/lib/generators/avo/action_generator.rb +1 -1
  34. data/lib/generators/avo/locales_generator.rb +2 -2
  35. data/lib/generators/avo/templates/action.tt +5 -9
  36. data/lib/generators/avo/templates/locales/avo.en.yml +1 -0
  37. data/lib/generators/avo/templates/locales/avo.nb-NO.yml +81 -0
  38. data/lib/generators/avo/templates/locales/avo.pt-BR.yml +82 -0
  39. data/lib/generators/avo/templates/locales/avo.ro.yml +70 -0
  40. data/public/avo-packs/css/{application-af3e670d.css → application-5bdca030.css} +92 -18
  41. data/public/avo-packs/css/application-5bdca030.css.br +0 -0
  42. data/public/avo-packs/css/application-5bdca030.css.gz +0 -0
  43. data/public/avo-packs/css/application-5bdca030.css.map +1 -0
  44. data/public/avo-packs/css/application-5bdca030.css.map.br +0 -0
  45. data/public/avo-packs/css/application-5bdca030.css.map.gz +0 -0
  46. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js +26 -0
  47. data/public/avo-packs/js/{application-274069794e43352bbda8.js.LICENSE.txt → application-16a456a2b7cb56b01153.js.LICENSE.txt} +0 -0
  48. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.br +0 -0
  49. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.gz +0 -0
  50. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map +1 -0
  51. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map.br +0 -0
  52. data/public/avo-packs/js/application-16a456a2b7cb56b01153.js.map.gz +0 -0
  53. data/public/avo-packs/manifest.json +15 -15
  54. metadata +28 -17
  55. data/public/avo-packs/css/application-af3e670d.css.br +0 -0
  56. data/public/avo-packs/css/application-af3e670d.css.gz +0 -0
  57. data/public/avo-packs/css/application-af3e670d.css.map +0 -1
  58. data/public/avo-packs/css/application-af3e670d.css.map.br +0 -0
  59. data/public/avo-packs/css/application-af3e670d.css.map.gz +0 -0
  60. data/public/avo-packs/js/application-274069794e43352bbda8.js +0 -26
  61. data/public/avo-packs/js/application-274069794e43352bbda8.js.br +0 -0
  62. data/public/avo-packs/js/application-274069794e43352bbda8.js.gz +0 -0
  63. data/public/avo-packs/js/application-274069794e43352bbda8.js.map +0 -1
  64. data/public/avo-packs/js/application-274069794e43352bbda8.js.map.br +0 -0
  65. data/public/avo-packs/js/application-274069794e43352bbda8.js.map.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f417048179aae7840d2571ab56e787c9eb6c7dd437f07e90221beeded482f5ce
4
- data.tar.gz: 74c2a021008367b695973a98f1ee69eb6711d943675254925f0573c788d5be75
3
+ metadata.gz: 6212847ad5e28753e23625482067816d88ec6728f3d35656182b3a6e46b021d2
4
+ data.tar.gz: 479c5130dd59104ab944af271b85ba398aec7007568116191d82668ab47b44cc
5
5
  SHA512:
6
- metadata.gz: 51bdf61a474220a6d743c81da789dc07b9b54b7c8c6aad0e046b2f576fb0d296145b93e05ce77b73fc57d02b8c8c0692e997faf02da6b76b67b1442a80884602
7
- data.tar.gz: 3faa5c7e35d64499f160a7aca161d6b0b15eccc6cbaceb8dc42bbe5832d063a0c0b39ea951543802599f1ee3c55d358ae64706d1390719a82e9ff49d5dcdbb6f
6
+ metadata.gz: e1a9663120f63b7458932e061a90d7287c7c7405382d64bcf5fb75c7c64c342a6a461a4ba06ae85478fc8e34efba204ecc99b7a3508541753d124153d4f519ae
7
+ data.tar.gz: 7dcb4d3494ff81a33d793f14e7fbd8d25aa178f6d22293fbaf27c6601c7426c87703f93674a5fb5db20d6c5b1b2b3a1aab24901571bd39a3da138f0d7df92a9a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- avo (1.3.0)
4
+ avo (1.3.5.pre.1)
5
5
  active_link_to
6
6
  addressable
7
7
  breadcrumbs_on_rails
data/README.md CHANGED
@@ -36,7 +36,7 @@ Avo is a beautiful next-generation framework that empowers you, the developer, t
36
36
  - **Localization*** - Have it available in any language you need.
37
37
  - **No asset pipeline pollution** - Bring your own asset pipeline.
38
38
 
39
- *Features are still under development
39
+ *Features still under development
40
40
 
41
41
  # Installation
42
42
  Add this line to your application's `Gemfile`:
@@ -0,0 +1,53 @@
1
+ <div class="w-full shadow-lg rounded-lg overflow-hidden"
2
+ data-controller="key-value"
3
+ data-key-value-target="controller"
4
+ data-options="<%= @field.options.to_json %>"
5
+ data-input-classes="<%= input_classes %>"
6
+ data-editable="<%= @view.in?([:edit, :create]) %>"
7
+ >
8
+ <div class="w-full flex flex-col">
9
+ <div class="flex w-full">
10
+ <div class="flex w-full bg-gray-800 shadow overflow-hidden">
11
+ <div class="w-1/2 py-3 px-3 uppercase font-semibold text-xs text-white border-gray-600 border-r">
12
+ <%= @field.key_label %>
13
+ </div>
14
+ <div class="w-1/2 py-3 px-3 uppercase font-semibold text-xs text-white">
15
+ <%= @field.value_label %>
16
+ </div>
17
+ <% if @view.in?([:edit, :create]) %>
18
+ <div class="flex items-center justify-center p-2 px-3 border-l border-gray-600">
19
+ <a href="javascript:void(0);"
20
+ title="<%= @field.action_text %>"
21
+ data-tippy="tooltip"
22
+ data-button="add-row"
23
+ data-action="click->key-value#addRow"
24
+ <% if @field.disable_adding_rows %>
25
+ class="cursor-not-allowed"
26
+ <% end %>
27
+ >
28
+ <%= svg 'plus-circle', class: 'text-gray-400 h-5 hover:text-gray-500' %>
29
+ </a>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
34
+ <div data-key-value-target="rows"></div>
35
+ </div>
36
+ <% if @form.present? %>
37
+ <%= @form.text_area @field.id,
38
+ value: @field.parsed_value,
39
+ class: 'hidden',
40
+ placeholder: @field.placeholder,
41
+ 'data-key-value-target': 'input',
42
+ 'data-view': :edit
43
+ %>
44
+ <% else %>
45
+ <%= text_area_tag @field.id,
46
+ @field.parsed_value,
47
+ class: 'hidden',
48
+ placeholder: @field.placeholder,
49
+ 'data-key-value-target': 'input',
50
+ 'data-view': :edit
51
+ %>
52
+ <% end %>
53
+ </div>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Common::KeyValueComponent < ViewComponent::Base
4
+ include Avo::ApplicationHelper
5
+
6
+ def initialize(field:, form: nil, view: :show)
7
+ @field = field
8
+ @form = form
9
+ @view = view
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, full_width: true do %>
2
+ <%= render Avo::Common::KeyValueComponent.new field: @field, form: @form, view: :edit %>
3
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Edit::Fields::KeyValueFieldComponent < Avo::Edit::Fields::FieldComponent
4
+ end
@@ -5,6 +5,7 @@
5
5
  placeholder: @field.placeholder,
6
6
  disabled: @field.readonly,
7
7
  'data-simple-mde-target': 'element',
8
+ 'data-component-options': @field.options.to_json,
8
9
  'data-view': :edit
9
10
  %>
10
11
  </div>
@@ -1,11 +1,20 @@
1
- <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal do %>
2
- <% trix_id = "trix_#{@resource.name.underscore}_#{@field.id}" %>
3
- <trix-editor input="<%= trix_id %>" value="<%= @field.value %>" placeholder="<%= @field.placeholder %>"><%== @field.value %></trix-editor>
4
- <%= @form.text_area @field.id,
5
- id: trix_id,
6
- class: helpers.input_classes('w-full hidden', has_error: (@resource.model.present? and @resource.model.errors.include?(@field.id))),
7
- placeholder: @field.placeholder,
8
- disabled: @field.readonly,
9
- rows: @field.meta[:rows]
10
- %>
1
+ <%= edit_field_wrapper field: @field, index: @index, form: @form, resource: @resource, displayed_in_modal: @displayed_in_modal, full_width: true do %>
2
+ <div
3
+ data-controller="trix-field"
4
+ data-trix-field-target="controller"
5
+ data-resource-name="<%= @resource.model_class.model_name.route_key %>"
6
+ data-resource-id="<%= @resource.model.id %>"
7
+ data-attachments-disabled="<%= @field.attachments_disabled %>"
8
+ data-attachment-key="<%= @field.attachment_key %>"
9
+ >
10
+ <% trix_id = "trix_#{@resource.name.underscore}_#{@field.id}" %>
11
+ <trix-editor data-trix-field-target="editor" input="<%= trix_id %>" placeholder="<%= @field.placeholder %>"><%== @field.value %></trix-editor>
12
+ <%= @form.text_area @field.id,
13
+ id: trix_id,
14
+ class: helpers.input_classes('w-full hidden', has_error: (@resource.model.present? and @resource.model.errors.include?(@field.id))),
15
+ placeholder: @field.placeholder,
16
+ disabled: @field.readonly,
17
+ rows: @field.meta[:rows]
18
+ %>
19
+ </div>
11
20
  <% end %>
@@ -1,9 +1,11 @@
1
1
  <div <%== data_attributes %>>
2
2
  <div class="mb-6">
3
3
  <div>
4
- <div class="breadcrumbs">
5
- <%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
6
- </div>
4
+ <% if show_breadcrumbs? %>
5
+ <div class="breadcrumbs">
6
+ <%= helpers.render_breadcrumbs(separator: helpers.svg('chevron-right', class: 'inline-block h-3 stroke-current relative top-[-1px] ml-1' )) if Avo.configuration.display_breadcrumbs %>
7
+ </div>
8
+ <% end %>
7
9
  <div class="text-2xl tracking-normal font-bold text-gray-800 truncate">
8
10
  <%= @title %>
9
11
  </div>
@@ -6,10 +6,11 @@ class Avo::PanelComponent < ViewComponent::Base
6
6
  renders_one :bare_content
7
7
  renders_one :footer
8
8
 
9
- def initialize(title: nil, body_classes: nil, data: {})
9
+ def initialize(title: nil, body_classes: nil, data: {}, show_breadcrumbs: true)
10
10
  @title = title
11
11
  @body_classes = body_classes
12
12
  @data = data
13
+ @show_breadcrumbs = show_breadcrumbs
13
14
  end
14
15
 
15
16
  private
@@ -21,4 +22,8 @@ class Avo::PanelComponent < ViewComponent::Base
21
22
  " data-#{key}=\"#{value}\""
22
23
  end.join
23
24
  end
25
+
26
+ def show_breadcrumbs?
27
+ @show_breadcrumbs
28
+ end
24
29
  end
@@ -0,0 +1,3 @@
1
+ <%= show_field_wrapper field: @field, index: @index, full_width: true do %>
2
+ <%= render Avo::Common::KeyValueComponent.new field: @field, view: :show %>
3
+ <% end %>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avo::Show::Fields::KeyValueFieldComponent < Avo::Show::Fields::FieldComponent
4
+ end
@@ -5,6 +5,7 @@
5
5
  placeholder: @field.placeholder,
6
6
  disabled: @field.readonly,
7
7
  'data-simple-mde-target': 'element',
8
+ 'data-component-options': @field.options.to_json,
8
9
  'data-view': :show %>
9
10
  </div>
10
11
  <% end %>
@@ -1,4 +1,4 @@
1
- <%= render Avo::PanelComponent.new title: title, body_classes: 'py-4', data: { component: 'resources-index' } do |c| %>
1
+ <%= render Avo::PanelComponent.new title: title, body_classes: 'py-4', data: { component: 'resources-index' }, show_breadcrumbs: @reflection.blank? do |c| %>
2
2
  <% c.tools do %>
3
3
  <%= render 'actions' if @actions.present? %>
4
4
 
@@ -3,7 +3,7 @@
3
3
  data-selected-resources="[<%= @resource.model.id %>]"
4
4
  >
5
5
  <% @resource.panels.each do |resource_panel| %>
6
- <%= render(Avo::PanelComponent.new(title: resource_panel[:name])) do |c| %>
6
+ <%= render Avo::PanelComponent.new(title: resource_panel[:name], show_breadcrumbs: @reflection.blank?) do |c| %>
7
7
  <% c.tools do %>
8
8
  <% if resource_panel[:name] == @resource.default_panel_name %>
9
9
  <%= render 'actions' %>
@@ -42,7 +42,7 @@ module Avo
42
42
  end
43
43
 
44
44
  def check_avo_license
45
- unless on_root_path || on_resources_path
45
+ unless on_root_path || on_resources_path || on_api_path
46
46
  if @license.invalid? || @license.lacks(:custom_tools)
47
47
  if Rails.env.development?
48
48
  @custom_tools_alert_visible = true
@@ -272,5 +272,9 @@ module Avo
272
272
  def on_resources_path
273
273
  request.original_url.match?(/.*\/#{Avo.configuration.namespace}\/resources\/.*/)
274
274
  end
275
+
276
+ def on_api_path
277
+ request.original_url.match?(/.*\/#{Avo.configuration.namespace}\/avo_api\/.*/)
278
+ end
275
279
  end
276
280
  end
@@ -2,9 +2,20 @@ require_dependency "avo/application_controller"
2
2
 
3
3
  module Avo
4
4
  class AttachmentsController < ApplicationController
5
- before_action :set_resource_name, only: :destroy
6
- before_action :set_resource, only: :destroy
7
- before_action :set_model, only: :destroy
5
+ before_action :set_resource_name, only: [:destroy, :create]
6
+ before_action :set_resource, only: [:destroy, :create]
7
+ before_action :set_model, only: [:destroy, :create]
8
+
9
+ def create
10
+ blob = ActiveStorage::Blob.create_and_upload! io: params[:file], filename: params[:filename]
11
+
12
+ @model.send(params[:attachment_key]).attach blob
13
+
14
+ render json: {
15
+ url: main_app.url_for(blob),
16
+ href: main_app.url_for(blob)
17
+ }
18
+ end
8
19
 
9
20
  def show
10
21
  end
@@ -2,7 +2,6 @@
2
2
  import 'core-js/stable'
3
3
  // eslint-disable-next-line import/no-extraneous-dependencies
4
4
  import 'regenerator-runtime/runtime'
5
- import 'trix'
6
5
  import * as Mousetrap from 'mousetrap'
7
6
  import { Application } from 'stimulus'
8
7
  import { Turbo } from '@hotwired/turbo-rails'
@@ -19,6 +18,19 @@ window.Turbolinks = Turbo
19
18
 
20
19
  Mousetrap.bind('r r r', () => Turbo.visit(window.location.href, { action: 'replace' }))
21
20
 
21
+ function initTippy() {
22
+ tippy('[data-tippy="tooltip"]', {
23
+ theme: 'light',
24
+ content(reference) {
25
+ const title = reference.getAttribute('title')
26
+ reference.removeAttribute('title')
27
+
28
+ return title
29
+ },
30
+ })
31
+ }
32
+ window.initTippy = initTippy
33
+
22
34
  const application = Application.start()
23
35
 
24
36
  const context = require.context('./../js/controllers', true, /\.js$/)
@@ -29,16 +41,7 @@ application.load(definitionsFromContext(fieldsContext))
29
41
 
30
42
  document.addEventListener('turbo:load', () => {
31
43
  document.body.classList.remove('turbo-loading')
32
-
33
- tippy('[data-tippy="tooltip"]', {
34
- theme: 'light',
35
- content(reference) {
36
- const title = reference.getAttribute('title')
37
- reference.removeAttribute('title')
38
-
39
- return title
40
- },
41
- })
44
+ initTippy()
42
45
  })
43
46
  document.addEventListener('turbo:visit', () => document.body.classList.add('turbo-loading'))
44
47
  document.addEventListener('turbo:submit-start', () => document.body.classList.add('turbo-loading'))
@@ -12,6 +12,7 @@ import 'codemirror/mode/shell/shell'
12
12
  import 'codemirror/mode/sql/sql'
13
13
  import 'codemirror/mode/vue/vue'
14
14
  import 'codemirror/mode/xml/xml'
15
+ import 'codemirror/mode/yaml/yaml'
15
16
 
16
17
  import { Controller } from 'stimulus'
17
18
  import { castBoolean } from '@/js/helpers/cast_boolean'
@@ -33,7 +33,7 @@ export default class extends Controller {
33
33
 
34
34
  // enable timezone display
35
35
  if (enableTime) {
36
- currentValue = DateTime.fromISO(this.inputTarget.value, { zone: window.timezone })
36
+ currentValue = DateTime.fromISO(this.inputTarget.value, { zone: window.Avo.configuration.timezone })
37
37
  currentValue = currentValue.setZone(this.inputTarget.dataset.timezone)
38
38
  currentValue = currentValue.toISO()
39
39
 
@@ -0,0 +1,137 @@
1
+ /* eslint-disable max-len */
2
+ import { Controller } from 'stimulus'
3
+ import { castBoolean } from '@/js/helpers/cast_boolean'
4
+
5
+ export default class extends Controller {
6
+ static targets = ['input', 'controller', 'rows']
7
+
8
+ fieldValue = []
9
+
10
+ options = {}
11
+
12
+ get keyInputDisabled() {
13
+ return !this.options.editable || this.options.disable_editing_keys
14
+ }
15
+
16
+ get valueInputDisabled() {
17
+ return !this.options.editable
18
+ }
19
+
20
+ connect() {
21
+ this.setOptions()
22
+
23
+ try {
24
+ const objectValue = JSON.parse(this.inputTarget.value)
25
+ Object.keys(objectValue).forEach((key) => this.fieldValue.push([key, objectValue[key]]))
26
+ } catch (error) {
27
+ this.fieldValue = []
28
+ }
29
+
30
+ this.updateKeyValueComponent()
31
+ }
32
+
33
+ addRow() {
34
+ if (this.options.disable_adding_rows || !this.options.editable) return
35
+ this.fieldValue.push(['', ''])
36
+ this.updateKeyValueComponent()
37
+ this.focusLastRow()
38
+ }
39
+
40
+ deleteRow(event) {
41
+ if (this.options.disable_deleting_rows || !this.options.editable) return
42
+ const { index } = event.target.dataset
43
+ this.fieldValue.splice(index, 1)
44
+ this.updateTextareaInput()
45
+ this.updateKeyValueComponent()
46
+ }
47
+
48
+ focusLastRow() {
49
+ return this.rowsTarget.querySelector('.flex.key-value-row:last-child .key-value-input-key').focus()
50
+ }
51
+
52
+ valueFieldUpdated(event) {
53
+ const { value } = event.target
54
+ const { index } = event.target.dataset
55
+ this.fieldValue[index][1] = value
56
+
57
+ this.updateTextareaInput()
58
+ }
59
+
60
+ keyFieldUpdated(event) {
61
+ const { value } = event.target
62
+ const { index } = event.target.dataset
63
+ this.fieldValue[index][0] = value
64
+
65
+ this.updateTextareaInput()
66
+ }
67
+
68
+ updateTextareaInput() {
69
+ if (!this.hasInputTarget) return
70
+ let result = {}
71
+ if (this.fieldValue && this.fieldValue.length > 0) {
72
+ result = Object.assign(...this.fieldValue.map(([key, val]) => ({ [key]: val })))
73
+ }
74
+ this.inputTarget.innerText = JSON.stringify(result)
75
+ }
76
+
77
+ updateKeyValueComponent() {
78
+ let result = ''
79
+ let index = 0
80
+ this.fieldValue.forEach((row) => {
81
+ const [key, value] = row
82
+ result += this.interpolatedRow(key, value, index)
83
+ index++
84
+ })
85
+ this.rowsTarget.innerHTML = result
86
+ window.initTippy()
87
+ }
88
+
89
+ interpolatedRow(key, value, index) {
90
+ let result = `<div class="flex key-value-row">
91
+ ${this.inputField('key', index, key, value)}
92
+ ${this.inputField('value', index, key, value)}`
93
+ if (this.options.editable) {
94
+ result += `<a
95
+ href="javascript:void(0);"
96
+ data-index="${index}"
97
+ data-action="click->key-value#deleteRow"
98
+ title="${this.options.delete_text}"
99
+ data-tippy="tooltip"
100
+ data-button="delete-row"
101
+ tabindex="-1"
102
+ ${this.options.disable_deleting_rows ? "disabled='disabled'" : ''}
103
+ class="flex items-center justify-center p-2 px-3 border-none ${this.options.disable_deleting_rows ? 'cursor-not-allowed' : ''}"
104
+ ><svg class="pointer-events-none text-gray-500 h-5 hover:text-gray-500" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg></a>`
105
+ }
106
+ result += '</div>'
107
+
108
+ return result
109
+ }
110
+
111
+ inputField(id = 'key', index, key, value) {
112
+ return `<input
113
+ class="${this.options.inputClasses} !rounded-none border-gray-600 border-r border-l-0 border-b-0 border-t-0 focus:border-gray-300 w-1/2 focus:outline-none outline-none key-value-input-${id}"
114
+ data-action="input->key-value#${id}FieldUpdated"
115
+ placeholder="${this.options[`${id}_label`]}"
116
+ data-index="${index}"
117
+ ${this[`${id}InputDisabled`] ? "disabled='disabled'" : ''}
118
+ value="${id === 'key' ? key : value}"
119
+ autofocus
120
+ />`
121
+ }
122
+
123
+ setOptions() {
124
+ let fieldOptions
125
+
126
+ try {
127
+ fieldOptions = JSON.parse(this.controllerTarget.dataset.options)
128
+ } catch (error) {
129
+ fieldOptions = {}
130
+ }
131
+ this.options = {
132
+ ...fieldOptions,
133
+ inputClasses: this.controllerTarget.dataset.inputClasses,
134
+ editable: castBoolean(this.controllerTarget.dataset.editable),
135
+ }
136
+ }
137
+ }