@stellar-expert/ui-framework 1.9.11 → 1.10.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.
@@ -1,36 +1,36 @@
1
- import React, {useCallback, useEffect, useState} from 'react'
2
- import cn from 'classnames'
3
- import './accordion.scss'
4
-
5
- /**
6
- * Group of collapsible panels
7
- * @param {{key: String, title: String|JSX.Element, content:*}[]} options - Accordion options
8
- * @param {String} [collapsedSymbol] - Prefix to show for collapsed panels
9
- * @param {String} [expandedSymbol] - Prefix to show for expanded panel
10
- */
11
- export function Accordion({options, collapsedSymbol = '+', expandedSymbol = '-', ...otherProps}) {
12
- const [selectedOption, setSelectedOption] = useState()
13
- useEffect(() => {
14
- const firstOption = options[0]
15
- setSelectedOption(firstOption.key || firstOption.title)
16
- }, [options])
17
- const changeSelected = useCallback(e => setSelectedOption(e.currentTarget.dataset.key), [options])
18
- return <div className="accordion" {...otherProps}>
19
- {options.map(({key, title, content}) => {
20
- const expanded = key ?
21
- selectedOption === key :
22
- selectedOption === title
23
- return <div key={key || title} className={cn('option', {expanded})}>
24
- <div className="accordion-header" data-collapsed={collapsedSymbol} data-expanded={expandedSymbol} data-key={key || title}
25
- onClick={changeSelected}>
26
- {title || key}
27
- </div>
28
- <div className="accordion-collapse">
29
- <div className="accordion-body">
30
- {content}
31
- </div>
32
- </div>
33
- </div>
34
- })}
35
- </div>
1
+ import React, {useCallback, useEffect, useState} from 'react'
2
+ import cn from 'classnames'
3
+ import './accordion.scss'
4
+
5
+ /**
6
+ * Group of collapsible panels
7
+ * @param {{key: String, title: String|JSX.Element, content:*}[]} options - Accordion options
8
+ * @param {String} [collapsedSymbol] - Prefix to show for collapsed panels
9
+ * @param {String} [expandedSymbol] - Prefix to show for expanded panel
10
+ */
11
+ export function Accordion({options, collapsedSymbol = '+', expandedSymbol = '-', ...otherProps}) {
12
+ const [selectedOption, setSelectedOption] = useState()
13
+ useEffect(() => {
14
+ const firstOption = options[0]
15
+ setSelectedOption(firstOption.key || firstOption.title)
16
+ }, [options])
17
+ const changeSelected = useCallback(e => setSelectedOption(e.currentTarget.dataset.key), [options])
18
+ return <div className="accordion" {...otherProps}>
19
+ {options.map(({key, title, content}) => {
20
+ const expanded = key ?
21
+ selectedOption === key :
22
+ selectedOption === title
23
+ return <div key={key || title} className={cn('option', {expanded})}>
24
+ <div className="accordion-header" data-collapsed={collapsedSymbol} data-expanded={expandedSymbol} data-key={key || title}
25
+ onClick={changeSelected}>
26
+ {title || key}
27
+ </div>
28
+ <div className="accordion-collapse">
29
+ <div className="accordion-body">
30
+ {content}
31
+ </div>
32
+ </div>
33
+ </div>
34
+ })}
35
+ </div>
36
36
  }
@@ -1,32 +1,32 @@
1
- .accordion {
2
- .accordion-header {
3
- position: relative;
4
- cursor: pointer;
5
- user-select: none;
6
-
7
- &:before {
8
- content: attr(data-collapsed);
9
- position: absolute;
10
- top: 50%;
11
- transform: translateY(-50%);
12
- left: -20px;
13
- }
14
- }
15
-
16
- .accordion-collapse {
17
- height: 0;
18
- overflow-y: hidden;
19
- }
20
-
21
- .option {
22
- &.expanded {
23
- .accordion-header:before {
24
- content: attr(data-expanded);
25
- }
26
-
27
- .accordion-collapse {
28
- height: auto;
29
- }
30
- }
31
- }
1
+ .accordion {
2
+ .accordion-header {
3
+ position: relative;
4
+ cursor: pointer;
5
+ user-select: none;
6
+
7
+ &:before {
8
+ content: attr(data-collapsed);
9
+ position: absolute;
10
+ top: 50%;
11
+ transform: translateY(-50%);
12
+ left: -20px;
13
+ }
14
+ }
15
+
16
+ .accordion-collapse {
17
+ height: 0;
18
+ overflow-y: hidden;
19
+ }
20
+
21
+ .option {
22
+ &.expanded {
23
+ .accordion-header:before {
24
+ content: attr(data-expanded);
25
+ }
26
+
27
+ .accordion-collapse {
28
+ height: auto;
29
+ }
30
+ }
31
+ }
32
32
  }
@@ -1,229 +1,229 @@
1
- import {useEffect} from 'react'
2
- import isEqual from 'react-fast-compare'
3
-
4
- const {origin} = window.location
5
- const domain = origin.replace(/https?:\/\//, '')
6
-
7
- const metaProps = {
8
- serviceTitle: '',
9
- description: '',
10
- facebookImage: '',
11
- twitterImage: '',
12
- twitterUsername: ''
13
- }
14
-
15
- /**
16
- * Initialize default meta properties for the application
17
- * @param {{serviceTitle: String, description: String, facebookImage: String, twitterImage: String, twitterUsername: String}} appMetaProps
18
- */
19
- export function initMeta(appMetaProps) {
20
- Object.assign(metaProps, appMetaProps)
21
- }
22
-
23
- function formatPageTitle(title) {
24
- if (!title)
25
- return metaProps.serviceTitle
26
- if (title.includes(metaProps.serviceTitle))
27
- return title
28
- return `${title} ${metaProps.serviceTitle}`
29
- }
30
-
31
- function generateCanonicalLink(params, canonicalUrl) {
32
- return {
33
- tag: 'link',
34
- locator: 'rel',
35
- tags: [
36
- {name: 'canonical', content: canonicalUrl, attribute: 'href'}
37
- ]
38
- }
39
- }
40
-
41
- function generateDescriptionMeta({description}) {
42
- return {
43
- locator: 'name',
44
- tags: [
45
- {name: 'description', content: description || metaProps.description}
46
- ]
47
- }
48
- }
49
-
50
- function generateTwitterMeta({description, title, twitterImage}) {
51
- return {
52
- locator: 'name',
53
- tags: [
54
- {name: 'twitter:card', content: 'summary_large_image'},
55
- {name: 'twitter:site', content: metaProps.twitterUsername},
56
- {name: 'twitter:title', content: formatPageTitle(title)},
57
- {name: 'twitter:description', content: description || metaProps.description},
58
- {name: 'twitter:image:src', content: twitterImage || metaProps.twitterImage}
59
- ]
60
- }
61
- }
62
-
63
- function generateOpenGraphMeta({description, title, facebookImage}, canonicalUrl) {
64
- let tags = [
65
- {name: 'og:title', content: formatPageTitle(title)},
66
- {name: 'og:url', content: canonicalUrl},
67
- {name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
68
- {name: 'og:description', content: description || metaProps.description},
69
- {name: 'og:type', content: 'website'}
70
- ]
71
- if (facebookImage) {
72
- tags.push({name: 'og:image', content: facebookImage})
73
- } else {
74
- tags.push({name: 'og:image', content: metaProps.facebookImage})
75
- /*tags = tags.concat([
76
- {name: 'og:image', content: metaProps.facebookImage},
77
- {name: 'og:image:width', content: 1200},
78
- {name: 'og:image:height', content: 630}])*/
79
- }
80
- return {
81
- locator: 'property',
82
- tags
83
- }
84
- }
85
-
86
- function generateItemPropSchema({description, title, image}) {
87
- return {
88
- locator: 'itemprop',
89
- tags: [
90
- {name: 'name', content: title},
91
- {name: 'description', content: description || metaProps.description},
92
- {name: 'image', content: image || metaProps.facebookImage}
93
- ]
94
- }
95
- }
96
-
97
- function generateLdJsonSchema({title}) {
98
- return {
99
- tag: 'script',
100
- locator: 'type',
101
- tags: [
102
- {
103
- name: 'application/ld+json',
104
- content: JSON.stringify({
105
- '@context': 'http://schema.org',
106
- '@type': 'WebSite',
107
- 'name': domain,
108
- 'alternateName': title,
109
- 'url': origin
110
- })
111
- }
112
- ]
113
- }
114
- }
115
-
116
- /**
117
- * @param {MetaTagReplacement} replacement
118
- */
119
- function replaceMetaTags(replacement) {
120
- const {tag, tags, locator} = replacement
121
- const selector = tag || 'meta'
122
- tags.forEach(tagSettings => {
123
- let tag = document.querySelector(`${selector}[${locator}="${tagSettings.name}"]`)
124
- if (!tag) {
125
- tag = createTag(selector)
126
- tag.setAttribute(locator, tagSettings.name)
127
- }
128
- if (selector === 'meta') {
129
- tag.content = tagSettings.content
130
- } else if (tagSettings.attribute) {
131
- tag[tagSettings.attribute] = tagSettings.content
132
- } else {
133
- tag.innerText = tagSettings.content
134
- }
135
- })
136
- }
137
-
138
- function createTag(tagName, props) {
139
- let tag = document.createElement(tagName)
140
- for (let key in props) {
141
- tag[key] = props[key]
142
- }
143
- document.head.appendChild(tag)
144
- return tag
145
- }
146
-
147
- function removeTag(selector) {
148
- let tag = document.querySelector(selector)
149
- if (tag) {
150
- tag.parentElement.removeChild(tag)
151
- }
152
- }
153
-
154
- let pageMeta = {}
155
-
156
- const tagReplacerPipeline = [generateCanonicalLink, generateDescriptionMeta, generateOpenGraphMeta, generateTwitterMeta, generateLdJsonSchema] // generateItemPropSchema
157
-
158
- /**
159
- * Update page metadata tags
160
- * @param {PageMeta} meta - Page metadata
161
- */
162
- export function setPageMetadata(meta) {
163
- if (isEqual(pageMeta, meta))
164
- return
165
- const canonicalUrl = origin + location.pathname// + location.search
166
- document.title = formatPageTitle(meta.title)
167
- for (const replacer of tagReplacerPipeline) {
168
- replaceMetaTags(replacer(meta, canonicalUrl))
169
- }
170
- if (meta.customMeta) {
171
- replaceMetaTags(meta.customMeta)
172
- }
173
- pageMeta = meta
174
- }
175
-
176
- /**
177
- * Reset page metadata tags to their default values
178
- * @param {PageMeta} meta - Page metadata
179
- */
180
- export function resetPageMetadata(meta) {
181
- setPageMetadata({
182
- title: metaProps.serviceTitle,
183
- description: metaProps.description,
184
- twitterImage: metaProps.twitterImage,
185
- facebookImage: metaProps.facebookImage
186
- })
187
- //TODO: add logic to cleanup custom page meta tags on page unload
188
- }
189
-
190
- /*export function setPageNoIndex(noIndex) {
191
- if (!noIndex) {
192
- removeTag('meta[name=robots]')
193
- } else {
194
- replaceMetaTags({
195
- locator: 'name',
196
- tags: [
197
- {name: 'robots', content: 'noindex,nofollow'}
198
- ]
199
- })
200
- }
201
- }*/
202
-
203
- /**
204
- * React hook for setting page metadata
205
- * @param {PageMeta} meta - Page metadata
206
- * @param {*[]} dependencies
207
- */
208
- export function usePageMetadata(meta, dependencies = []) {
209
- useEffect(() => {
210
- setPageMetadata(meta)
211
- return () => resetPageMetadata(meta)
212
- }, [JSON.stringify(meta), ...dependencies])
213
- }
214
-
215
- /**
216
- * @typedef {Object} PageMeta
217
- * @property {String} title - Page title
218
- * @property {String} description - Contents description
219
- * @property {String} [twitterImage] - Twitter image url
220
- * @property {String} [facebookImage] - Facebook image url
221
- * @property {MetaTagReplacement} [customMeta] - Custom metadata tags
222
- */
223
-
224
- /**
225
- * @typedef {Object} MetaTagReplacement
226
- * @property {String} [tag] - Tag name to search for ("meta" by default)
227
- * @property {String} locator - Tag attribute to match
228
- * @property {{name: String, content: String, [attribute]: String}[]} tags - Tag properties to set
1
+ import {useEffect} from 'react'
2
+ import isEqual from 'react-fast-compare'
3
+
4
+ const {origin} = window.location
5
+ const domain = origin.replace(/https?:\/\//, '')
6
+
7
+ const metaProps = {
8
+ serviceTitle: '',
9
+ description: '',
10
+ facebookImage: '',
11
+ twitterImage: '',
12
+ twitterUsername: ''
13
+ }
14
+
15
+ /**
16
+ * Initialize default meta properties for the application
17
+ * @param {{serviceTitle: String, description: String, facebookImage: String, twitterImage: String, twitterUsername: String}} appMetaProps
18
+ */
19
+ export function initMeta(appMetaProps) {
20
+ Object.assign(metaProps, appMetaProps)
21
+ }
22
+
23
+ function formatPageTitle(title) {
24
+ if (!title)
25
+ return metaProps.serviceTitle
26
+ if (title.includes(metaProps.serviceTitle))
27
+ return title
28
+ return `${title} ${metaProps.serviceTitle}`
29
+ }
30
+
31
+ function generateCanonicalLink(params, canonicalUrl) {
32
+ return {
33
+ tag: 'link',
34
+ locator: 'rel',
35
+ tags: [
36
+ {name: 'canonical', content: canonicalUrl, attribute: 'href'}
37
+ ]
38
+ }
39
+ }
40
+
41
+ function generateDescriptionMeta({description}) {
42
+ return {
43
+ locator: 'name',
44
+ tags: [
45
+ {name: 'description', content: description || metaProps.description}
46
+ ]
47
+ }
48
+ }
49
+
50
+ function generateTwitterMeta({description, title, twitterImage}) {
51
+ return {
52
+ locator: 'name',
53
+ tags: [
54
+ {name: 'twitter:card', content: 'summary_large_image'},
55
+ {name: 'twitter:site', content: metaProps.twitterUsername},
56
+ {name: 'twitter:title', content: formatPageTitle(title)},
57
+ {name: 'twitter:description', content: description || metaProps.description},
58
+ {name: 'twitter:image:src', content: twitterImage || metaProps.twitterImage}
59
+ ]
60
+ }
61
+ }
62
+
63
+ function generateOpenGraphMeta({description, title, facebookImage}, canonicalUrl) {
64
+ let tags = [
65
+ {name: 'og:title', content: formatPageTitle(title)},
66
+ {name: 'og:url', content: canonicalUrl},
67
+ {name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
68
+ {name: 'og:description', content: description || metaProps.description},
69
+ {name: 'og:type', content: 'website'}
70
+ ]
71
+ if (facebookImage) {
72
+ tags.push({name: 'og:image', content: facebookImage})
73
+ } else {
74
+ tags.push({name: 'og:image', content: metaProps.facebookImage})
75
+ /*tags = tags.concat([
76
+ {name: 'og:image', content: metaProps.facebookImage},
77
+ {name: 'og:image:width', content: 1200},
78
+ {name: 'og:image:height', content: 630}])*/
79
+ }
80
+ return {
81
+ locator: 'property',
82
+ tags
83
+ }
84
+ }
85
+
86
+ function generateItemPropSchema({description, title, image}) {
87
+ return {
88
+ locator: 'itemprop',
89
+ tags: [
90
+ {name: 'name', content: title},
91
+ {name: 'description', content: description || metaProps.description},
92
+ {name: 'image', content: image || metaProps.facebookImage}
93
+ ]
94
+ }
95
+ }
96
+
97
+ function generateLdJsonSchema({title}) {
98
+ return {
99
+ tag: 'script',
100
+ locator: 'type',
101
+ tags: [
102
+ {
103
+ name: 'application/ld+json',
104
+ content: JSON.stringify({
105
+ '@context': 'http://schema.org',
106
+ '@type': 'WebSite',
107
+ 'name': domain,
108
+ 'alternateName': title,
109
+ 'url': origin
110
+ })
111
+ }
112
+ ]
113
+ }
114
+ }
115
+
116
+ /**
117
+ * @param {MetaTagReplacement} replacement
118
+ */
119
+ function replaceMetaTags(replacement) {
120
+ const {tag, tags, locator} = replacement
121
+ const selector = tag || 'meta'
122
+ tags.forEach(tagSettings => {
123
+ let tag = document.querySelector(`${selector}[${locator}="${tagSettings.name}"]`)
124
+ if (!tag) {
125
+ tag = createTag(selector)
126
+ tag.setAttribute(locator, tagSettings.name)
127
+ }
128
+ if (selector === 'meta') {
129
+ tag.content = tagSettings.content
130
+ } else if (tagSettings.attribute) {
131
+ tag[tagSettings.attribute] = tagSettings.content
132
+ } else {
133
+ tag.innerText = tagSettings.content
134
+ }
135
+ })
136
+ }
137
+
138
+ function createTag(tagName, props) {
139
+ let tag = document.createElement(tagName)
140
+ for (let key in props) {
141
+ tag[key] = props[key]
142
+ }
143
+ document.head.appendChild(tag)
144
+ return tag
145
+ }
146
+
147
+ function removeTag(selector) {
148
+ let tag = document.querySelector(selector)
149
+ if (tag) {
150
+ tag.parentElement.removeChild(tag)
151
+ }
152
+ }
153
+
154
+ let pageMeta = {}
155
+
156
+ const tagReplacerPipeline = [generateCanonicalLink, generateDescriptionMeta, generateOpenGraphMeta, generateTwitterMeta, generateLdJsonSchema] // generateItemPropSchema
157
+
158
+ /**
159
+ * Update page metadata tags
160
+ * @param {PageMeta} meta - Page metadata
161
+ */
162
+ export function setPageMetadata(meta) {
163
+ if (isEqual(pageMeta, meta))
164
+ return
165
+ const canonicalUrl = origin + location.pathname// + location.search
166
+ document.title = formatPageTitle(meta.title)
167
+ for (const replacer of tagReplacerPipeline) {
168
+ replaceMetaTags(replacer(meta, canonicalUrl))
169
+ }
170
+ if (meta.customMeta) {
171
+ replaceMetaTags(meta.customMeta)
172
+ }
173
+ pageMeta = meta
174
+ }
175
+
176
+ /**
177
+ * Reset page metadata tags to their default values
178
+ * @param {PageMeta} meta - Page metadata
179
+ */
180
+ export function resetPageMetadata(meta) {
181
+ setPageMetadata({
182
+ title: metaProps.serviceTitle,
183
+ description: metaProps.description,
184
+ twitterImage: metaProps.twitterImage,
185
+ facebookImage: metaProps.facebookImage
186
+ })
187
+ //TODO: add logic to cleanup custom page meta tags on page unload
188
+ }
189
+
190
+ /*export function setPageNoIndex(noIndex) {
191
+ if (!noIndex) {
192
+ removeTag('meta[name=robots]')
193
+ } else {
194
+ replaceMetaTags({
195
+ locator: 'name',
196
+ tags: [
197
+ {name: 'robots', content: 'noindex,nofollow'}
198
+ ]
199
+ })
200
+ }
201
+ }*/
202
+
203
+ /**
204
+ * React hook for setting page metadata
205
+ * @param {PageMeta} meta - Page metadata
206
+ * @param {*[]} dependencies
207
+ */
208
+ export function usePageMetadata(meta, dependencies = []) {
209
+ useEffect(() => {
210
+ setPageMetadata(meta)
211
+ return () => resetPageMetadata(meta)
212
+ }, [JSON.stringify(meta), ...dependencies])
213
+ }
214
+
215
+ /**
216
+ * @typedef {Object} PageMeta
217
+ * @property {String} title - Page title
218
+ * @property {String} description - Contents description
219
+ * @property {String} [twitterImage] - Twitter image url
220
+ * @property {String} [facebookImage] - Facebook image url
221
+ * @property {MetaTagReplacement} [customMeta] - Custom metadata tags
222
+ */
223
+
224
+ /**
225
+ * @typedef {Object} MetaTagReplacement
226
+ * @property {String} [tag] - Tag name to search for ("meta" by default)
227
+ * @property {String} locator - Tag attribute to match
228
+ * @property {{name: String, content: String, [attribute]: String}[]} tags - Tag properties to set
229
229
  */