bullet_train 1.1.6 → 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e088bbba7d158e14c1bb6f6e573842f90b05b598b480f80210c2a021c6d9755a
4
- data.tar.gz: 64a3ae2acafde00c0ea3c2f2fdcafbcefa67a43983c2a4b45b5e03c7747a5b85
3
+ metadata.gz: 398b4fbeb7a7498078dfce7f74bdb6518763246d4dac6186fe751cbbafc6ddeb
4
+ data.tar.gz: 5b98cfa4d24635d9a2290637ef2ae4a16614943c59dad22669032c23f4f3b1b1
5
5
  SHA512:
6
- metadata.gz: f6200ec9cc117de6868a7061a372ba2f6b0641272b8ce183a01e0e94387a4dc8cdfbe4f6a35ee154e713804b230ffcc507e8de0160f46ab09b6a0918222c7f37
7
- data.tar.gz: a25fcf36a418be54b27cb2b4697ce40697dc78315fbfeede66d6ddc91eca02695dea99b0c9bc65046c8b036d351ce0a91439ca30b932d445891979bda819e95f
6
+ metadata.gz: 84b8c473cdf360347dd498dd3ae3721ae0b776587e6f93da18b289c59ed470d7795a47fa7ee73c214d2800a636ead41ee8666ce9bdd4d22364385e0075c91a02
7
+ data.tar.gz: f7e34ab6d0112a3cddf5a3ad18e4c740c9fecdd9b46ccb24ad34cac6aadab0269aece837c3abcf03738558aef4e86ee0d190dad0ce42632e0dbdab01f437d08f
@@ -84,11 +84,16 @@ module Account::Memberships::ControllerBase
84
84
  end
85
85
 
86
86
  def reinvite
87
- @invitation = Invitation.new(membership: @membership, team: @team, email: @membership.user_email, from_membership: current_membership)
88
- if @invitation.save
89
- redirect_to [:account, @team, :memberships], notice: I18n.t("account.memberships.notifications.reinvited")
87
+ if helpers.current_limits.can?(:create, Membership)
88
+ @invitation = Invitation.new(membership: @membership, team: @team, email: @membership.user_email, from_membership: current_membership)
89
+ if @invitation.save
90
+ redirect_to [:account, @team, :memberships], notice: I18n.t("account.memberships.notifications.reinvited")
91
+ else
92
+ redirect_to [:account, @team, :memberships], notice: "There was an error creating the invitation (#{@invitation.errors.full_messages.to_sentence})"
93
+ end
90
94
  else
91
- redirect_to [:account, @team, :memberships], notice: "There was an error creating the invitation (#{@invitation.errors.full_messages.to_sentence})"
95
+ flash[:error] = :create_limit
96
+ redirect_to [:account, @team, :memberships]
92
97
  end
93
98
  end
94
99
 
@@ -1,5 +1,7 @@
1
1
  module Account::TeamsHelper
2
2
  def current_team
3
+ # TODO We do not want this to be based on the `current_team_id`.
4
+ # TODO We want this to be based on the current resource being loaded.
3
5
  current_user&.current_team
4
6
  end
5
7
 
@@ -77,4 +79,12 @@ module Account::TeamsHelper
77
79
  def can_invite?
78
80
  can?(:create, Invitation.new(team: current_team))
79
81
  end
82
+
83
+ def current_limits
84
+ @limiter ||= if billing_enabled? && defined?(Billing::Limiter)
85
+ Billing::Limiter.new(current_team)
86
+ else
87
+ Billing::MockLimiter.new(current_team)
88
+ end
89
+ end
80
90
  end
@@ -0,0 +1,12 @@
1
+ class Billing::MockLimiter
2
+ def initialize(team)
3
+ end
4
+
5
+ def broken_hard_limits_for(action, model, count: 1)
6
+ []
7
+ end
8
+
9
+ def can?(action, model)
10
+ true
11
+ end
12
+ end
@@ -31,7 +31,7 @@ module Memberships::Base
31
31
 
32
32
  # TODO Probably we can provide a way for gem packages to define these kinds of extensions.
33
33
  if billing_enabled?
34
- scope :billable, -> { current }
34
+ scope :billable, -> { current_and_invited }
35
35
  end
36
36
  end
37
37
 
@@ -4,6 +4,10 @@ module Records::Base
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
+ if billing_enabled? && defined?(Billing::UsageSupport)
8
+ include Billing::UsageSupport
9
+ end
10
+
7
11
  if defined?(Webhooks::Outgoing::IssuingModel)
8
12
  include Webhooks::Outgoing::IssuingModel
9
13
  end
@@ -27,6 +27,10 @@ module Teams::Base
27
27
  if defined?(Billing::Stripe::Subscription)
28
28
  has_many :billing_stripe_subscriptions, class_name: "Billing::Stripe::Subscription", dependent: :destroy, foreign_key: :team_id
29
29
  end
30
+
31
+ if defined?(Billing::Usage::TeamSupport)
32
+ include Billing::Usage::TeamSupport
33
+ end
30
34
  end
31
35
 
32
36
  # validations
@@ -62,6 +66,12 @@ module Teams::Base
62
66
 
63
67
  # TODO Probably we can provide a way for gem packages to define these kinds of extensions.
64
68
  if billing_enabled?
69
+ def current_billing_subscription
70
+ # If by some bug we have two subscriptions, we want to use the one that existed first.
71
+ # The reasoning here is that it's more likely to be on some legacy plan that benefits the customer.
72
+ billing_subscriptions.active.order(:created_at).first
73
+ end
74
+
65
75
  def needs_billing_subscription?
66
76
  return false if freemium_enabled?
67
77
  billing_subscriptions.active.empty?
@@ -1,7 +1,7 @@
1
1
  <% invitation ||= @invitation %>
2
2
  <% team ||= @team || invitation&.team %>
3
3
  <%= render 'account/teams/breadcrumbs', team: team %>
4
- <%= render 'account/shared/breadcrumb', label: t('.label'), url: [:account, team, :memberships] %>
4
+ <%= render 'account/shared/breadcrumb', label: t('memberships.label'), url: [:account, team, :memberships] %>
5
5
  <%= render 'account/shared/breadcrumb', label: t('.label'), url: [:account, team, :invitations] %>
6
6
  <% if invitation&.persisted? %>
7
7
  <%= render 'account/shared/breadcrumb', label: invitation.label_string, url: [:account, invitation] %>
@@ -1,49 +1,51 @@
1
1
  <%= form_with(model: [:account, (@team unless invitation.persisted?), invitation], class: 'form', local: true) do |form| %>
2
- <%= render 'account/shared/forms/errors', form: form %>
2
+ <%= render "shared/limits/form", form: form, model: invitation.membership, cancel_path: @cancel_path || [:account, invitation] do %>
3
+ <%= render 'account/shared/forms/errors', form: form %>
3
4
 
4
- <%= render 'shared/fields/email_field', form: form, method: :email, options: {autofocus: true} %>
5
+ <%= render 'shared/fields/email_field', form: form, method: :email, options: {autofocus: true} %>
5
6
 
6
- <%= form.fields_for :membership do |membership_form| %>
7
- <div class="grid grid-cols-1 gap-y gap-x sm:grid-cols-6">
8
- <div class="sm:col-span-3">
9
- <%= render 'shared/fields/text_field', form: membership_form, method: :user_first_name %>
10
- </div>
7
+ <%= form.fields_for :membership do |membership_form| %>
8
+ <div class="grid grid-cols-1 gap-y gap-x sm:grid-cols-6">
9
+ <div class="sm:col-span-3">
10
+ <%= render 'shared/fields/text_field', form: membership_form, method: :user_first_name %>
11
+ </div>
11
12
 
12
- <div class="sm:col-span-3">
13
- <%= render 'shared/fields/text_field', form: membership_form, method: :user_last_name %>
13
+ <div class="sm:col-span-3">
14
+ <%= render 'shared/fields/text_field', form: membership_form, method: :user_last_name %>
15
+ </div>
14
16
  </div>
15
- </div>
16
- <% end %>
17
+ <% end %>
17
18
 
18
- <% if can? :manage, @team %>
19
- <%= form.fields_for :membership do |fields| %>
20
- <%= fields.hidden_field :team_id, value: @team.id %>
21
- <div class="space-y-3">
22
- <% Membership.assignable_roles.each do |role| %>
23
- <% if current_membership.can_manage_role?(role) %>
24
- <div class="flex items-top">
25
- <%= fields.check_box :role_ids, {multiple: true, class: "h-4 w-4 text-blue focus:ring-blue-dark border-gray-300 rounded mt-0.5"}, role.id, nil %>
26
- <label for="invitation_membership_attributes_role_ids_<%= role.id %>" class="ml-2 block select-none">
27
- <span><%= t('invitations.form.invite_as', role_key: t("memberships.fields.role_ids.options.#{role.key}.label")) %></span>
28
- <div class="mt-0.5 text-gray-400 font-light leading-normal">
29
- <%= t("memberships.fields.role_ids.options.#{role.key}.description") %>
30
- </div>
31
- </label>
32
- </div>
19
+ <% if can? :manage, @team %>
20
+ <%= form.fields_for :membership do |fields| %>
21
+ <%= fields.hidden_field :team_id, value: @team.id %>
22
+ <div class="space-y-3">
23
+ <% Membership.assignable_roles.each do |role| %>
24
+ <% if current_membership.can_manage_role?(role) %>
25
+ <div class="flex items-top">
26
+ <%= fields.check_box :role_ids, {multiple: true, class: "h-4 w-4 text-blue focus:ring-blue-dark border-gray-300 rounded mt-0.5"}, role.id, nil %>
27
+ <label for="invitation_membership_attributes_role_ids_<%= role.id %>" class="ml-2 block select-none">
28
+ <span><%= t('invitations.form.invite_as', role_key: t("memberships.fields.role_ids.options.#{role.key}.label")) %></span>
29
+ <div class="mt-0.5 text-gray-400 font-light leading-normal">
30
+ <%= t("memberships.fields.role_ids.options.#{role.key}.description") %>
31
+ </div>
32
+ </label>
33
+ </div>
34
+ <% end %>
33
35
  <% end %>
34
- <% end %>
35
- </div>
36
+ </div>
37
+ <% end %>
36
38
  <% end %>
37
- <% end %>
38
39
 
39
- <%# 🚅 super scaffolding will insert new fields above this line. %>
40
+ <%# 🚅 super scaffolding will insert new fields above this line. %>
40
41
 
41
- <div class="buttons">
42
- <%= form.submit (form.object.persisted? ? t('.buttons.update') : t('.buttons.create')), class: "button" %>
43
- <% if form.object.persisted? %>
44
- <%= link_to t('global.buttons.cancel'), account_invitation_path(invitation), class: "button-secondary" %>
45
- <% else %>
46
- <%= link_to t('global.buttons.cancel'), @cancel_path || account_team_invitations_path(@team), class: "button-secondary" %>
47
- <% end %>
48
- </div>
42
+ <div class="buttons">
43
+ <%= form.submit (form.object.persisted? ? t('.buttons.update') : t('.buttons.create')), class: "button" %>
44
+ <% if form.object.persisted? %>
45
+ <%= link_to t('global.buttons.cancel'), account_invitation_path(invitation), class: "button-secondary" %>
46
+ <% else %>
47
+ <%= link_to t('global.buttons.cancel'), @cancel_path || account_team_invitations_path(@team), class: "button-secondary" %>
48
+ <% end %>
49
+ </div>
50
+ <% end %>
49
51
  <% end %>
@@ -42,5 +42,4 @@
42
42
  <%= form.submit t('.buttons.update'), class: "button" %>
43
43
  <%= link_to t('global.buttons.cancel'), [:account, @team, :memberships], class: "button-secondary" %>
44
44
  </div>
45
-
46
45
  <% end %>
@@ -2,65 +2,68 @@
2
2
  <% hide_actions ||= false %>
3
3
  <% hide_back ||= false %>
4
4
 
5
- <%= render 'account/shared/box' do |p| %>
6
- <% p.content_for :title, t(".contexts.#{context.class.name.underscore}.header") %>
7
- <% p.content_for :description do %>
8
- <%= raw t(".contexts.#{context.class.name.underscore}.#{memberships.any? ? 'description' : 'description_empty'}") %>
9
- <% end %>
5
+ <%= updates_for context, :memberships do %>
6
+ <%= render 'account/shared/box' do |p| %>
7
+ <% p.content_for :title, t(".contexts.#{context.class.name.underscore}.header") %>
8
+ <% p.content_for :description do %>
9
+ <%= raw t(".contexts.#{context.class.name.underscore}.#{memberships.any? ? 'description' : 'description_empty'}") %>
10
+ <%= render "shared/limits/index", model: memberships.model %>
11
+ <% end %>
10
12
 
11
- <% p.content_for :table do %>
12
- <% if memberships.any? %>
13
- <table class="table">
14
- <thead>
15
- <tr>
16
- <th><%= t('memberships.singular') %></th>
17
- <th><%= t('memberships.fields.role_ids.heading') %></th>
18
- <%# 🚅 super scaffolding will insert new field headers above this line. %>
19
- <th></th>
20
- </tr>
21
- </thead>
22
- <tbody data-model="Membership" data-scope="current">
23
- <% memberships.each do |membership| %>
24
- <tr data-id="<%= membership.id %>">
13
+ <% p.content_for :table do %>
14
+ <% if memberships.any? %>
15
+ <table class="table">
16
+ <thead>
17
+ <tr>
18
+ <th><%= t('memberships.singular') %></th>
19
+ <th><%= t('memberships.fields.role_ids.heading') %></th>
20
+ <%# 🚅 super scaffolding will insert new field headers above this line. %>
21
+ <th></th>
22
+ </tr>
23
+ </thead>
24
+ <tbody data-model="Membership" data-scope="current">
25
+ <% memberships.each do |membership| %>
26
+ <tr data-id="<%= membership.id %>">
25
27
 
26
- <td class="px-6 py-4 whitespace-nowrap">
27
- <%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
28
- <div class="flex-shrink-0 h-10 w-10">
29
- <%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
30
- </div>
28
+ <td class="px-6 py-4 whitespace-nowrap">
29
+ <%= link_to [:account, membership], class: 'block flex items-center group hover:no-underline no-underline' do %>
30
+ <div class="flex-shrink-0 h-10 w-10">
31
+ <%= image_tag membership_profile_photo_url(membership), title: membership.label_string, class: 'h-10 w-10 rounded-full' %>
32
+ </div>
31
33
 
32
- <div class="ml-3">
33
- <span class="group-hover:underline"><%= membership.label_string %></span>
34
- <% if membership.unclaimed? %>
35
- <span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
36
- Invited
37
- </span>
38
- <% end %>
39
- </div>
40
- <% end %>
41
- </td>
34
+ <div class="ml-3">
35
+ <span class="group-hover:underline"><%= membership.label_string %></span>
36
+ <% if membership.unclaimed? %>
37
+ <span class="ml-1.5 px-2 inline-flex text-xs text-green-dark bg-green-light border border-green-dark rounded-md">
38
+ Invited
39
+ </span>
40
+ <% end %>
41
+ </div>
42
+ <% end %>
43
+ </td>
42
44
 
43
- <td>
44
- <% if membership.roles_without_defaults.any? %>
45
- <%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
46
- <% else %>
47
- <%= t("memberships.fields.role_ids.options.default.label") %>
48
- <% end %>
49
- </td>
50
- <td class="text-right">
51
- <%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
52
- </td>
53
- </tr>
54
- <% end %>
55
- </tbody>
56
- </table>
45
+ <td>
46
+ <% if membership.roles_without_defaults.any? %>
47
+ <%= membership.roles_without_defaults.map { |role| t("memberships.fields.role_ids.options.#{role.key}.label") }.to_sentence %>
48
+ <% else %>
49
+ <%= t("memberships.fields.role_ids.options.default.label") %>
50
+ <% end %>
51
+ </td>
52
+ <td class="text-right">
53
+ <%= link_to t('.buttons.show'), [:account, membership], class: 'button-secondary button-smaller' %>
54
+ </td>
55
+ </tr>
56
+ <% end %>
57
+ </tbody>
58
+ </table>
59
+ <% end %>
57
60
  <% end %>
58
- <% end %>
59
61
 
60
- <% unless hide_actions %>
61
- <% p.content_for :actions do %>
62
- <%= link_to t('invitations.buttons.new'), new_account_team_invitation_path(@team, cancel_path: account_team_memberships_path(@team)), class: "#{first_button_primary}" %>
63
- <%= link_to t('global.buttons.back'), [:account, context], class: "#{first_button_primary} back" unless hide_back %>
62
+ <% unless hide_actions %>
63
+ <% p.content_for :actions do %>
64
+ <%= link_to t('invitations.buttons.new'), new_account_team_invitation_path(@team, cancel_path: account_team_memberships_path(@team)), class: "#{first_button_primary}" %>
65
+ <%= link_to t('global.buttons.back'), [:account, context], class: "#{first_button_primary} back" unless hide_back %>
66
+ <% end %>
64
67
  <% end %>
65
68
  <% end %>
66
69
  <% end %>
@@ -1,3 +1,3 @@
1
1
  module BulletTrain
2
- VERSION = "1.1.6"
2
+ VERSION = "1.1.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6
4
+ version: 1.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-17 00:00:00.000000000 Z
11
+ date: 2022-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: standard
@@ -428,8 +428,6 @@ files:
428
428
  - README.md
429
429
  - Rakefile
430
430
  - app/assets/config/bullet_train_manifest.js
431
- - app/assets/javascripts/bullet-train.js
432
- - app/assets/javascripts/bullet-train.js.map
433
431
  - app/controllers/account/invitations_controller.rb
434
432
  - app/controllers/account/memberships_controller.rb
435
433
  - app/controllers/account/onboarding/user_details_controller.rb
@@ -483,6 +481,7 @@ files:
483
481
  - app/mailers/concerns/mailers/base.rb
484
482
  - app/mailers/devise_mailer.rb
485
483
  - app/mailers/user_mailer.rb
484
+ - app/models/billing/mock_limiter.rb
486
485
  - app/models/concerns/current_attributes/base.rb
487
486
  - app/models/concerns/invitations/base.rb
488
487
  - app/models/concerns/memberships/base.rb
@@ -1,2 +0,0 @@
1
- import{Controller as e}from"@hotwired/stimulus";function t(e){const t=(e.match(/^(?:\.\/)?(.+)(?:[_-]controller\..+?)$/)||[])[1];if(t)return t.replace(/_/g,"-").replace(/\//g,"--")}class l extends e{connect(){this.updateAvailability()}updateFormAndSubmit(e){return this.recreateIdsHiddenFields(),this.createOrUpdateAllField(),!0}updateIds(e){var t;null!=e&&null!=(t=e.detail)&&t.ids&&(this.idsValue=e.detail.ids,this.allValue=e.detail.all),this.updateAvailability(),this.updateButtonLabel()}updateAvailability(){this.element.classList.toggle(this.hiddenClass,0===this.idsValue.length)}updateButtonLabel(){let e=this.buttonIfAllValue;this.idsValue.length&&!1===this.allValue&&(e=this.buttonIfIdsValue.replace("{num}",this.idsValue.length)),"INPUT"===this.buttonTarget.tagName?this.buttonTarget.value=e:this.buttonTarget.textContent=e}recreateIdsHiddenFields(){this.removeIdsHiddenFields(),this.createIdsHiddenFields()}removeIdsHiddenFields(){this.idsHiddenFieldTargets.forEach(e=>{this.element.removeChild(e)})}createIdsHiddenFields(){this.idsValue.forEach(e=>{let t=document.createElement("input");t.type="hidden",t.name=this.objectNameValue+"["+this.idsFieldNameValue+"][]",t.value=e,this.element.appendChild(t)})}createOrUpdateAllField(){this.hasAllHiddenFieldTarget?this.allHiddenFieldTarget.value=this.allValue?"true":"false":this.createAllField()}createAllField(){let e=document.createElement("input");e.type="hidden",e.name=this.objectNameValue+"["+this.allFieldNameValue+"]",e.value=this.allValue?"true":"false",this.element.appendChild(e)}}l.targets=["button","idsHiddenField","allHiddenField"],l.classes=["hidden"],l.values={buttonIfAll:String,buttonIfIds:String,ids:Array,all:Boolean,objectName:String,idsFieldName:String,allFieldName:String};class s extends e{connect(){this.element.classList.add(this.selectableAvailableClass)}toggleSelectable(){this.selectableValue=!this.selectableValue}updateSelectedIds(){this.updateActions(),this.updateSelectAllCheckbox()}updateActions(){this.actionTargets.forEach(e=>{e.dispatchEvent(new CustomEvent("update-ids",{detail:{ids:this.selectedIds,all:this.allSelected}}))})}selectAllOrNone(e){this.allSelected?this.selectNone():this.selectAll(),this.updateSelectAllCheckbox()}selectAll(){this.checkboxTargets.forEach(e=>{e.checked=!0}),this.updateActions()}selectNone(){this.checkboxTargets.forEach(e=>{e.checked=!1}),this.updateActions()}updateSelectAllCheckbox(){let e=this.selectAllCheckboxTarget,t=this.selectAllLabelTarget;this.allSelected?(e.checked=!0,e.indeterminate=!1,t.dispatchEvent(new CustomEvent("toggle",{detail:{useAlternate:!0}}))):this.selectedIds.length>0?(e.indeterminate=!0,t.dispatchEvent(new CustomEvent("toggle",{detail:{useAlternate:!1}}))):(e.checked=!1,e.indeterminate=!1,t.dispatchEvent(new CustomEvent("toggle",{detail:{useAlternate:!1}})))}selectableValueChanged(){this.element.classList.toggle(this.selectableClass,this.selectableValue),this.updateToggleLabel()}updateToggleLabel(){this.selectableToggleTarget.dispatchEvent(new CustomEvent("toggle",{detail:{useAlternate:this.selectableValue}}))}get selectedIds(){let e=[];return this.checkboxTargets.forEach(t=>{t.checked&&e.push(t.value)}),e}get allSelected(){return this.selectedIds.length===this.checkboxTargets.length}}s.targets=["checkbox","selectAllCheckbox","action","selectableToggle","selectAllLabel"],s.classes=["selectableAvailable","selectable"],s.values={selectable:Boolean};class a extends e{copy(){this.inputTarget.value=this.sourceTarget.innerText,this.inputTarget.select(),document.execCommand("copy"),this.buttonTarget.innerHTML='<i id="copied" class="fas fa-check w-4 h-4 block text-green-600"></i>',setTimeout(function(){document.getElementById("copied").innerHTML='<i class="far fa-copy w-4 h-4 block text-gray-600"></i>'},1500)}}a.targets=["source","input","button"];class i extends e{constructor(){super(...arguments),this.removeTrailingNewlines=e=>{e.element.innerHTML.match(/<br><\/div>$/)&&(e.element.innerHTML=e.element.innerHTML.slice(0,-10)+"</div>",this.removeTrailingNewlines(e))},this.removeTrailingWhitespace=e=>{e.element.innerHTML.match(/&nbsp;<\/div>$/)?(e.element.innerHTML=e.element.innerHTML.slice(0,-12)+"</div>",this.removeTrailingWhitespace(e)):e.element.innerHTML.match(/&nbsp; <\/div>$/)&&(e.element.innerHTML=e.element.innerHTML.slice(0,-13)+"</div>",this.removeTrailingWhitespace(e))}}resetOnSuccess(e){e.detail.success&&e.target.reset()}stripTrix(){this.trixFieldTargets.forEach(e=>{this.removeTrailingNewlines(e.editor),this.removeTrailingWhitespace(e.editor),e.parentElement.querySelector("input").value=e.innerHTML})}submitOnReturn(e){if((e.metaKey||e.ctrlKey)&&13==e.keyCode){e.preventDefault();let t=e.target.closest("form");this.submitForm(t)}}submitForm(e){e.requestSubmit?e.requestSubmit():e.querySelector("[type=submit]").click()}}async function n(e,t,l){const s=t.dataset,a=l?`${l}-${e}`:e;let i=`transition${e.charAt(0).toUpperCase()+e.slice(1)}`;const n=s[i]?s[i].split(" "):[a],c=s[`${i}Start`]?s[`${i}Start`].split(" "):[`${a}-start`],o=s[`${i}End`]?s[`${i}End`].split(" "):[`${a}-end`];r(t,n),r(t,c),await new Promise(e=>{requestAnimationFrame(()=>{requestAnimationFrame(e)})}),d(t,c),r(t,o),await function(e){return new Promise(t=>{const l=getComputedStyle(e).transitionDuration.split(",")[0],s=1e3*Number(l.replace("s",""));setTimeout(()=>{t()},s)})}(t),d(t,o),d(t,n)}function r(e,t){e.classList.add(...t)}function d(e,t){e.classList.remove(...t)}i.targets=["trixField","scroll"];class c extends e{open(){this.showWrapper(),this.revealableTargets.forEach(e=>{!async function(e,t=null){e.classList.remove("hidden"),await n("enter",e,t)}(e)})}close(){Promise.all(this.revealableTargets.map(e=>async function(e,t=null){await n("leave",e,t),e.classList.add("hidden")}(e))).then(()=>{this.hideWrapper()})}showWrapper(){this.wrapperTarget.classList.remove(this.hiddenClass)}hideWrapper(){this.wrapperTarget.classList.add(this.hiddenClass)}}c.targets=["wrapper","revealable"],c.classes=["hidden"];class o extends e{connect(){this.updateLabel()}toggle(e){var t;this.useAlternateValue=void 0!==(null==e||null==(t=e.detail)?void 0:t.useAlternate)?e.detail.useAlternate:!this.useAlternateValue}useAlternateValueChanged(){this.updateLabel()}updateLabel(){this.hasLabelValue&&this.hasLabelAlternateValue&&this.hasUseAlternateValue&&(this.element.textContent=!0===this.useAlternateValue?this.labelAlternateValue:this.labelValue)}}o.values={label:String,labelAlternate:String,useAlternate:Boolean};const h=[[l,"bulk_action_form_controller.js"],[s,"bulk_actions_controller.js"],[a,"clipboard_controller.js"],[i,"form_controller.js"],[c,"mobile_menu_controller.js"],[o,"text_toggle_controller.js"]].map(function(e){const l=e[0];return{identifier:t(e[1]),controllerConstructor:l}});document.addEventListener("turbo:load",()=>{navigator.userAgent.toLocaleLowerCase().includes("electron")&&document.body.classList.add("electron")});export{h as controllerDefinitions};
2
- //# sourceMappingURL=bullet-train.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bullet-train.js","sources":["../../../node_modules/@hotwired/stimulus-webpack-helpers/dist/stimulus-webpack-helpers.js","../../javascript/controllers/bulk_action_form_controller.js","../../javascript/controllers/bulk_actions_controller.js","../../javascript/controllers/clipboard_controller.js","../../javascript/controllers/form_controller.js","../../../node_modules/el-transition/index.js","../../javascript/controllers/mobile_menu_controller.js","../../javascript/controllers/text_toggle_controller.js","../../javascript/controllers/index.js","../../javascript/electron/index.js"],"sourcesContent":["/*\nStimulus Webpack Helpers 1.0.0\nCopyright © 2021 Basecamp, LLC\n */\nfunction definitionsFromContext(context) {\n return context.keys()\n .map((key) => definitionForModuleWithContextAndKey(context, key))\n .filter((value) => value);\n}\nfunction definitionForModuleWithContextAndKey(context, key) {\n const identifier = identifierForContextKey(key);\n if (identifier) {\n return definitionForModuleAndIdentifier(context(key), identifier);\n }\n}\nfunction definitionForModuleAndIdentifier(module, identifier) {\n const controllerConstructor = module.default;\n if (typeof controllerConstructor == \"function\") {\n return { identifier, controllerConstructor };\n }\n}\nfunction identifierForContextKey(key) {\n const logicalName = (key.match(/^(?:\\.\\/)?(.+)(?:[_-]controller\\..+?)$/) || [])[1];\n if (logicalName) {\n return logicalName.replace(/_/g, \"-\").replace(/\\//g, \"--\");\n }\n}\n\nexport { definitionForModuleAndIdentifier, definitionForModuleWithContextAndKey, definitionsFromContext, identifierForContextKey };\n","import { Controller } from '@hotwired/stimulus'\n\nexport default class extends Controller {\n static targets = [ \"button\", \"idsHiddenField\", \"allHiddenField\" ]\n static classes = [ \"hidden\" ]\n static values = {\n buttonIfAll: String,\n buttonIfIds: String,\n ids: Array,\n all: Boolean,\n objectName: String,\n idsFieldName: String,\n allFieldName: String\n }\n\n connect() {\n this.updateAvailability()\n }\n\n updateFormAndSubmit(event) {\n this.recreateIdsHiddenFields()\n this.createOrUpdateAllField()\n return true\n }\n\n updateIds(event) {\n if (event?.detail?.ids) {\n this.idsValue = event.detail.ids\n this.allValue = event.detail.all\n }\n\n this.updateAvailability()\n this.updateButtonLabel()\n }\n\n updateAvailability() {\n this.element.classList.toggle(this.hiddenClass, this.idsValue.length === 0)\n }\n\n updateButtonLabel() {\n let label = this.buttonIfAllValue\n if (this.idsValue.length && this.allValue === false) {\n label = this.buttonIfIdsValue.replace('{num}', this.idsValue.length)\n }\n\n switch (this.buttonTarget.tagName) {\n case 'INPUT': this.buttonTarget.value = label; break;\n default: this.buttonTarget.textContent = label; break;\n }\n }\n\n recreateIdsHiddenFields() {\n this.removeIdsHiddenFields()\n this.createIdsHiddenFields()\n }\n\n removeIdsHiddenFields() {\n this.idsHiddenFieldTargets.forEach(field => {\n this.element.removeChild(field)\n })\n }\n\n createIdsHiddenFields() {\n this.idsValue.forEach(id => {\n let field = document.createElement('input')\n field.type = 'hidden'\n field.name = `${this.objectNameValue}[${this.idsFieldNameValue}][]`\n field.value = id\n this.element.appendChild(field)\n })\n }\n\n createOrUpdateAllField() {\n if (this.hasAllHiddenFieldTarget) {\n this.allHiddenFieldTarget.value = this.allValue? 'true': 'false'\n } else {\n this.createAllField()\n }\n }\n\n createAllField() {\n let field = document.createElement('input')\n field.type = 'hidden'\n field.name = `${this.objectNameValue}[${this.allFieldNameValue}]`\n field.value = this.allValue? 'true': 'false'\n this.element.appendChild(field)\n }\n}","import { Controller } from '@hotwired/stimulus'\n\nexport default class extends Controller {\n static targets = [ \"checkbox\", \"selectAllCheckbox\", \"action\", \"selectableToggle\", \"selectAllLabel\" ]\n static classes = [ \"selectableAvailable\", \"selectable\" ]\n static values = {\n selectable: Boolean\n }\n\n connect() {\n this.element.classList.add(this.selectableAvailableClass)\n }\n\n toggleSelectable() {\n this.selectableValue = !this.selectableValue\n }\n\n updateSelectedIds() {\n this.updateActions()\n this.updateSelectAllCheckbox()\n }\n\n updateActions() {\n this.actionTargets.forEach(actionTarget => {\n actionTarget.dispatchEvent(new CustomEvent('update-ids', { detail: {\n ids: this.selectedIds,\n all: this.allSelected\n }}))\n })\n }\n\n selectAllOrNone(event) {\n if (this.allSelected) {\n this.selectNone()\n } else {\n this.selectAll()\n }\n this.updateSelectAllCheckbox()\n }\n\n selectAll() {\n this.checkboxTargets.forEach(checkbox => {\n checkbox.checked = true\n })\n this.updateActions()\n }\n\n selectNone() {\n this.checkboxTargets.forEach(checkbox => {\n checkbox.checked = false\n })\n this.updateActions()\n }\n\n updateSelectAllCheckbox() {\n let checkbox = this.selectAllCheckboxTarget\n let label = this.selectAllLabelTarget\n\n if (this.allSelected) {\n checkbox.checked = true\n checkbox.indeterminate = false\n label.dispatchEvent(new CustomEvent('toggle', { detail: { useAlternate: true }} ))\n } else if (this.selectedIds.length > 0) {\n checkbox.indeterminate = true\n label.dispatchEvent(new CustomEvent('toggle', { detail: { useAlternate: false }} ))\n } else {\n checkbox.checked = false\n checkbox.indeterminate = false\n label.dispatchEvent(new CustomEvent('toggle', { detail: { useAlternate: false }} ))\n }\n }\n\n selectableValueChanged() {\n this.element.classList.toggle(this.selectableClass, this.selectableValue)\n this.updateToggleLabel()\n }\n\n updateToggleLabel() {\n this.selectableToggleTarget.dispatchEvent(new CustomEvent('toggle', { detail: { useAlternate: this.selectableValue }} ))\n }\n\n get selectedIds() {\n let ids = []\n this.checkboxTargets.forEach(checkbox => {\n if (checkbox.checked) {\n ids.push(checkbox.value)\n }\n })\n return ids\n }\n\n get allSelected() {\n return this.selectedIds.length === this.checkboxTargets.length\n }\n}","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = ['source', 'input', 'button']\n\n copy() {\n this.inputTarget.value = this.sourceTarget.innerText\n this.inputTarget.select()\n document.execCommand('copy')\n this.buttonTarget.innerHTML = '<i id=\"copied\" class=\"fas fa-check w-4 h-4 block text-green-600\"></i>'\n setTimeout(function () {\n document.getElementById('copied').innerHTML = '<i class=\"far fa-copy w-4 h-4 block text-gray-600\"></i>'\n }, 1500)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\n// TODO Some of this feels really specific to the conversation messages form. Should we rename this controller?\nexport default class extends Controller {\n static targets = ['trixField', 'scroll']\n\n resetOnSuccess(e){\n if(e.detail.success) {\n e.target.reset();\n }\n }\n\n stripTrix(){\n this.trixFieldTargets.forEach(element => {\n this.removeTrailingNewlines(element.editor)\n this.removeTrailingWhitespace(element.editor)\n // When doing this as part of the form submission, Trix does not update the input element's value attribute fast enough.\n // In order to submit the stripped value, we manually update it here to fix the race condition\n element.parentElement.querySelector(\"input\").value = element.innerHTML\n })\n }\n\n submitOnReturn(e) {\n if((e.metaKey || e.ctrlKey) && e.keyCode == 13) {\n e.preventDefault();\n let form = e.target.closest(\"form\")\n this.submitForm(form)\n }\n }\n\n removeTrailingNewlines = (trixEditor) => {\n if (trixEditor.element.innerHTML.match(/<br><\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -10) + \"</div>\"\n this.removeTrailingNewlines(trixEditor)\n }\n }\n\n removeTrailingWhitespace = (trixEditor) => {\n if (trixEditor.element.innerHTML.match(/&nbsp;<\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -12) + \"</div>\"\n this.removeTrailingWhitespace(trixEditor)\n } else if (trixEditor.element.innerHTML.match(/&nbsp; <\\/div>$/)) {\n trixEditor.element.innerHTML = trixEditor.element.innerHTML.slice(0, -13) + \"</div>\"\n this.removeTrailingWhitespace(trixEditor)\n }\n }\n\n submitForm(form) {\n // Right now, Safari and IE don't support the requestSubmit method which is required for Turbo\n // Doing form.submit() doesn't actually fire the submit event which Turbo needs\n if (form.requestSubmit) {\n form.requestSubmit()\n } else {\n form.querySelector(\"[type=submit]\").click()\n }\n }\n}\n","export async function enter(element, transitionName = null) {\n element.classList.remove('hidden')\n await transition('enter', element, transitionName)\n}\n\nexport async function leave(element, transitionName = null) {\n await transition('leave', element, transitionName)\n element.classList.add('hidden')\n}\n\nexport async function toggle(element, transitionName = null) {\n if (element.classList.contains('hidden')) {\n await enter(element, transitionName)\n } else {\n await leave(element, transitionName)\n }\n}\n\nasync function transition(direction, element, animation) {\n const dataset = element.dataset\n const animationClass = animation ? `${animation}-${direction}` : direction\n let transition = `transition${direction.charAt(0).toUpperCase() + direction.slice(1)}`\n const genesis = dataset[transition] ? dataset[transition].split(\" \") : [animationClass]\n const start = dataset[`${transition}Start`] ? dataset[`${transition}Start`].split(\" \") : [`${animationClass}-start`]\n const end = dataset[`${transition}End`] ? dataset[`${transition}End`].split(\" \") : [`${animationClass}-end`]\n\n addClasses(element, genesis)\n addClasses(element, start)\n await nextFrame()\n removeClasses(element, start)\n addClasses(element, end);\n await afterTransition(element)\n removeClasses(element, end)\n removeClasses(element, genesis)\n}\n\nfunction addClasses(element, classes) {\n element.classList.add(...classes)\n}\n\nfunction removeClasses(element, classes) {\n element.classList.remove(...classes)\n}\n\nfunction nextFrame() {\n return new Promise(resolve => {\n requestAnimationFrame(() => {\n requestAnimationFrame(resolve)\n });\n });\n}\n\nfunction afterTransition(element) {\n return new Promise(resolve => {\n // safari return string with comma separate values\n const computedDuration = getComputedStyle(element).transitionDuration.split(\",\")[0]\n const duration = Number(computedDuration.replace('s', '')) * 1000;\n setTimeout(() => {\n resolve()\n }, duration)\n });\n}","import { Controller } from \"@hotwired/stimulus\"\nimport { enter, leave } from \"el-transition\"\n\nexport default class extends Controller {\n static targets = [ \"wrapper\", \"revealable\"]\n static classes = [ \"hidden\" ] // necessary because we're always hiding the mobile menu on larger screens and this is the class used for only mobile screen sizes\n \n open() {\n this.showWrapper()\n this.revealableTargets.forEach(revealableTarget => {\n enter(revealableTarget)\n })\n }\n \n close() {\n Promise.all(\n this.revealableTargets.map(revealableTarget => {\n return leave(revealableTarget)\n })\n ).then(() => {\n this.hideWrapper()\n })\n \n }\n \n showWrapper() {\n this.wrapperTarget.classList.remove(this.hiddenClass)\n }\n \n hideWrapper() {\n this.wrapperTarget.classList.add(this.hiddenClass)\n }\n}","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n label: String,\n labelAlternate: String,\n useAlternate: Boolean,\n }\n\n connect() {\n this.updateLabel()\n }\n\n toggle(event) {\n if (undefined !== event?.detail?.useAlternate) {\n this.useAlternateValue = event.detail.useAlternate\n } else {\n this.useAlternateValue = !this.useAlternateValue\n }\n }\n\n useAlternateValueChanged() {\n this.updateLabel()\n }\n\n updateLabel() {\n if (!this.hasLabelValue || !this.hasLabelAlternateValue || !this.hasUseAlternateValue) {\n return\n }\n\n this.element.textContent = this.useAlternateValue === true ? this.labelAlternateValue : this.labelValue\n }\n}\n","import { identifierForContextKey } from \"@hotwired/stimulus-webpack-helpers\"\n\nimport BulkActionFormController from './bulk_action_form_controller'\nimport BulkActionsController from './bulk_actions_controller'\nimport ClipboardController from './clipboard_controller'\nimport FormController from './form_controller'\nimport MobileMenuController from './mobile_menu_controller'\nimport TextToggleController from './text_toggle_controller'\n\nexport const controllerDefinitions = [\n [BulkActionFormController, 'bulk_action_form_controller.js'],\n [BulkActionsController, 'bulk_actions_controller.js'],\n [ClipboardController, 'clipboard_controller.js'],\n [FormController, 'form_controller.js'],\n [MobileMenuController, 'mobile_menu_controller.js'],\n [TextToggleController, 'text_toggle_controller.js'],\n].map(function(d) {\n const key = d[1]\n const controller = d[0]\n return {\n identifier: identifierForContextKey(key),\n controllerConstructor: controller\n }\n})\n","document.addEventListener(\"turbo:load\", () => {\n if (navigator.userAgent.toLocaleLowerCase().includes('electron')) {\n document.body.classList.add('electron')\n }\n})"],"names":["identifierForContextKey","key","logicalName","match","replace","_class","connect","this","updateAvailability","updateFormAndSubmit","event","recreateIdsHiddenFields","createOrUpdateAllField","updateIds","_event$detail","detail","ids","idsValue","allValue","all","updateButtonLabel","element","classList","toggle","hiddenClass","length","label","buttonIfAllValue","buttonIfIdsValue","buttonTarget","tagName","value","textContent","removeIdsHiddenFields","createIdsHiddenFields","idsHiddenFieldTargets","forEach","field","removeChild","id","document","createElement","type","name","objectNameValue","idsFieldNameValue","appendChild","hasAllHiddenFieldTarget","allHiddenFieldTarget","createAllField","allFieldNameValue","targets","classes","values","buttonIfAll","String","buttonIfIds","Array","Boolean","objectName","idsFieldName","allFieldName","Controller","add","selectableAvailableClass","toggleSelectable","selectableValue","updateSelectedIds","updateActions","updateSelectAllCheckbox","actionTargets","actionTarget","dispatchEvent","selectedIds","allSelected","selectAllOrNone","selectNone","selectAll","checkboxTargets","checkbox","checked","selectAllCheckboxTarget","selectAllLabelTarget","indeterminate","CustomEvent","useAlternate","selectableValueChanged","selectableClass","updateToggleLabel","selectableToggleTarget","push","selectable","copy","inputTarget","sourceTarget","innerText","select","execCommand","innerHTML","setTimeout","getElementById","removeTrailingNewlines","trixEditor","slice","removeTrailingWhitespace","resetOnSuccess","e","success","target","reset","stripTrix","trixFieldTargets","editor","parentElement","querySelector","submitOnReturn","metaKey","ctrlKey","keyCode","preventDefault","form","closest","submitForm","requestSubmit","click","async","transition","direction","animation","dataset","animationClass","charAt","toUpperCase","genesis","split","start","end","addClasses","Promise","resolve","requestAnimationFrame","removeClasses","computedDuration","getComputedStyle","transitionDuration","duration","Number","afterTransition","remove","open","showWrapper","revealableTargets","revealableTarget","transitionName","enter","close","map","leave","then","hideWrapper","wrapperTarget","updateLabel","useAlternateValue","undefined","useAlternateValueChanged","hasLabelValue","hasLabelAlternateValue","hasUseAlternateValue","labelAlternateValue","labelValue","labelAlternate","controllerDefinitions","BulkActionFormController","BulkActionsController","ClipboardController","FormController","MobileMenuController","TextToggleController","d","controller","identifier","controllerConstructor","addEventListener","navigator","userAgent","toLocaleLowerCase","includes","body"],"mappings":"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"}