saby-customizer 0.0.0-pre.7 → 0.0.0

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.
@@ -0,0 +1,272 @@
1
+ import { oom } from '@notml/core'
2
+ import { getListOfOpenCards } from './edo.js'
3
+
4
+ const { MutationObserver } = window
5
+ /**
6
+ * @typedef ToolbarContainerOptions Опции по умолчанию для панели
7
+ * @property {boolean} global Признак глобальной панели
8
+ * @property {import('saby-customizer/lib.js').Card} [card] Ссылка на экземпляр карточки задачи
9
+ * @property {string} [position='right'] Положение панели на сайте right|top
10
+ */
11
+ /** @type {ToolbarContainerOptions} */
12
+ const optionsDefaults = { global: false, card: null, position: 'right' }
13
+
14
+ /** Класс контейнера для расширения панели сайта дополнительными кнопками */
15
+ class ToolbarContainer extends oom.extends(HTMLElement, optionsDefaults) {
16
+
17
+ static tagName = 'saby-customizer-toolbar'
18
+ static style = oom.style({
19
+ 'display': 'flex',
20
+ 'alignItems': 'center',
21
+ 'saby-customizer-toolbar.right': {
22
+ flexDirection: 'column',
23
+ marginBottom: '6px'
24
+ },
25
+ 'saby-customizer-toolbar.top': {
26
+ marginLeft: '16px'
27
+ },
28
+ '.button': {
29
+ padding: '2px',
30
+ color: 'var(--secondary_icon-color)',
31
+ fill: 'var(--secondary_icon-color)',
32
+ cursor: 'pointer',
33
+ display: 'flex',
34
+ justifyContent: 'center',
35
+ alignItems: 'center'
36
+ },
37
+ 'saby-customizer-toolbar.right .button': {
38
+ height: '32px',
39
+ width: '32px',
40
+ marginBottom: '4px'
41
+ },
42
+ 'saby-customizer-toolbar.top .button': {
43
+ height: '20px',
44
+ width: '20px',
45
+ marginRight: '8px'
46
+ },
47
+ 'saby-customizer-toolbar.top .button:last-child': {
48
+ marginRight: '0'
49
+ },
50
+ '.button:hover': {
51
+ color: 'var(--link_hover_text-color)',
52
+ fill: 'var(--link_hover_text-color)',
53
+ borderColor: ' var(--border-color_hover_button_toolButton)',
54
+ background: 'var(--background-color_hover_button_toolButton)',
55
+ borderRadius: 'var(--border-radius_button_toolButton)',
56
+ borderWidth: 'var(--border-thickness_button)'
57
+ }
58
+ })
59
+
60
+ /**
61
+ * @typedef ButtonOptionsFilter
62
+ * @property {Array<string>} [names] Фильтр по названию документа
63
+ */
64
+
65
+ /**
66
+ * @typedef ButtonOptions
67
+ * @property {string} [position='right'] Позиция панели в которой отображается кнопка
68
+ * @property {ButtonOptionsFilter} [filter] Фильтры отображения кнопок
69
+ * @property {string} title Всплывающая подсказка на кнопке
70
+ * @property {string} iconSVG Верстка иконки в формате SVG
71
+ * @property {Function} onclick Обработчик клика на иконку
72
+ */
73
+ /** @type {ButtonOptions[]} */
74
+ static buttons = []
75
+
76
+ /** @type {Set<ToolbarContainer>} */
77
+ static containers = new Set()
78
+
79
+ /** @type {ToolbarContainer} */
80
+ static globalContainer = null
81
+
82
+ /** @type {MutationObserver} */
83
+ static observer = null
84
+
85
+ /**
86
+ * Регистрирует новую общую кнопку в панели сайта
87
+ *
88
+ * @param {ButtonOptions} options Опции кнопки
89
+ */
90
+ static addButton(options) {
91
+ options = Object.assign({ position: 'right' }, options)
92
+ this.buttons.push(options)
93
+
94
+ this.containers.forEach(container => container.addButton(options))
95
+
96
+ if (!ToolbarContainer.globalContainer) {
97
+ this.addGlobalContainer()
98
+ }
99
+ if (!this.observer) {
100
+ this.addTaskPanelContainer()
101
+ this.registerTaskPanelWatcher()
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Добавляет в вертску сайта глобальный контейнер в панели для добавления кнопок
107
+ *
108
+ * @param {{searchCount:number}} options Опции регистарции обработчиков
109
+ */
110
+ static addGlobalContainer({ searchCount } = { searchCount: 0 }) {
111
+ if (!ToolbarContainer.globalContainer) {
112
+ const sabyPageRightPanel = document.querySelector('.sabyPage-MainLayout__rightPanel .sabyPage-MainLayout__wrapper') ||
113
+ document.querySelector('.sabyPage-MainLayout__mainContent .sabyPage-widgets__rightPanel__bottomButtons .sabyPage-widgets__wrapper')
114
+
115
+ if (sabyPageRightPanel) {
116
+ ToolbarContainer.globalContainer = new ToolbarContainer({ global: true })
117
+ sabyPageRightPanel.prepend(ToolbarContainer.globalContainer)
118
+ } else {
119
+ if (searchCount < 10) {
120
+ searchCount++
121
+ setTimeout(() => {
122
+ this.addGlobalContainer({ searchCount })
123
+ }, 1000)
124
+ } else {
125
+ console.warn('saby-customizer - addGlobalContainer: Не найден ни один общий контейнер для размещения доп. панелей с кнопками')
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Регистрирует обработчик для поиска открытых задач и добавления в них панели
133
+ *
134
+ * @param {{searchCount:number}} options Опции регистарции обработчиков
135
+ */
136
+ static registerTaskPanelWatcher({ searchCount } = { searchCount: 0 }) {
137
+ const popupContainer = document.getElementById('popup')
138
+ const pageEntityContainer = document.querySelector('.onlinePage_Entity-wrapper.controls-Popup__dialog-target-container')
139
+ let timeout = null
140
+
141
+ if (!this.observer) {
142
+ this.observer = new MutationObserver(() => {
143
+ if (!timeout) {
144
+ timeout = setTimeout(() => {
145
+ this.addTaskPanelContainer()
146
+ timeout = null
147
+ }, 1000)
148
+ }
149
+ })
150
+ }
151
+
152
+ if (popupContainer) {
153
+ this.observer.observe(popupContainer, { childList: true, subtree: true })
154
+ }
155
+ if (pageEntityContainer) {
156
+ this.observer.observe(pageEntityContainer, { childList: true, subtree: true })
157
+ }
158
+ if (!popupContainer && !pageEntityContainer) {
159
+ if (searchCount < 10) {
160
+ searchCount++
161
+ setTimeout(() => {
162
+ this.registerTaskPanelWatcher({ searchCount })
163
+ }, 1000)
164
+ } else {
165
+ console.error('saby-customizer - registerTaskPanelWatcher: Не найден ни один общий контейнер для размещения доп. панелей с кнопками')
166
+ }
167
+ }
168
+ }
169
+
170
+ /** Добавления во все найденные открытые задачи правую панель */
171
+ static addTaskPanelContainer() {
172
+ const openTaskList = getListOfOpenCards({ duplicates: true })
173
+
174
+ openTaskList.forEach(card => {
175
+ const rightPanel = card.element.querySelector('.controls-StackTemplate__rightPanel .sabyPage-MainLayout__wrapper') ||
176
+ // Кнопки для всплывающий панелей в карточке открытой в отдельной вкладке
177
+ card.element.querySelector('.controls-StackTemplate__rightPanel .sabyPage-widgets__wrapper')
178
+ const topPanel = card.element.querySelector('.edo3-Dialog__head-firstLine-buttons')
179
+
180
+ if (rightPanel) {
181
+ const rightSidebar = rightPanel.querySelector('saby-customizer-toolbar')
182
+
183
+ if (!rightSidebar) {
184
+ rightPanel.prepend(new ToolbarContainer())
185
+ }
186
+ }
187
+ if (topPanel) {
188
+ /** @type {ToolbarContainer} */
189
+ let topSidebar = topPanel.querySelector('saby-customizer-toolbar')
190
+
191
+ if (topSidebar && topSidebar.options?.card.data.id !== card.data.id) {
192
+ topSidebar.remove()
193
+ topSidebar = null
194
+ }
195
+
196
+ if (!topSidebar) {
197
+ topPanel.prepend(new ToolbarContainer({ card, position: 'top' }))
198
+ }
199
+ }
200
+ })
201
+ }
202
+
203
+ template = () => {
204
+ // Атрибуты для игнорирования контейнера платформой wasaby
205
+ this.setAttribute('data-vdomignore', 'true')
206
+ this.setAttribute('contenteditable', 'true')
207
+ this.classList.add(this.options.position)
208
+ }
209
+
210
+ /** Будем запоминать все контейнеры для синхронизации кнопок */
211
+ connectedCallback() {
212
+ super.connectedCallback()
213
+ ToolbarContainer.containers.add(this)
214
+ ToolbarContainer.buttons.forEach(options => this.addButton(options))
215
+ }
216
+
217
+ /** Очищаем удаленные из DOM контейнеры */
218
+ disconnectedCallback() {
219
+ if (this.options.global) {
220
+ ToolbarContainer.globalContainer = null
221
+ ToolbarContainer.addGlobalContainer()
222
+ }
223
+ ToolbarContainer.containers.delete(this)
224
+ this.innerHTML = ''
225
+ }
226
+
227
+ /**
228
+ * Добавляет новую общую кнопку в текущий контейнер панели
229
+ *
230
+ * @param {ButtonOptions} options Опции кнопки
231
+ */
232
+ addButton(options) {
233
+ let match = true
234
+
235
+ match = match && this.options.position === options.position
236
+
237
+ if (match && this.options.card && options.filter) {
238
+ if (options.filter.names) {
239
+ match = match && options.filter.names.includes(this.options.card.data.name)
240
+ }
241
+ }
242
+
243
+ if (match) {
244
+ const button = oom.div({
245
+ class: 'button',
246
+ title: options.title,
247
+ onclick: () => {
248
+ const [card] = this.options.card
249
+ ? getListOfOpenCards({ id: this.options.card.data.id })
250
+ : [null]
251
+
252
+ options.onclick(card)
253
+ }
254
+ })
255
+
256
+ if (options.iconSVG) {
257
+ button({ innerHTML: options.iconSVG })
258
+ }
259
+
260
+ oom(this, button)
261
+ }
262
+ }
263
+
264
+ }
265
+
266
+
267
+ oom.define(ToolbarContainer)
268
+
269
+
270
+ export const toolbar = {
271
+ addButton: (options) => ToolbarContainer.addButton(options)
272
+ }
package/lib.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from './lib/context.js'
2
+ export * from './lib/database.js'
3
+ export * from './lib/hotkeys.js'
4
+ export * from './lib/layout.js'
5
+ export * from './lib/popup-dialog.js'
6
+ export * from './lib/saby-lib/edo.js'
7
+ export * from './lib/saby-lib/rpc.js'
8
+ export * from './lib/saby-lib/toolbar.js'