ustatic-css 0.0.1-b.2
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.
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/_ustatic-vars-Cx6_uZJY.cjs +1 -0
- package/dist/_ustatic-vars-D2NgNZNI.js +4 -0
- package/dist/css/modules/align.css +1 -0
- package/dist/css/modules/animations.css +1 -0
- package/dist/css/modules/base.css +1 -0
- package/dist/css/modules/bg.css +1 -0
- package/dist/css/modules/border.css +1 -0
- package/dist/css/modules/cursor.css +1 -0
- package/dist/css/modules/display.css +1 -0
- package/dist/css/modules/effects.css +1 -0
- package/dist/css/modules/filters.css +1 -0
- package/dist/css/modules/flexbox.css +1 -0
- package/dist/css/modules/grid.css +1 -0
- package/dist/css/modules/hide.css +1 -0
- package/dist/css/modules/interactivity.css +1 -0
- package/dist/css/modules/outline.css +1 -0
- package/dist/css/modules/position.css +1 -0
- package/dist/css/modules/scroll.css +1 -0
- package/dist/css/modules/sizing.css +1 -0
- package/dist/css/modules/spacing.css +1 -0
- package/dist/css/modules/typography.css +1 -0
- package/dist/css/ustatic-vars.css +1 -0
- package/dist/css/ustatic.css +1 -0
- package/dist/js/index.cjs.js +9 -0
- package/dist/js/index.es.js +175 -0
- package/dist/types/package.json.d.ts +102 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/plugins/vue.plugin.d.ts +15 -0
- package/dist/types/src/plugins/vue.plugin.d.ts.map +1 -0
- package/dist/types/src/utils/styleLoader.d.ts +35 -0
- package/dist/types/src/utils/styleLoader.d.ts.map +1 -0
- package/dist/types/src/utils/styleloader.classmap.d.ts +2 -0
- package/dist/types/src/utils/styleloader.classmap.d.ts.map +1 -0
- package/dist/types/src/utils/useCssProperties.d.ts +17 -0
- package/dist/types/src/utils/useCssProperties.d.ts.map +1 -0
- package/dist/types/src/utils/useTokens.d.ts +21 -0
- package/dist/types/src/utils/useTokens.d.ts.map +1 -0
- package/dist/ustatic-index-1SZfqZon.js +4 -0
- package/dist/ustatic-index-QcZrD98s.cjs +1 -0
- package/package.json +99 -0
- package/src/css/assets/tokens/_ustatic-list.scss +460 -0
- package/src/css/assets/tokens/_ustatic-prefix.scss +1 -0
- package/src/css/assets/tokens/_ustatic-vars.scss +757 -0
- package/src/css/modules/align/index.scss +17 -0
- package/src/css/modules/animations/index.scss +151 -0
- package/src/css/modules/base/index.scss +406 -0
- package/src/css/modules/base/scrollbar.scss +21 -0
- package/src/css/modules/bg/index.scss +60 -0
- package/src/css/modules/border/border.scss +88 -0
- package/src/css/modules/border/divider.scss +38 -0
- package/src/css/modules/border/index.scss +3 -0
- package/src/css/modules/border/rounded.scss +70 -0
- package/src/css/modules/cursor/index.scss +33 -0
- package/src/css/modules/display/index.scss +19 -0
- package/src/css/modules/effects/index.scss +35 -0
- package/src/css/modules/filters/index.scss +34 -0
- package/src/css/modules/flexbox/flex.scss +132 -0
- package/src/css/modules/flexbox/gap.scss +15 -0
- package/src/css/modules/flexbox/index.scss +2 -0
- package/src/css/modules/grid/index.scss +94 -0
- package/src/css/modules/hide/index.scss +27 -0
- package/src/css/modules/interactivity/index.scss +28 -0
- package/src/css/modules/outline/index.scss +63 -0
- package/src/css/modules/position/index.scss +94 -0
- package/src/css/modules/scroll/index.scss +68 -0
- package/src/css/modules/sizing/index.scss +91 -0
- package/src/css/modules/spacing/index.scss +56 -0
- package/src/css/modules/typography/index.scss +139 -0
- package/src/css/tokens/base/animations/underline.yaml +5 -0
- package/src/css/tokens/base/border/color.yaml +17 -0
- package/src/css/tokens/base/border/radius.yaml +33 -0
- package/src/css/tokens/base/border/width.yaml +19 -0
- package/src/css/tokens/base/color/accent.yaml +416 -0
- package/src/css/tokens/base/color/base.yaml +492 -0
- package/src/css/tokens/base/color/opacity.yaml +4 -0
- package/src/css/tokens/base/color/variant.yaml +18 -0
- package/src/css/tokens/base/cursor/base.yaml +18 -0
- package/src/css/tokens/base/font/weight.yaml +23 -0
- package/src/css/tokens/base/grid/base.yaml +9 -0
- package/src/css/tokens/base/position/base.yaml +253 -0
- package/src/css/tokens/base/rotation/base.yaml +20 -0
- package/src/css/tokens/base/screen/base.yaml +10 -0
- package/src/css/tokens/base/scroll/base.yaml +16 -0
- package/src/css/tokens/base/size/base.yaml +16 -0
- package/src/css/tokens/base/text/color.yaml +20 -0
- package/src/css/tokens/base/text/size.yaml +37 -0
- package/src/css/tokens/base/visibility/base.yaml +61 -0
- package/src/css/ustatic-index.scss +24 -0
- package/src/css/utils/_token.scss +56 -0
- package/src/css/variables.scss +359 -0
- package/src/index.ts +6 -0
- package/src/plugins/vue.plugin.ts +77 -0
- package/src/utils/styleLoader.ts +257 -0
- package/src/utils/styleloader.classmap.ts +109 -0
- package/src/utils/useCssProperties.ts +152 -0
- package/src/utils/useTokens.ts +287 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { classMap } from './styleloader.classmap'
|
|
2
|
+
|
|
3
|
+
export interface IStyleLoaderOptions {
|
|
4
|
+
modules?: string[];
|
|
5
|
+
classes?: string[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let icssLoaded = false
|
|
9
|
+
|
|
10
|
+
export const getModulesFromClasses = (classes: string | string[]): string[] => {
|
|
11
|
+
if (!classes) return []
|
|
12
|
+
|
|
13
|
+
let _classes = Array.isArray(classes) ? classes : []
|
|
14
|
+
|
|
15
|
+
if (classes?.length > 0 && !Array.isArray(classes)) {
|
|
16
|
+
_classes = classes?.replaceAll(' ', '')?.split(',')
|
|
17
|
+
}
|
|
18
|
+
const modules = new Set<string>()
|
|
19
|
+
|
|
20
|
+
_classes.forEach(className => {
|
|
21
|
+
// Проверяем точные соответствия
|
|
22
|
+
if (classMap[className]) {
|
|
23
|
+
modules.add(classMap[className])
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Проверяем частичные соответствия (префиксы)
|
|
28
|
+
for (const [ prefix, module ] of Object.entries(classMap)) {
|
|
29
|
+
// проверим классы на совпадение с префиксом в модуле
|
|
30
|
+
if (className.split('-')?.[0] === prefix?.split('-')?.[0]) {
|
|
31
|
+
modules.add(module)
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
return Array.from(modules)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface IStyleLink {
|
|
41
|
+
href: string
|
|
42
|
+
rel: string
|
|
43
|
+
type: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface IStyleLinksOptions extends IStyleLoaderOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Базовый путь для CSS файлов
|
|
49
|
+
* По умолчанию используется для SSR: пути относительно public папки
|
|
50
|
+
* Для браузера: используется import(...?url) для получения правильного URL
|
|
51
|
+
*/
|
|
52
|
+
basePath?: string
|
|
53
|
+
/**
|
|
54
|
+
* Режим работы: 'ssr' | 'browser'
|
|
55
|
+
* Если не указан, определяется автоматически
|
|
56
|
+
*/
|
|
57
|
+
mode?: 'ssr' | 'browser'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Определяет режим работы автоматически
|
|
62
|
+
*/
|
|
63
|
+
const detectMode = (): 'ssr' | 'browser' => {
|
|
64
|
+
return typeof document === 'undefined' ? 'ssr' : 'browser'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Возвращает список ссылок на CSS файлы для SSR и браузера
|
|
69
|
+
* Использует ту же логику формирования путей, что и loadStyles
|
|
70
|
+
*
|
|
71
|
+
* @param options - опции загрузчика стилей
|
|
72
|
+
* @param options.mode - режим работы ('ssr' или 'browser'), по умолчанию определяется автоматически
|
|
73
|
+
* @param options.basePath - базовый путь для CSS файлов (для SSR режима)
|
|
74
|
+
* @returns Promise с массивом объектов IStyleLink для вставки в <head>
|
|
75
|
+
*/
|
|
76
|
+
export const getStyleLinks = async (options?: IStyleLinksOptions): Promise<IStyleLink[]> => {
|
|
77
|
+
const mode = options?.mode ?? detectMode()
|
|
78
|
+
const basePath = options?.basePath ?? '/ustatic-css'
|
|
79
|
+
|
|
80
|
+
let modulesToLoad: string[] = getModulesFromClasses(options?.classes ?? [])
|
|
81
|
+
|
|
82
|
+
// Если переданы модули, используем их
|
|
83
|
+
if (options?.modules && options.modules.length > 0) {
|
|
84
|
+
modulesToLoad = modulesToLoad.concat(options.modules
|
|
85
|
+
.filter((module: string) => {
|
|
86
|
+
return !modulesToLoad.includes(module)
|
|
87
|
+
})
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const linkPromises: Promise<IStyleLink>[] = []
|
|
92
|
+
|
|
93
|
+
// Функция для получения URL в зависимости от режима
|
|
94
|
+
const getHref = async (modulePath: string, moduleName: string): Promise<string> => {
|
|
95
|
+
if (mode === 'browser') {
|
|
96
|
+
// В браузере используем import(...?url) для получения правильного URL от Vite
|
|
97
|
+
try {
|
|
98
|
+
const { default: css } = await import(`${modulePath}?url`)
|
|
99
|
+
return css
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('Failed to get CSS URL:', error)
|
|
102
|
+
throw error
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// В SSR режиме возвращаем путь относительно public папки
|
|
106
|
+
// moduleName - это имя файла без .css или имя модуля
|
|
107
|
+
return `${basePath}/${moduleName}`
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Если ничего не передано, загружаем все модули
|
|
112
|
+
if (modulesToLoad?.length === 0) {
|
|
113
|
+
linkPromises.push(
|
|
114
|
+
getHref('../css/ustatic.css', 'ustatic.css').then(href => ({ href, rel: 'stylesheet', type: 'text/css' })),
|
|
115
|
+
getHref('../css/vars.css', 'vars.css').then(href => ({ href, rel: 'stylesheet', type: 'text/css' }))
|
|
116
|
+
)
|
|
117
|
+
} else if (modulesToLoad.includes('ustatic.css') || modulesToLoad.includes('vars.css')) {
|
|
118
|
+
// Загружаем основные CSS файлы
|
|
119
|
+
linkPromises.push(
|
|
120
|
+
getHref('../css/ustatic.css', 'ustatic.css').then(href => ({ href, rel: 'stylesheet', type: 'text/css' })),
|
|
121
|
+
getHref('../css/vars.css', 'vars.css').then(href => ({ href, rel: 'stylesheet', type: 'text/css' }))
|
|
122
|
+
)
|
|
123
|
+
} else {
|
|
124
|
+
// Загружаем модули по отдельности
|
|
125
|
+
modulesToLoad.forEach(module => {
|
|
126
|
+
if (module === 'ustatic.css' || module === 'vars.css') {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
linkPromises.push(
|
|
130
|
+
getHref(`../css/modules/${module}.css`, `modules/${module}.css`).then(href => ({ href, rel: 'stylesheet', type: 'text/css' }))
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Всегда добавляем vars.css
|
|
135
|
+
linkPromises.push(
|
|
136
|
+
getHref('../css/vars.css', 'vars.css').then(href => ({ href, rel: 'stylesheet', type: 'text/css' }))
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return Promise.all(linkPromises)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Добавляет <link> на CSS файл в <head>
|
|
145
|
+
* @param href - URL CSS файла
|
|
146
|
+
*/
|
|
147
|
+
const loadCSS = (href: string): void => {
|
|
148
|
+
if (typeof document === 'undefined') return
|
|
149
|
+
|
|
150
|
+
const head = document.head
|
|
151
|
+
|
|
152
|
+
// Проверяем, существует ли уже ссылка на этот CSS
|
|
153
|
+
const existingLink = Array.from(head.getElementsByTagName('link')).find(
|
|
154
|
+
(linkElement) => {
|
|
155
|
+
const linkHref = linkElement.getAttribute('href')
|
|
156
|
+
if (!linkHref) return false
|
|
157
|
+
|
|
158
|
+
// Сравниваем пути без параметров и timestamp
|
|
159
|
+
const cleanHref = linkHref.split('?')[0]
|
|
160
|
+
const cleanNewHref = href.split('?')[0]
|
|
161
|
+
|
|
162
|
+
return cleanHref === cleanNewHref
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if (!existingLink) {
|
|
167
|
+
const linkEl = document.createElement('link')
|
|
168
|
+
linkEl.type = 'text/css'
|
|
169
|
+
linkEl.rel = 'stylesheet'
|
|
170
|
+
linkEl.href = href
|
|
171
|
+
head.appendChild(linkEl)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Формирует URL CSS файла на основе basePath
|
|
177
|
+
* @param cssPath - относительный путь к CSS файлу
|
|
178
|
+
* @param basePath - базовый путь из options
|
|
179
|
+
* @returns URL CSS файла
|
|
180
|
+
*/
|
|
181
|
+
const getCssUrl = (cssPath: string, basePath: string): string => {
|
|
182
|
+
// Убираем ведущие "../" из пути
|
|
183
|
+
const cleanPath = cssPath.replace(/^\.\.\//, '')
|
|
184
|
+
|
|
185
|
+
// Формируем полный путь
|
|
186
|
+
return basePath ? `${basePath}/${cleanPath}` : `/${cleanPath}`
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export const loadStyles = async (options?: IStyleLoaderOptions): Promise<void> => {
|
|
190
|
+
console.log('ustatic-css - loadStyles', { options })
|
|
191
|
+
// Проверяем, что document доступен
|
|
192
|
+
if (typeof document === 'undefined') return
|
|
193
|
+
|
|
194
|
+
if (icssLoaded) return
|
|
195
|
+
icssLoaded = true
|
|
196
|
+
|
|
197
|
+
console.log('ustatic-css - loadStyles - start loading', { options })
|
|
198
|
+
|
|
199
|
+
// Определяем basePath из options или из <base> тега
|
|
200
|
+
let basePath = options?.basePath || ''
|
|
201
|
+
|
|
202
|
+
if (!basePath && typeof document !== 'undefined') {
|
|
203
|
+
const baseElement = document.querySelector('base')
|
|
204
|
+
if (baseElement) {
|
|
205
|
+
basePath = baseElement.getAttribute('href')?.replace(/\/$/, '') || ''
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let modulesToLoad: string[] = getModulesFromClasses(options?.classes ?? [])
|
|
210
|
+
|
|
211
|
+
// Если переданы модули, используем их
|
|
212
|
+
if (options?.modules && options.modules.length > 0) {
|
|
213
|
+
modulesToLoad = modulesToLoad.concat(options.modules
|
|
214
|
+
.filter((module: string) => {
|
|
215
|
+
return !modulesToLoad.includes(module)
|
|
216
|
+
})
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
// Если ничего не передано, загружаем все модули
|
|
220
|
+
if (modulesToLoad?.length === 0) {
|
|
221
|
+
modulesToLoad = [
|
|
222
|
+
'ustatic.css',
|
|
223
|
+
'vars.css'
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.info('uCSS modules for load', { modulesToLoad, basePath, options })
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
if (modulesToLoad.includes('ustatic.css') || modulesToLoad.includes('vars.css')) {
|
|
231
|
+
// Загружаем основные CSS файлы
|
|
232
|
+
loadCSS(getCssUrl('../css/ustatic.css', basePath))
|
|
233
|
+
loadCSS(getCssUrl('../css/vars.css', basePath))
|
|
234
|
+
} else if (modulesToLoad?.length > 0) {
|
|
235
|
+
// Загружаем модули по отдельности
|
|
236
|
+
const modulePromises: Promise<void>[] = []
|
|
237
|
+
|
|
238
|
+
modulesToLoad.forEach(module => {
|
|
239
|
+
if (module === 'ustatic.css' || module === 'vars.css') {
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Формируем путь к модулю
|
|
244
|
+
const modulePath = getCssUrl(`../css/modules/${module}.css`, basePath)
|
|
245
|
+
modulePromises.push(loadCSS(modulePath))
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
modulePromises.push(loadCSS(getCssUrl('../css/vars.css', basePath)))
|
|
249
|
+
|
|
250
|
+
await Promise.all(modulePromises)
|
|
251
|
+
} else {
|
|
252
|
+
console.log('No css for load')
|
|
253
|
+
}
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Failed to load CSS files:', error)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export const classMap: Record<string, string> = {
|
|
2
|
+
// flexbox
|
|
3
|
+
'flex': 'flexbox',
|
|
4
|
+
'flex-column': 'flexbox',
|
|
5
|
+
'flex-row': 'flexbox',
|
|
6
|
+
'flex-wrap': 'flexbox',
|
|
7
|
+
'flex-nowrap': 'flexbox',
|
|
8
|
+
'items': 'flexbox',
|
|
9
|
+
'justify': 'flexbox',
|
|
10
|
+
'content': 'flexbox',
|
|
11
|
+
'self': 'flexbox',
|
|
12
|
+
'shrink': 'flexbox',
|
|
13
|
+
'grow': 'flexbox',
|
|
14
|
+
'order': 'flexbox',
|
|
15
|
+
|
|
16
|
+
// display
|
|
17
|
+
'inline': 'display',
|
|
18
|
+
'block': 'display',
|
|
19
|
+
'inline-block': 'display',
|
|
20
|
+
'table': 'display',
|
|
21
|
+
'table-cell': 'display',
|
|
22
|
+
'overflow': 'display',
|
|
23
|
+
|
|
24
|
+
// align
|
|
25
|
+
'align': 'align',
|
|
26
|
+
|
|
27
|
+
// position
|
|
28
|
+
'position': 'position',
|
|
29
|
+
'top': 'position',
|
|
30
|
+
'right': 'position',
|
|
31
|
+
'bottom': 'position',
|
|
32
|
+
'left': 'position',
|
|
33
|
+
|
|
34
|
+
// border
|
|
35
|
+
'border': 'border',
|
|
36
|
+
'rounded': 'border',
|
|
37
|
+
'divider': 'border',
|
|
38
|
+
|
|
39
|
+
// typography
|
|
40
|
+
'text': 'typography',
|
|
41
|
+
'font': 'typography',
|
|
42
|
+
'leading': 'typography',
|
|
43
|
+
'tracking': 'typography',
|
|
44
|
+
|
|
45
|
+
// spacing
|
|
46
|
+
'm': 'spacing',
|
|
47
|
+
'mx': 'spacing',
|
|
48
|
+
'my': 'spacing',
|
|
49
|
+
'mt': 'spacing',
|
|
50
|
+
'mr': 'spacing',
|
|
51
|
+
'mb': 'spacing',
|
|
52
|
+
'ml': 'spacing',
|
|
53
|
+
'p': 'spacing',
|
|
54
|
+
'px': 'spacing',
|
|
55
|
+
'py': 'spacing',
|
|
56
|
+
'pt': 'spacing',
|
|
57
|
+
'pr': 'spacing',
|
|
58
|
+
'pb': 'spacing',
|
|
59
|
+
'pl': 'spacing',
|
|
60
|
+
|
|
61
|
+
// sizing
|
|
62
|
+
'w': 'sizing',
|
|
63
|
+
'h': 'sizing',
|
|
64
|
+
'min-w': 'sizing',
|
|
65
|
+
'min-h': 'sizing',
|
|
66
|
+
'max-w': 'sizing',
|
|
67
|
+
'max-h': 'sizing',
|
|
68
|
+
|
|
69
|
+
// bg
|
|
70
|
+
'bg': 'bg',
|
|
71
|
+
|
|
72
|
+
// effects
|
|
73
|
+
'shadow': 'effects',
|
|
74
|
+
'opacity': 'effects',
|
|
75
|
+
|
|
76
|
+
// interactivity
|
|
77
|
+
'cursor': 'interactivity',
|
|
78
|
+
'pointer-events': 'interactivity',
|
|
79
|
+
'resize': 'interactivity',
|
|
80
|
+
|
|
81
|
+
// scroll
|
|
82
|
+
'scroll': 'scroll',
|
|
83
|
+
|
|
84
|
+
// animations
|
|
85
|
+
'animate': 'animations',
|
|
86
|
+
|
|
87
|
+
// outline
|
|
88
|
+
'outline': 'outline',
|
|
89
|
+
|
|
90
|
+
// grid
|
|
91
|
+
'grid': 'grid',
|
|
92
|
+
'col': 'grid',
|
|
93
|
+
'row': 'grid',
|
|
94
|
+
|
|
95
|
+
// filters
|
|
96
|
+
'filter': 'filters',
|
|
97
|
+
'blur': 'filters',
|
|
98
|
+
'brightness': 'filters',
|
|
99
|
+
'contrast': 'filters',
|
|
100
|
+
'grayscale': 'filters',
|
|
101
|
+
'hue-rotate': 'filters',
|
|
102
|
+
'invert': 'filters',
|
|
103
|
+
'saturate': 'filters',
|
|
104
|
+
'sepia': 'filters',
|
|
105
|
+
|
|
106
|
+
// hide
|
|
107
|
+
'hide': 'hide',
|
|
108
|
+
'show': 'hide',
|
|
109
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Хук для управления CSS-свойствами через классы
|
|
4
|
+
* @param params - Параметры конфигурации
|
|
5
|
+
* @param params.resetOnUnmount - Флаг, определяющий нужно ли сбрасывать свойства при размонтировании компонента.
|
|
6
|
+
* По умолчанию true
|
|
7
|
+
*/
|
|
8
|
+
export const useCssProperties = () => {
|
|
9
|
+
const styleId = 'css-features'
|
|
10
|
+
|
|
11
|
+
const generalProperties: Set<string> = new Set()
|
|
12
|
+
const classes: Set<string> = new Set()
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Устанавливает CSS-свойства для указанного класса
|
|
16
|
+
* @param className - Имя класса
|
|
17
|
+
* @param properties - Объект с CSS-свойствами и их значениями
|
|
18
|
+
*/
|
|
19
|
+
const setCssPropertiesViaClass = (className: string, properties: Record<string, string>): void => {
|
|
20
|
+
// Найти или создать тег <style> с нужным id
|
|
21
|
+
let styleTag = document.getElementById(styleId) as HTMLStyleElement
|
|
22
|
+
if (!styleTag) {
|
|
23
|
+
styleTag = document.createElement('style')
|
|
24
|
+
styleTag.id = styleId
|
|
25
|
+
document.head.appendChild(styleTag)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Создаем CSS-свойства для указанного класса
|
|
29
|
+
const classProperties = Object.entries(properties)
|
|
30
|
+
.map(([ property, value ]) => `${property}: ${value};`)
|
|
31
|
+
.join('\n ')
|
|
32
|
+
|
|
33
|
+
// Генерируем новый CSS-код для класса
|
|
34
|
+
const classContent = `.${className} {\n ${classProperties}\n}`
|
|
35
|
+
|
|
36
|
+
// Удаляем старую запись для этого класса, если она существует
|
|
37
|
+
let styleContent = styleTag.textContent || ''
|
|
38
|
+
const classRegex = new RegExp(`\\.${className}\\s*\\{[^}]*\\}`, 'g')
|
|
39
|
+
styleContent = styleContent.replace(classRegex, '')
|
|
40
|
+
|
|
41
|
+
// Добавляем обновлённый контент для класса
|
|
42
|
+
styleContent += `\n${classContent}`
|
|
43
|
+
// Обновляем содержимое тега <style>
|
|
44
|
+
styleTag.textContent = styleContent
|
|
45
|
+
|
|
46
|
+
classes.add(className)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Получает значение CSS-свойства для указанного класса
|
|
51
|
+
* @param className - Имя класса
|
|
52
|
+
* @param property - Имя CSS-свойства
|
|
53
|
+
* @returns Значение свойства или пустая строка, если свойство не найдено
|
|
54
|
+
*/
|
|
55
|
+
const getCssPropertyViaClass = (className: string, property: string): string => {
|
|
56
|
+
// Найти или создать тег <style> с нужным id
|
|
57
|
+
const styleTag = document.getElementById(styleId) as HTMLStyleElement
|
|
58
|
+
if (!styleTag) {
|
|
59
|
+
return ''
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Получаем содержимое тега <style>
|
|
63
|
+
const styleContent = styleTag.textContent
|
|
64
|
+
// Ищем нужный класс в содержимом
|
|
65
|
+
const classRegex = new RegExp(`\\.${className}\\s*\\{[^}]*\\}`, 'g')
|
|
66
|
+
const classContent = styleContent?.match(classRegex)
|
|
67
|
+
if (!classContent) {
|
|
68
|
+
return ''
|
|
69
|
+
}
|
|
70
|
+
// Получаем значение свойства
|
|
71
|
+
const propertyRegex = new RegExp(`${property}:\\s*([^;]+);`, 'g')
|
|
72
|
+
const propertyStr = classContent[0].match(propertyRegex)?.[0]
|
|
73
|
+
if (!propertyStr) {
|
|
74
|
+
return ''
|
|
75
|
+
}
|
|
76
|
+
return propertyStr.split(':')[1].trim()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Удаляет CSS-свойства для указанного класса
|
|
81
|
+
* @param className - Имя класса
|
|
82
|
+
*/
|
|
83
|
+
const removeClassWithProperties = (className: string): void => {
|
|
84
|
+
// Найти или создать тег <style> с нужным id
|
|
85
|
+
const styleTag = document.getElementById(styleId) as HTMLStyleElement
|
|
86
|
+
if (!styleTag) {
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
// Удаляем запись для этого класса, если она существует
|
|
90
|
+
let styleContent = styleTag.textContent || ''
|
|
91
|
+
const classRegex = new RegExp(`\\.${className}\\s*\\{[^}]*\\}`, 'g')
|
|
92
|
+
styleContent = styleContent.replace(classRegex, '')
|
|
93
|
+
// Обновляем содержимое тега <style>
|
|
94
|
+
styleTag.textContent = styleContent
|
|
95
|
+
// Удаляем класс из списка
|
|
96
|
+
classes.delete(className)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Очищает все CSS-свойства для всех классов
|
|
101
|
+
*/
|
|
102
|
+
const clearClassWithProperties = (): void => {
|
|
103
|
+
classes.forEach(className => {
|
|
104
|
+
removeClassWithProperties(className)
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Устанавливает CSS-свойство для элемента
|
|
110
|
+
* @param property - Имя CSS-свойства
|
|
111
|
+
* @param value - Значение свойства
|
|
112
|
+
*/
|
|
113
|
+
const setCssProperty = (property: string, value: string): void => {
|
|
114
|
+
generalProperties.add(property)
|
|
115
|
+
document.documentElement.style.setProperty(property, value)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Удаляет CSS-свойство для элемента
|
|
120
|
+
* @param property - Имя CSS-свойства
|
|
121
|
+
*/
|
|
122
|
+
const removeCssProperty = (property: string): void => {
|
|
123
|
+
generalProperties.delete(property)
|
|
124
|
+
document.documentElement.style.removeProperty(property)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Очищает все CSS-свойства для элемента
|
|
129
|
+
*/
|
|
130
|
+
const clearCssProperties = (): void => {
|
|
131
|
+
generalProperties.forEach(property => {
|
|
132
|
+
removeCssProperty(property)
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Очистка при размонтировании компонента
|
|
137
|
+
const clearAll = () => {
|
|
138
|
+
clearCssProperties()
|
|
139
|
+
clearClassWithProperties()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
setCssPropertiesViaClass,
|
|
144
|
+
getCssPropertyViaClass,
|
|
145
|
+
removeClassWithProperties,
|
|
146
|
+
clearClassWithProperties,
|
|
147
|
+
setCssProperty,
|
|
148
|
+
removeCssProperty,
|
|
149
|
+
clearCssProperties,
|
|
150
|
+
clearAll
|
|
151
|
+
}
|
|
152
|
+
}
|