satis 2.1.53 → 2.1.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 358cd4b72fd41d7611be2d1c2ddae7d64ee55ad9b9d68cd10169d30b9d241697
4
- data.tar.gz: 538a47d63390c4c94c7497d73d5c95b9aaaa9e1dc22a22ace1d071f8a1350a24
3
+ metadata.gz: 8e4087c76633f9b2a02fc083fd95c60a4a28d9677dbe44ee11735e4fa0e29810
4
+ data.tar.gz: 2ab23a72ad341b331ef4c8ea1fc1780d46e32d1657194a4a8d1fb7ca0193db37
5
5
  SHA512:
6
- metadata.gz: c0c085c46aee29463647e80eb10bff6beb997aadb25e9464ee86f4758d969a574241659918342ad1b4ea06a42e36edcec171ef0209b30607bc070e4739963f17
7
- data.tar.gz: 1966166a699aa72b262b909325569083365c63919bc238204a9b1c048d08b769366611e6e7a200d6ea1dfa9a507e53e1f683b7173c7df1b031520bf652e6b11b
6
+ metadata.gz: 609e9ac871cbfb81c23b4bad5a60822671d1b4e26132fe2ac219ac6185ef459e7a69a5ca8f28ece3eb911588e505d93fd0206bbf0f07f338d20a0783f2f6a54e
7
+ data.tar.gz: b572b8fcfb7ee048e40b729b3f56fa5f3b61cd97515b9ddce60c35c1a5a77f01544ca4b53f5f0fdc2a5fb335b3fc2969b02d71997f2d325b080aed3da7ca11b6
@@ -6,22 +6,20 @@ div.satis-date-time-picker data-controller="satis-date-time-picker" data-satis-d
6
6
  button.cursor-pointer.w-6.h-full.flex.items-center.text-gray-400.outline-none.focus:outline-none data-satis-date-time-picker-target="clearButton" data-action="click->satis-date-time-picker#clear"
7
7
  i.fas.fa-xmark
8
8
 
9
- .container.z-10.shadow.bg-white.border.border-gray-300.dark:bg-gray-800.dark:border-gray-700.rounded.p-4.w-72 class="#{inline ? 'inline-block' : 'hidden'}" data-satis-date-time-picker-target="calendarView"
9
+ .container.z-10.shadow.bg-white.border.border-gray-300.dark:bg-gray-800.dark:border-gray-700.rounded.p-4.w-96 class="#{inline ? 'inline-block' : 'hidden'}" data-satis-date-time-picker-target="calendarView"
10
10
  .flex.justify-between.items-center.mb-2
11
11
  div
12
- button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#previousYear"
13
- i.text-gray-500.inline-flex.py-1 class=Satis.config.icons[:previous_year]
14
12
  button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#previousMonth"
15
13
  i.text-gray-500.inline-flex.px-1.py-1 class=Satis.config.icons[:previous_month]
16
14
  div.text-center
17
15
  span.text-lg.font-bold.text-gray-800.dark:text-gray-200 data-satis-date-time-picker-target="month"
18
- span.ml-1.text-lg.text-gray-600.dark:text-gray-200.font-normal data-satis-date-time-picker-target="year"
16
+ select.appearance-none.border-none.bg-transparent.text-lg.font-normal.ml-1.text-gray-600.dark:text-gray-200.w-32.focus:outline-none.focus:ring-0 style="max-height: 50px; overflow-y:auto;" data-satis-date-time-picker-target="select" data-action="change->satis-date-time-picker#selectYear"
17
+ - current_year = Time.current.year
18
+ - (current_year - 100..current_year + 100).each do |year|
19
+ option value=year data-satis-date-time-picker-target="year" = year
19
20
  div
20
21
  button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#nextMonth"
21
22
  i.text-gray-500.inline-flex.px-1.py-1 class=Satis.config.icons[:next_month]
22
- button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#nextYear"
23
- i.text-gray-500.inline-flex.py-1 class=Satis.config.icons[:next_year]
24
-
25
23
 
26
24
  .grid.grid-cols-7 data-satis-date-time-picker-target="weekDays"
27
25
  template data-satis-date-time-picker-target="weekDayTemplate"
@@ -164,21 +164,17 @@ export default class DateTimePickerComponentController extends ApplicationContro
164
164
 
165
165
  previousMonth(event) {
166
166
  this.displayValue = new Date(new Date(this.displayValue).setMonth(this.displayValue.getMonth() - 1))
167
+
168
+ this.updateYear()
169
+
167
170
  this.refreshCalendar(false)
168
171
  }
169
172
 
170
173
  nextMonth(event) {
171
174
  this.displayValue = new Date(new Date(this.displayValue).setMonth(this.displayValue.getMonth() + 1))
172
- this.refreshCalendar(false)
173
- }
174
175
 
175
- previousYear(event) {
176
- this.displayValue = new Date(new Date(this.displayValue).setFullYear(this.displayValue.getFullYear() - 1))
177
- this.refreshCalendar(false)
178
- }
176
+ this.updateYear()
179
177
 
180
- nextYear(event) {
181
- this.displayValue = new Date(new Date(this.displayValue).setFullYear(this.displayValue.getFullYear() + 1))
182
178
  this.refreshCalendar(false)
183
179
  }
184
180
 
@@ -328,13 +324,17 @@ export default class DateTimePickerComponentController extends ApplicationContro
328
324
  return false
329
325
  }
330
326
  selectedDate.setMonth(this.displayValue.getMonth() - 1)
327
+ this.displayValue = selectedDate
331
328
  } else if (dayType === "next") {
332
329
  if (this.rangeValue) {
333
330
  return false
334
331
  }
335
332
  selectedDate.setMonth(this.displayValue.getMonth() + 1)
333
+ this.displayValue = selectedDate
336
334
  }
337
335
 
336
+ this.updateYear()
337
+
338
338
  selectedDate.setDate(+event.target.innerText)
339
339
 
340
340
  if (!this.rangeValue && !this.multipleValue) {
@@ -559,7 +559,6 @@ export default class DateTimePickerComponentController extends ApplicationContro
559
559
  })
560
560
  }
561
561
  }
562
-
563
562
  // Format the given Date into an ISO8601 string whilst preserving the given timezone
564
563
  iso8601(date) {
565
564
  let tzo = -date.getTimezoneOffset(),
@@ -667,4 +666,25 @@ export default class DateTimePickerComponentController extends ApplicationContro
667
666
 
668
667
  return results
669
668
  }
669
+
670
+ selectYear(event) {
671
+ let selectedYear = Number(event.target.value)
672
+ this.displayValue.setFullYear(selectedYear)
673
+ this.refreshCalendar(false)
674
+
675
+ if (!this.rangeValue && !this.multipleValue) {
676
+ this.selectedValue[0] = new Date(this.displayValue)
677
+ } else if(this.rangeValue && this.selectedValue.length == 2){
678
+ return false
679
+ }
680
+
681
+ this.refreshInputs()
682
+ event.cancelBubble = true
683
+ }
684
+
685
+ updateYear() {
686
+ const yearSelect = document.querySelector('[data-satis-date-time-picker-target="select"]')
687
+ const newYear = this.displayValue.getFullYear()
688
+ yearSelect.value = newYear
689
+ }
670
690
  }
@@ -0,0 +1,8 @@
1
+ div data-controller="satis-signature" data-satis-signature-url-value=(form.object.send(attribute).attached? ? helpers.main_app.url_for(form.object.send(attribute)) : nil)
2
+ canvas.border-solid.border.rounded-md data-satis-signature-target="canvas" data-satis-signature-action="change->satis-signature#update" width="600" height="200"
3
+ .button
4
+ i.fal.fa-trash data-action='click->satis-signature#clear' class='cursor-pointer'
5
+ |<
6
+ .button
7
+ i.fal.fa-undo data-action='click->satis-signature#undo' class='cursor-pointer'
8
+ = form.file_field :signature, data: { "satis-signature-target" => 'file' }, class: 'hidden'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Satis
4
+ module Signature
5
+ class Component < ViewComponent::Base
6
+ attr_reader :url, :form, :attribute, :title, :options
7
+
8
+ def initialize(form:, attribute:, **options, &block)
9
+ @form = form
10
+ @attribute = attribute
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ import ApplicationController from "satis/controllers/application_controller"
2
+
3
+ import SignaturePad from "signature_pad"
4
+
5
+ export default class SignatureComponentController extends ApplicationController {
6
+ static targets = ["canvas", "file"]
7
+ static values = { url: String }
8
+
9
+ async connect() {
10
+ this.signaturePad = new SignaturePad(this.canvasTarget)
11
+ if (this.hasUrlValue) {
12
+ let blob = await fetch(this.urlValue)
13
+ .then(r => r.blob())
14
+
15
+
16
+ let dataUrl = await new Promise(resolve => {
17
+ let reader = new FileReader()
18
+ reader.onload = () => resolve(reader.result)
19
+ reader.readAsDataURL(blob)
20
+ })
21
+ const dpi = window.devicePixelRatio
22
+ window.devicePixelRatio = 1
23
+ this.signaturePad.fromDataURL(dataUrl)
24
+ const file = this.dataURLtoFile(dataUrl, "signature.svg")
25
+ window.devicePixelRatio = dpi
26
+ this.signaturePad.addEventListener("beginStroke", this.clear.bind(this), { once: true })
27
+ this.persistFile(file)
28
+ }
29
+ this.signaturePad.addEventListener("endStroke", this.persist.bind(this))
30
+ }
31
+
32
+ clear(event) {
33
+ this.signaturePad.clear()
34
+ this.fileTarget.files = new DataTransfer().files
35
+ }
36
+
37
+ undo(event) {
38
+ event.preventDefault()
39
+ const data = this.signaturePad.toData()
40
+ if (data) {
41
+ data.pop()
42
+ this.signaturePad.fromData(data)
43
+ }
44
+ }
45
+
46
+ persist(event) {
47
+ const dpi = window.devicePixelRatio
48
+ const dataUrl = this.signaturePad.toSVG()
49
+ window.devicePixelRatio = 1
50
+ const file = new File([this.signaturePad?.toSVG()], "signature.svg", { type: "image/svg+xml" })
51
+ window.devicePixelRatio = dpi
52
+ this.persistFile(file)
53
+ }
54
+
55
+ persistFile(file) {
56
+ const dt = new DataTransfer()
57
+ dt.items.add(file)
58
+ this.fileTarget.files = dt.files
59
+ }
60
+
61
+ // HELPER
62
+
63
+ dataURLtoFile(dataurl, filename) {
64
+ var arr = dataurl.split(","),
65
+ mime = arr[0].match(/:(.*?);/)[1],
66
+ bstr = atob(arr[arr.length - 1]),
67
+ n = bstr.length,
68
+ u8arr = new Uint8Array(n);
69
+ while (n--) {
70
+ u8arr[n] = bstr.charCodeAt(n);
71
+ }
72
+ return new File([u8arr], filename, { type: mime });
73
+ }
74
+ }
@@ -48,6 +48,9 @@ application.register("satis-dialog", DialogComponentController);
48
48
  import ColorPickerComponentController from "satis/components/color_picker/component_controller";
49
49
  application.register("satis-color-picker", ColorPickerComponentController);
50
50
 
51
+ import SignatureComponentController from "satis/components/signature/component_controller";
52
+ application.register("satis-signature", SignatureComponentController);
53
+
51
54
  // Controllers
52
55
 
53
56
  import LinkController from "satis/controllers/link_controller";
data/config/importmap.rb CHANGED
@@ -52,3 +52,4 @@ pin "dayjs" # @1.11.13
52
52
  pin "dayjs/plugin/customParseFormat", to: "dayjs--plugin--customParseFormat.js" # @1.11.13
53
53
  pin "dayjs/plugin/localizedFormat", to: "dayjs--plugin--localizedFormat.js" # @1.11.13
54
54
  pin "dayjs/plugin/utc", to: "dayjs--plugin--utc.js" # @1.11.13
55
+ pin "signature_pad" # @5.0.4
@@ -34,10 +34,8 @@ module Satis
34
34
  option :current_user, default: lambda {}
35
35
 
36
36
  option :icons, default: {
37
- previous_year: "fa-solid fa-angle-double-left",
38
37
  previous_month: "fa-solid fa-angle-left",
39
- next_month: "fa-solid fa-angle-right",
40
- next_year: "fa-solid fa-angle-double-right"
38
+ next_month: "fa-solid fa-angle-right"
41
39
  }
42
40
 
43
41
  option(:default_help_text, default: lambda do |template, object, key, additional_scope|
@@ -328,31 +328,15 @@ module Satis
328
328
  @template.render(Satis::Editor::Component.new(form: self, attribute: method, **options, &block))
329
329
  ]
330
330
  end
331
- # form_group(method, options) do
332
- # safe_join [
333
- # (custom_label(method, options[:label], options) unless options[:label] == false),
334
- # tag.div(text_area(method,
335
- # merge_input_options({
336
- # class: 'form-control hidden',
337
- # data: {
338
- # # controller: 'satis-editor',
339
- # 'satis-editor-target' => 'textarea',
340
- # 'satis-editor-read-only-value' => options.delete(:read_only) || false,
341
- # 'satis-editor-mode-value' => options.delete(:mode) || 'text/html',
342
- # 'satis-editor-height-value' => options.delete(:height) || '200px',
343
- # 'satis-editor-color-scheme-value' => options.delete(:color_scheme),
344
- # 'satis-editor-color-scheme-dark-value' => options.delete(:color_scheme_dark) || 'lucario'
345
- # }
346
- #
347
- # }, options[:input_html])), class: "editor #{
348
- # if has_error?(method)
349
- # 'is-invalid'
350
- # end}", data: {
351
- # controller: 'satis-editor'
352
- # }),
353
- # hint_text(options[:hint] || '⌘-F/⌃-f: search; ⌥-g: goto line, ⌃-space: autocomplete')
354
- # ]
355
- # end
331
+ end
332
+
333
+ def signature(method, options = {}, &block)
334
+ form_group(method, options) do
335
+ safe_join [
336
+ (custom_label(method, options[:label], options) unless options[:label] == false),
337
+ @template.render(Satis::Signature::Component.new(form: self, attribute: method, **options, &block))
338
+ ]
339
+ end
356
340
  end
357
341
 
358
342
  def boolean_input(method, options = {})
data/lib/satis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Satis
2
- VERSION = "2.1.53"
2
+ VERSION = "2.1.54"
3
3
  end
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
3
+ * Original file: /npm/signature_pad@5.0.4/dist/signature_pad.js
4
+ *
5
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
+ */
7
+ /*!
8
+ * Signature Pad v5.0.4 | https://github.com/szimek/signature_pad
9
+ * (c) 2024 Szymon Nowak | Released under the MIT license
10
+ */
11
+ class t { constructor(t, e, i, n) { if (isNaN(t) || isNaN(e)) throw new Error(`Point is invalid: (${t}, ${e})`); this.x = +t, this.y = +e, this.pressure = i || 0, this.time = n || Date.now() } distanceTo(t) { return Math.sqrt(Math.pow(this.x - t.x, 2) + Math.pow(this.y - t.y, 2)) } equals(t) { return this.x === t.x && this.y === t.y && this.pressure === t.pressure && this.time === t.time } velocityFrom(t) { return this.time !== t.time ? this.distanceTo(t) / (this.time - t.time) : 0 } } class e { static fromPoints(t, i) { const n = this.calculateControlPoints(t[0], t[1], t[2]).c2, s = this.calculateControlPoints(t[1], t[2], t[3]).c1; return new e(t[1], n, s, t[2], i.start, i.end) } static calculateControlPoints(e, i, n) { const s = e.x - i.x, o = e.y - i.y, r = i.x - n.x, h = i.y - n.y, a = (e.x + i.x) / 2, c = (e.y + i.y) / 2, d = (i.x + n.x) / 2, l = (i.y + n.y) / 2, u = Math.sqrt(s * s + o * o), v = Math.sqrt(r * r + h * h), _ = u + v == 0 ? 0 : v / (u + v), p = d + (a - d) * _, m = l + (c - l) * _, g = i.x - p, w = i.y - m; return { c1: new t(a + g, c + w), c2: new t(d + g, l + w) } } constructor(t, e, i, n, s, o) { this.startPoint = t, this.control2 = e, this.control1 = i, this.endPoint = n, this.startWidth = s, this.endWidth = o } length() { let t, e, i = 0; for (let n = 0; n <= 10; n += 1) { const s = n / 10, o = this.point(s, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x), r = this.point(s, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y); if (n > 0) { const n = o - t, s = r - e; i += Math.sqrt(n * n + s * s) } t = o, e = r } return i } point(t, e, i, n, s) { return e * (1 - t) * (1 - t) * (1 - t) + 3 * i * (1 - t) * (1 - t) * t + 3 * n * (1 - t) * t * t + s * t * t * t } } class i { constructor() { try { this._et = new EventTarget } catch (t) { this._et = document } } addEventListener(t, e, i) { this._et.addEventListener(t, e, i) } dispatchEvent(t) { return this._et.dispatchEvent(t) } removeEventListener(t, e, i) { this._et.removeEventListener(t, e, i) } } class n extends i { constructor(t, e = {}) { var i, s, o; super(), this.canvas = t, this._drawingStroke = !1, this._isEmpty = !0, this._lastPoints = [], this._data = [], this._lastVelocity = 0, this._lastWidth = 0, this._handleMouseDown = t => { this._isLeftButtonPressed(t, !0) && !this._drawingStroke && this._strokeBegin(this._pointerEventToSignatureEvent(t)) }, this._handleMouseMove = t => { this._isLeftButtonPressed(t, !0) && this._drawingStroke ? this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t)) : this._strokeEnd(this._pointerEventToSignatureEvent(t), !1) }, this._handleMouseUp = t => { this._isLeftButtonPressed(t) || this._strokeEnd(this._pointerEventToSignatureEvent(t)) }, this._handleTouchStart = t => { 1 !== t.targetTouches.length || this._drawingStroke || (t.cancelable && t.preventDefault(), this._strokeBegin(this._touchEventToSignatureEvent(t))) }, this._handleTouchMove = t => { 1 === t.targetTouches.length && (t.cancelable && t.preventDefault(), this._drawingStroke ? this._strokeMoveUpdate(this._touchEventToSignatureEvent(t)) : this._strokeEnd(this._touchEventToSignatureEvent(t), !1)) }, this._handleTouchEnd = t => { 0 === t.targetTouches.length && (t.cancelable && t.preventDefault(), this.canvas.removeEventListener("touchmove", this._handleTouchMove), this._strokeEnd(this._touchEventToSignatureEvent(t))) }, this._handlePointerDown = t => { t.isPrimary && this._isLeftButtonPressed(t) && !this._drawingStroke && (t.preventDefault(), this._strokeBegin(this._pointerEventToSignatureEvent(t))) }, this._handlePointerMove = t => { t.isPrimary && (this._isLeftButtonPressed(t, !0) && this._drawingStroke ? (t.preventDefault(), this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t))) : this._strokeEnd(this._pointerEventToSignatureEvent(t), !1)) }, this._handlePointerUp = t => { t.isPrimary && !this._isLeftButtonPressed(t) && (t.preventDefault(), this._strokeEnd(this._pointerEventToSignatureEvent(t))) }, this.velocityFilterWeight = e.velocityFilterWeight || .7, this.minWidth = e.minWidth || .5, this.maxWidth = e.maxWidth || 2.5, this.throttle = null !== (i = e.throttle) && void 0 !== i ? i : 16, this.minDistance = null !== (s = e.minDistance) && void 0 !== s ? s : 5, this.dotSize = e.dotSize || 0, this.penColor = e.penColor || "black", this.backgroundColor = e.backgroundColor || "rgba(0,0,0,0)", this.compositeOperation = e.compositeOperation || "source-over", this.canvasContextOptions = null !== (o = e.canvasContextOptions) && void 0 !== o ? o : {}, this._strokeMoveUpdate = this.throttle ? function (t, e = 250) { let i, n, s, o = 0, r = null; const h = () => { o = Date.now(), r = null, i = t.apply(n, s), r || (n = null, s = []) }; return function (...a) { const c = Date.now(), d = e - (c - o); return n = this, s = a, d <= 0 || d > e ? (r && (clearTimeout(r), r = null), o = c, i = t.apply(n, s), r || (n = null, s = [])) : r || (r = window.setTimeout(h, d)), i } }(n.prototype._strokeUpdate, this.throttle) : n.prototype._strokeUpdate, this._ctx = t.getContext("2d", this.canvasContextOptions), this.clear(), this.on() } clear() { const { _ctx: t, canvas: e } = this; t.fillStyle = this.backgroundColor, t.clearRect(0, 0, e.width, e.height), t.fillRect(0, 0, e.width, e.height), this._data = [], this._reset(this._getPointGroupOptions()), this._isEmpty = !0 } fromDataURL(t, e = {}) { return new Promise(((i, n) => { const s = new Image, o = e.ratio || window.devicePixelRatio || 1, r = e.width || this.canvas.width / o, h = e.height || this.canvas.height / o, a = e.xOffset || 0, c = e.yOffset || 0; this._reset(this._getPointGroupOptions()), s.onload = () => { this._ctx.drawImage(s, a, c, r, h), i() }, s.onerror = t => { n(t) }, s.crossOrigin = "anonymous", s.src = t, this._isEmpty = !1 })) } toDataURL(t = "image/png", e) { return "image/svg+xml" === t ? ("object" != typeof e && (e = void 0), `data:image/svg+xml;base64,${btoa(this.toSVG(e))}`) : ("number" != typeof e && (e = void 0), this.canvas.toDataURL(t, e)) } on() { this.canvas.style.touchAction = "none", this.canvas.style.msTouchAction = "none", this.canvas.style.userSelect = "none"; const t = /Macintosh/.test(navigator.userAgent) && "ontouchstart" in document; window.PointerEvent && !t ? this._handlePointerEvents() : (this._handleMouseEvents(), "ontouchstart" in window && this._handleTouchEvents()) } off() { this.canvas.style.touchAction = "auto", this.canvas.style.msTouchAction = "auto", this.canvas.style.userSelect = "auto", this.canvas.removeEventListener("pointerdown", this._handlePointerDown), this.canvas.removeEventListener("mousedown", this._handleMouseDown), this.canvas.removeEventListener("touchstart", this._handleTouchStart), this._removeMoveUpEventListeners() } _getListenerFunctions() { var t; const e = window.document === this.canvas.ownerDocument ? window : null !== (t = this.canvas.ownerDocument.defaultView) && void 0 !== t ? t : this.canvas.ownerDocument; return { addEventListener: e.addEventListener.bind(e), removeEventListener: e.removeEventListener.bind(e) } } _removeMoveUpEventListeners() { const { removeEventListener: t } = this._getListenerFunctions(); t("pointermove", this._handlePointerMove), t("pointerup", this._handlePointerUp), t("mousemove", this._handleMouseMove), t("mouseup", this._handleMouseUp), t("touchmove", this._handleTouchMove), t("touchend", this._handleTouchEnd) } isEmpty() { return this._isEmpty } fromData(t, { clear: e = !0 } = {}) { e && this.clear(), this._fromData(t, this._drawCurve.bind(this), this._drawDot.bind(this)), this._data = this._data.concat(t) } toData() { return this._data } _isLeftButtonPressed(t, e) { return e ? 1 === t.buttons : 1 == (1 & t.buttons) } _pointerEventToSignatureEvent(t) { return { event: t, type: t.type, x: t.clientX, y: t.clientY, pressure: "pressure" in t ? t.pressure : 0 } } _touchEventToSignatureEvent(t) { const e = t.changedTouches[0]; return { event: t, type: t.type, x: e.clientX, y: e.clientY, pressure: e.force } } _getPointGroupOptions(t) { return { penColor: t && "penColor" in t ? t.penColor : this.penColor, dotSize: t && "dotSize" in t ? t.dotSize : this.dotSize, minWidth: t && "minWidth" in t ? t.minWidth : this.minWidth, maxWidth: t && "maxWidth" in t ? t.maxWidth : this.maxWidth, velocityFilterWeight: t && "velocityFilterWeight" in t ? t.velocityFilterWeight : this.velocityFilterWeight, compositeOperation: t && "compositeOperation" in t ? t.compositeOperation : this.compositeOperation } } _strokeBegin(t) { if (!this.dispatchEvent(new CustomEvent("beginStroke", { detail: t, cancelable: !0 }))) return; const { addEventListener: e } = this._getListenerFunctions(); switch (t.event.type) { case "mousedown": e("mousemove", this._handleMouseMove), e("mouseup", this._handleMouseUp); break; case "touchstart": e("touchmove", this._handleTouchMove), e("touchend", this._handleTouchEnd); break; case "pointerdown": e("pointermove", this._handlePointerMove), e("pointerup", this._handlePointerUp) }this._drawingStroke = !0; const i = this._getPointGroupOptions(), n = Object.assign(Object.assign({}, i), { points: [] }); this._data.push(n), this._reset(i), this._strokeUpdate(t) } _strokeUpdate(t) { if (!this._drawingStroke) return; if (0 === this._data.length) return void this._strokeBegin(t); this.dispatchEvent(new CustomEvent("beforeUpdateStroke", { detail: t })); const e = this._createPoint(t.x, t.y, t.pressure), i = this._data[this._data.length - 1], n = i.points, s = n.length > 0 && n[n.length - 1], o = !!s && e.distanceTo(s) <= this.minDistance, r = this._getPointGroupOptions(i); if (!s || !s || !o) { const t = this._addPoint(e, r); s ? t && this._drawCurve(t, r) : this._drawDot(e, r), n.push({ time: e.time, x: e.x, y: e.y, pressure: e.pressure }) } this.dispatchEvent(new CustomEvent("afterUpdateStroke", { detail: t })) } _strokeEnd(t, e = !0) { this._removeMoveUpEventListeners(), this._drawingStroke && (e && this._strokeUpdate(t), this._drawingStroke = !1, this.dispatchEvent(new CustomEvent("endStroke", { detail: t }))) } _handlePointerEvents() { this._drawingStroke = !1, this.canvas.addEventListener("pointerdown", this._handlePointerDown) } _handleMouseEvents() { this._drawingStroke = !1, this.canvas.addEventListener("mousedown", this._handleMouseDown) } _handleTouchEvents() { this.canvas.addEventListener("touchstart", this._handleTouchStart) } _reset(t) { this._lastPoints = [], this._lastVelocity = 0, this._lastWidth = (t.minWidth + t.maxWidth) / 2, this._ctx.fillStyle = t.penColor, this._ctx.globalCompositeOperation = t.compositeOperation } _createPoint(e, i, n) { const s = this.canvas.getBoundingClientRect(); return new t(e - s.left, i - s.top, n, (new Date).getTime()) } _addPoint(t, i) { const { _lastPoints: n } = this; if (n.push(t), n.length > 2) { 3 === n.length && n.unshift(n[0]); const t = this._calculateCurveWidths(n[1], n[2], i), s = e.fromPoints(n, t); return n.shift(), s } return null } _calculateCurveWidths(t, e, i) { const n = i.velocityFilterWeight * e.velocityFrom(t) + (1 - i.velocityFilterWeight) * this._lastVelocity, s = this._strokeWidth(n, i), o = { end: s, start: this._lastWidth }; return this._lastVelocity = n, this._lastWidth = s, o } _strokeWidth(t, e) { return Math.max(e.maxWidth / (t + 1), e.minWidth) } _drawCurveSegment(t, e, i) { const n = this._ctx; n.moveTo(t, e), n.arc(t, e, i, 0, 2 * Math.PI, !1), this._isEmpty = !1 } _drawCurve(t, e) { const i = this._ctx, n = t.endWidth - t.startWidth, s = 2 * Math.ceil(t.length()); i.beginPath(), i.fillStyle = e.penColor; for (let i = 0; i < s; i += 1) { const o = i / s, r = o * o, h = r * o, a = 1 - o, c = a * a, d = c * a; let l = d * t.startPoint.x; l += 3 * c * o * t.control1.x, l += 3 * a * r * t.control2.x, l += h * t.endPoint.x; let u = d * t.startPoint.y; u += 3 * c * o * t.control1.y, u += 3 * a * r * t.control2.y, u += h * t.endPoint.y; const v = Math.min(t.startWidth + h * n, e.maxWidth); this._drawCurveSegment(l, u, v) } i.closePath(), i.fill() } _drawDot(t, e) { const i = this._ctx, n = e.dotSize > 0 ? e.dotSize : (e.minWidth + e.maxWidth) / 2; i.beginPath(), this._drawCurveSegment(t.x, t.y, n), i.closePath(), i.fillStyle = e.penColor, i.fill() } _fromData(e, i, n) { for (const s of e) { const { points: e } = s, o = this._getPointGroupOptions(s); if (e.length > 1) for (let n = 0; n < e.length; n += 1) { const s = e[n], r = new t(s.x, s.y, s.pressure, s.time); 0 === n && this._reset(o); const h = this._addPoint(r, o); h && i(h, o) } else this._reset(o), n(e[0], o) } } toSVG({ includeBackgroundColor: t = !1 } = {}) { const e = this._data, i = Math.max(window.devicePixelRatio || 1, 1), n = this.canvas.width / i, s = this.canvas.height / i, o = document.createElementNS("http://www.w3.org/2000/svg", "svg"); if (o.setAttribute("xmlns", "http://www.w3.org/2000/svg"), o.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"), o.setAttribute("viewBox", `0 0 ${n} ${s}`), o.setAttribute("width", n.toString()), o.setAttribute("height", s.toString()), t && this.backgroundColor) { const t = document.createElement("rect"); t.setAttribute("width", "100%"), t.setAttribute("height", "100%"), t.setAttribute("fill", this.backgroundColor), o.appendChild(t) } return this._fromData(e, ((t, { penColor: e }) => { const i = document.createElement("path"); if (!(isNaN(t.control1.x) || isNaN(t.control1.y) || isNaN(t.control2.x) || isNaN(t.control2.y))) { const n = `M ${t.startPoint.x.toFixed(3)},${t.startPoint.y.toFixed(3)} C ${t.control1.x.toFixed(3)},${t.control1.y.toFixed(3)} ${t.control2.x.toFixed(3)},${t.control2.y.toFixed(3)} ${t.endPoint.x.toFixed(3)},${t.endPoint.y.toFixed(3)}`; i.setAttribute("d", n), i.setAttribute("stroke-width", (2.25 * t.endWidth).toFixed(3)), i.setAttribute("stroke", e), i.setAttribute("fill", "none"), i.setAttribute("stroke-linecap", "round"), o.appendChild(i) } }), ((t, { penColor: e, dotSize: i, minWidth: n, maxWidth: s }) => { const r = document.createElement("circle"), h = i > 0 ? i : (n + s) / 2; r.setAttribute("r", h.toString()), r.setAttribute("cx", t.x.toString()), r.setAttribute("cy", t.y.toString()), r.setAttribute("fill", e), o.appendChild(r) })), o.outerHTML } } export { n as default };
12
+ //# sourceMappingURL=/sm/fcb20b2f86ed10615b8b8c6a0c684f42d4284af51d740bacacf53d4ccdbe767b.map
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: satis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.53
4
+ version: 2.1.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-24 00:00:00.000000000 Z
11
+ date: 2025-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser
@@ -847,6 +847,9 @@ files:
847
847
  - app/components/satis/sidebar_menu_item/component_controller.js
848
848
  - app/components/satis/sidebar_menu_item/mobile/component.html.slim
849
849
  - app/components/satis/sidebar_menu_item/mobile/component.rb
850
+ - app/components/satis/signature/component.html.slim
851
+ - app/components/satis/signature/component.rb
852
+ - app/components/satis/signature/component_controller.js
850
853
  - app/components/satis/switch/component.html.slim
851
854
  - app/components/satis/switch/component.rb
852
855
  - app/components/satis/switch/component_controller.js
@@ -968,6 +971,7 @@ files:
968
971
  - vendor/javascript/leaflet.js
969
972
  - vendor/javascript/pickr.es5.min.js
970
973
  - vendor/javascript/popper.js.js
974
+ - vendor/javascript/signature_pad.js
971
975
  - vendor/javascript/sortablejs.js
972
976
  - vendor/javascript/style-mod.js
973
977
  - vendor/javascript/tippy.js.js