cdek 0.3.13 → 0.3.16

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: ad8868fd8f6e296c1439f90772bf3c16e724d551ed095d6627250b8182bd486c
4
- data.tar.gz: 29cabd18b3071e083f7ff8964bf8005f218da9985e35f6bcc80f030ac2677924
3
+ metadata.gz: 1dd8710b9122c71addd999aab3d16759f3a919916a11e3d1715d88483d6aa21b
4
+ data.tar.gz: 50b97380e716f8b23c4d75165552caff3c93856b814a3f64b2ac725e6a61ff02
5
5
  SHA512:
6
- metadata.gz: bb520764c8df5cfa6b6b012d5e28e219c0a664124caaa07f58ce46ed7a56d079290d7bb8bf1e7f27c02805fd7faca35b051b4df60e3fb94a40112066ad258bf8
7
- data.tar.gz: 9f832dae5fbb1443e52a9fb4f621b3a8052d308cc88d05d08089e6f2e423f27c815f4e9ecaac3f1c153ce828e470ee5947d528175b4d83c20f7b70eb05bd71ee
6
+ metadata.gz: b4adea92e439961fce478880f1792bb9790809bdf1073e3bc1ba45a4468652558f59278bae279bd0bf40643876bcf9ea96e14f9c6c7e4f779d150baa299b444b
7
+ data.tar.gz: be21d7a9b78b4bdefc1c624742b6a52e5de70228b93497571731ff714379f936f035792fa5dad2daa7963096195eab58e7bcdb86a54add254df46f24e3dde87d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.16
4
+
5
+ * Полная изоляция инстансов виджета между открытиями карты. Singleton-гард
6
+ 0.3.14 (один живой инстанс) оказался недостаточным: `@cdek-it/widget@3`
7
+ держит ГЛОБАЛЬНЫЙ pinia-стор на уровне модуля, а `destroy()` размонтирует
8
+ Vue-приложение, но не очищает этот стор и не снимает подписки, заведённые
9
+ в бутстрапе виджета (вне дерева компонентов). После смены города
10
+ (пересоздания модалки) подписки уничтоженного инстанса оставались на общем
11
+ сторе и, реагируя на запись нового инстанса, перезапрашивали офисы и
12
+ пере-центрировали карту на ПРЕЖНИЙ город — точки нового города уходили
13
+ «за границы» и пропадали навсегда (в server-логах — `widget_service` по
14
+ старому городу сразу после нового).
15
+ * Решение: шаблон `cdek_widget_controller.js` теперь перед каждым монтажом
16
+ удаляет `window.CDEKWidget` и заново выполняет бандл, поэтому каждый виджет
17
+ получает свежий модуль со своим стором, а подписки прежнего инстанса висят
18
+ на осиротевшем сторе и на новый виджет не влияют. Повторное выполнение
19
+ бандла безопасно: Yandex Maps (ymaps3) грузится идемпотентно, дублирование
20
+ `<style>` безвредно, custom elements не регистрируются. Прежний
21
+ `ensureWidgetScript` (один общий `_scriptPromise`) заменён на
22
+ `loadFreshWidgetScript`.
23
+ * Шаблон генератора снова пишет координаты выбранного ПВЗ (`office.location`
24
+ → строка `"lon lat"`) в скрытое поле `order_cdek_point_coors`. Поддержка
25
+ `fieldCoors` была в host-копиях ≤0.3.12, но потерялась при переносе фиксов
26
+ 0.3.13/0.3.14 в шаблон — из-за этого после синхронизации контроллера
27
+ хост-приложение переставало получать координаты пункта и не могло
28
+ отрисовать карту заказа. Значение `fieldCoors` (по умолчанию
29
+ `order_cdek_point_coors`) и его data-атрибут `cdek_widget_field_coors_value`
30
+ добавлены и в шаблон, и в `cdek_widget_tag` (новый необязательный параметр
31
+ `field_coors:`), консистентно с остальными `field_*`.
32
+ * Шаблон: после асинхронной загрузки скрипта виджета монтирование
33
+ выполняется только если контроллер ещё подключён
34
+ (`if (this._connected) this._mountWidget()`). Раньше промис загрузки мог
35
+ доехать уже после `disconnect()` (быстрое закрытие/переоткрытие модалки),
36
+ и виджет монтировался в уже отсоединённый узел.
37
+ * Совокупно с 0.3.13 (центрирование карты по координатам вместо асинхронного
38
+ геокодинга названия) и 0.3.14 (ровно один живой инстанс) это полностью
39
+ убирает «мелькание» и пропадание точек ПВЗ при открытии карты после смены
40
+ города. Хост-приложениям, сгенерировавшим контроллер на ≤0.3.15, нужно
41
+ перегенерировать его (`bin/rails generate cdek:install`) или внести правку
42
+ вручную из шаблона.
43
+
44
+ ## 0.3.14
45
+
46
+ * Шаблон генератора `cdek_widget_controller.js` теперь держит ровно один
47
+ живой инстанс виджета на странице: предыдущий уничтожается синхронно
48
+ перед монтированием нового. `@cdek-it/widget` — Vue-приложение с
49
+ глобальным стором, поэтому два одновременно живых инстанса делили стор
50
+ и конфликтовали — при смене города (пересоздание модалки) старый инстанс
51
+ перезапрашивал офисы и пере-центрировал карту на прежний город (точки
52
+ выбранного города мелькали и пропадали). Добавлены module-level
53
+ `_activeWidget` и `destroyWidget()`; `disconnect` и `_mountWidget`
54
+ переведены на них. Хост-приложениям, сгенерировавшим контроллер на
55
+ ≤0.3.13, нужно перегенерировать его (`bin/rails generate cdek:install`)
56
+ или внести правку вручную.
57
+
3
58
  ## 0.3.13
4
59
 
5
60
  * Виджет ПВЗ больше не откатывает центр карты на Москву при выборе
data/README.md CHANGED
@@ -115,9 +115,11 @@ suggestions = Cdek.city_suggestions("мос")
115
115
  * `#order_cdek_point_name`
116
116
  * `#order_cdek_point_address`
117
117
  * `#order_cdek_city_code`
118
+ * `#order_cdek_point_coors` — координаты ПВЗ строкой `"долгота широта"`
119
+ (из `office.location`), чтобы хост мог отрисовать карту заказа.
118
120
 
119
121
  DOM-id переопределяются именованными аргументами `field_code`,
120
- `field_name`, `field_address`, `field_city_code` хелпера.
122
+ `field_name`, `field_address`, `field_city_code`, `field_coors` хелпера.
121
123
 
122
124
  5. Также диспатчится событие `cdek-widget:chosen` с `detail.office` —
123
125
  можно слушать в собственных Stimulus-контроллерах.
@@ -43,6 +43,7 @@ module Cdek
43
43
  field_name: "order_cdek_point_name",
44
44
  field_address: "order_cdek_point_address",
45
45
  field_city_code: "order_cdek_city_code",
46
+ field_coors: "order_cdek_point_coors",
46
47
  label_selector: "[data-cdek-widget-label]",
47
48
  address_selector: "#order_cdek_point_address_view",
48
49
  height: "600px")
@@ -62,6 +63,7 @@ module Cdek
62
63
  field_name: field_name,
63
64
  field_address: field_address,
64
65
  field_city_code: field_city_code,
66
+ field_coors: field_coors,
65
67
  label_selector: label_selector,
66
68
  address_selector: address_selector
67
69
  )
@@ -95,7 +97,7 @@ module Cdek
95
97
  def cdek_widget_data(api_key:, default_city:, default_coords:, sender_city:, sender_city_code:,
96
98
  goods_json:,
97
99
  service_path:, script_url:, modal_id:,
98
- field_code:, field_name:, field_address:, field_city_code:,
100
+ field_code:, field_name:, field_address:, field_city_code:, field_coors:,
99
101
  label_selector:, address_selector:)
100
102
  {
101
103
  controller: "cdek-widget",
@@ -112,6 +114,7 @@ module Cdek
112
114
  cdek_widget_field_name_value: field_name,
113
115
  cdek_widget_field_address_value: field_address,
114
116
  cdek_widget_field_city_code_value: field_city_code,
117
+ cdek_widget_field_coors_value: field_coors,
115
118
  cdek_widget_label_selector_value: label_selector,
116
119
  cdek_widget_address_selector_value: address_selector
117
120
  }
data/lib/cdek/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cdek
4
- VERSION = "0.3.13"
4
+ VERSION = "0.3.16"
5
5
  end
@@ -26,22 +26,27 @@ import { Controller } from "@hotwired/stimulus"
26
26
  // виджет будет открыт без расчётных packages, чтобы не считать доставку
27
27
  // по выдуманным размерам.
28
28
 
29
- let _scriptPromise = null
29
+ let _activeWidget = null
30
30
 
31
- function ensureWidgetScript(url) {
32
- if (typeof window.CDEKWidget !== "undefined") {
33
- return Promise.resolve()
34
- }
35
- if (_scriptPromise) return _scriptPromise
36
- _scriptPromise = new Promise((resolve, reject) => {
31
+ function destroyWidget(widget) {
32
+ if (!widget) return
33
+ try {
34
+ if (typeof widget.destroy === "function") widget.destroy()
35
+ else if (typeof widget.close === "function") widget.close()
36
+ } catch (_) { /* виджет мог не домонтироваться — это ок */ }
37
+ if (_activeWidget === widget) _activeWidget = null
38
+ }
39
+
40
+ function loadFreshWidgetScript(url) {
41
+ return new Promise((resolve, reject) => {
42
+ try { delete window.CDEKWidget } catch (_) { window.CDEKWidget = undefined }
37
43
  const s = document.createElement("script")
38
44
  s.src = url
39
45
  s.async = true
40
46
  s.onload = () => resolve()
41
- s.onerror = () => { _scriptPromise = null; reject(new Error("Не удалось загрузить скрипт виджета СДЭК")) }
47
+ s.onerror = () => reject(new Error("Не удалось загрузить скрипт виджета СДЭК"))
42
48
  document.head.appendChild(s)
43
49
  })
44
- return _scriptPromise
45
50
  }
46
51
 
47
52
  function setFieldValue(id, value) {
@@ -86,6 +91,7 @@ export default class extends Controller {
86
91
  fieldName: { type: String, default: "order_cdek_point_name" },
87
92
  fieldAddress: { type: String, default: "order_cdek_point_address" },
88
93
  fieldCityCode: { type: String, default: "order_cdek_city_code" },
94
+ fieldCoors: { type: String, default: "order_cdek_point_coors" },
89
95
  labelSelector: { type: String, default: "[data-cdek-widget-label]" },
90
96
  addressSelector: { type: String, default: "#order_cdek_point_address_view" }
91
97
  }
@@ -101,13 +107,8 @@ export default class extends Controller {
101
107
  window.clearTimeout(this._mountTimer)
102
108
  this._mountTimer = null
103
109
  }
104
- if (this._widget) {
105
- try {
106
- if (typeof this._widget.destroy === "function") this._widget.destroy()
107
- else if (typeof this._widget.close === "function") this._widget.close()
108
- } catch (_) { /* no-op */ }
109
- this._widget = null
110
- }
110
+ destroyWidget(this._widget)
111
+ this._widget = null
111
112
  }
112
113
 
113
114
  _mountWhenReady() {
@@ -118,8 +119,8 @@ export default class extends Controller {
118
119
  }
119
120
 
120
121
  this._mounting = true
121
- ensureWidgetScript(this.scriptUrlValue)
122
- .then(() => this._mountWidget())
122
+ loadFreshWidgetScript(this.scriptUrlValue)
123
+ .then(() => { if (this._connected) this._mountWidget() })
123
124
  .catch((err) => this._showError(err && err.message ? err.message : "ошибка"))
124
125
  .finally(() => { this._mounting = false })
125
126
  }
@@ -193,8 +194,13 @@ export default class extends Controller {
193
194
  widgetOptions.goods = goods
194
195
  }
195
196
 
197
+ // Гасим любой предыдущий инстанс ДО создания нового — иначе два виджета
198
+ // дерутся за глобальный стор (старый город перезапрашивается, карта прыгает).
199
+ destroyWidget(_activeWidget)
200
+
196
201
  try {
197
202
  this._widget = new window.CDEKWidget(widgetOptions)
203
+ _activeWidget = this._widget
198
204
  } catch (err) {
199
205
  this._showError(err && err.message ? err.message : "ошибка инициализации виджета")
200
206
  }
@@ -207,10 +213,16 @@ export default class extends Controller {
207
213
  const address = office.address || office.address_full || ""
208
214
  const cityCode = office.city_code || ""
209
215
 
216
+ const loc = Array.isArray(office.location) ? office.location : []
217
+ const lon = Number(loc[0])
218
+ const lat = Number(loc[1])
219
+ const coors = (Number.isFinite(lon) && Number.isFinite(lat)) ? `${lon} ${lat}` : ""
220
+
210
221
  setFieldValue(this.fieldCodeValue, code)
211
222
  setFieldValue(this.fieldNameValue, name)
212
223
  setFieldValue(this.fieldAddressValue, address)
213
224
  setFieldValue(this.fieldCityCodeValue, cityCode)
225
+ setFieldValue(this.fieldCoorsValue, coors)
214
226
 
215
227
  const label = name ? (code ? `${name} (${code})` : name) : "не выбран"
216
228
  setText(this.labelSelectorValue, label)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cdek
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.13
4
+ version: 0.3.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Korolyov