@pareto-engineering/design-system 2.0.0-alpha.32 → 2.0.0-alpha.35

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.
Files changed (46) hide show
  1. package/dist/cjs/a/ContentTree/ContentTree.js +83 -0
  2. package/dist/cjs/a/ContentTree/common/Tree/Tree.js +116 -0
  3. package/dist/cjs/a/ContentTree/common/Tree/index.js +15 -0
  4. package/dist/cjs/a/ContentTree/common/index.js +31 -0
  5. package/dist/cjs/a/ContentTree/common/useContentTree.js +82 -0
  6. package/dist/cjs/a/ContentTree/common/useFirstVisibleNode.js +65 -0
  7. package/dist/cjs/a/ContentTree/index.js +15 -0
  8. package/dist/cjs/a/ContentTree/styles.scss +33 -0
  9. package/dist/cjs/a/index.js +9 -1
  10. package/dist/cjs/c/SocialMediaShareButton/SocialMediaShareButton.js +106 -0
  11. package/dist/cjs/c/SocialMediaShareButton/index.js +15 -0
  12. package/dist/cjs/c/SocialMediaShareButton/styles.scss +39 -0
  13. package/dist/cjs/c/index.js +9 -1
  14. package/dist/es/a/ContentTree/ContentTree.js +67 -0
  15. package/dist/es/a/ContentTree/common/Tree/Tree.js +98 -0
  16. package/dist/es/a/ContentTree/common/Tree/index.js +2 -0
  17. package/dist/es/a/ContentTree/common/index.js +3 -0
  18. package/dist/es/a/ContentTree/common/useContentTree.js +74 -0
  19. package/dist/es/a/ContentTree/common/useFirstVisibleNode.js +54 -0
  20. package/dist/es/a/ContentTree/index.js +2 -0
  21. package/dist/es/a/ContentTree/styles.scss +33 -0
  22. package/dist/es/a/index.js +2 -1
  23. package/dist/es/c/SocialMediaShareButton/SocialMediaShareButton.js +90 -0
  24. package/dist/es/c/SocialMediaShareButton/index.js +2 -0
  25. package/dist/es/c/SocialMediaShareButton/styles.scss +39 -0
  26. package/dist/es/c/index.js +2 -1
  27. package/package.json +3 -2
  28. package/src/__snapshots__/Storyshots.test.js.snap +196 -12
  29. package/src/local.scss +1 -0
  30. package/src/stories/StyleGuide/Sprites.stories.mdx +25 -0
  31. package/src/stories/StyleGuide/helpers.js +16 -0
  32. package/src/stories/a/ContentTree.stories.jsx +662 -0
  33. package/src/stories/c/SocialMediaShareButton.stories.jsx +25 -0
  34. package/src/ui/a/ContentTree/ContentTree.jsx +88 -0
  35. package/src/ui/a/ContentTree/common/Tree/Tree.jsx +138 -0
  36. package/src/ui/a/ContentTree/common/Tree/index.js +2 -0
  37. package/src/ui/a/ContentTree/common/index.js +3 -0
  38. package/src/ui/a/ContentTree/common/useContentTree.js +83 -0
  39. package/src/ui/a/ContentTree/common/useFirstVisibleNode.js +59 -0
  40. package/src/ui/a/ContentTree/index.js +2 -0
  41. package/src/ui/a/ContentTree/styles.scss +33 -0
  42. package/src/ui/a/index.js +1 -0
  43. package/src/ui/c/SocialMediaShareButton/SocialMediaShareButton.jsx +111 -0
  44. package/src/ui/c/SocialMediaShareButton/index.js +2 -0
  45. package/src/ui/c/SocialMediaShareButton/styles.scss +39 -0
  46. package/src/ui/c/index.js +1 -0
@@ -0,0 +1,25 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ import * as React from 'react'
3
+ import { SocialMediaShareButton } from 'ui'
4
+ import Router from '../utils/Router'
5
+
6
+ export default {
7
+ title :'c/SocialMediaShareButton',
8
+ component :SocialMediaShareButton,
9
+ subcomponents:{
10
+ // Item:SocialMediaShareButton.Item
11
+ },
12
+ decorators:[
13
+ (storyfn) => <Router>{storyfn()}</Router>,
14
+ ],
15
+ argTypes:{
16
+ backgroundColor:{ control: 'color' },
17
+ },
18
+ }
19
+
20
+ export const Base = () => (
21
+ <div>
22
+ <SocialMediaShareButton type="facebook" />
23
+ <SocialMediaShareButton type="twitter" />
24
+ </div>
25
+ )
@@ -0,0 +1,88 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ import * as React from 'react'
3
+
4
+ import { useLayoutEffect } from 'react'
5
+
6
+ import PropTypes from 'prop-types'
7
+
8
+ import styleNames from '@pareto-engineering/bem'
9
+
10
+ // Local Definitions
11
+ import { Tree, useContentTree } from './common'
12
+
13
+ const baseClassName = styleNames.base
14
+
15
+ const componentClassName = 'content-tree'
16
+
17
+ /**
18
+ * This is the component description.
19
+ */
20
+ const ContentTree = ({
21
+ id,
22
+ className:userClassName,
23
+ style,
24
+ target,
25
+ selectors,
26
+ // ...otherProps
27
+ }) => {
28
+ useLayoutEffect(() => {
29
+ import('./styles.scss')
30
+ }, [])
31
+
32
+ const contentTree = useContentTree(target, selectors)
33
+
34
+ return (
35
+ <div
36
+ id={id}
37
+ className={[
38
+
39
+ baseClassName,
40
+
41
+ componentClassName,
42
+ userClassName,
43
+ ]
44
+ .filter((e) => e)
45
+ .join(' ')}
46
+ style={style}
47
+ // {...otherProps}
48
+ >
49
+ <Tree tree={contentTree} />
50
+ </div>
51
+ )
52
+ }
53
+
54
+ ContentTree.propTypes = {
55
+ /**
56
+ * The HTML id for this element
57
+ */
58
+ id:PropTypes.string,
59
+
60
+ /**
61
+ * The HTML class names for this element
62
+ */
63
+ className:PropTypes.string,
64
+
65
+ /**
66
+ * The React-written, css properties for this element.
67
+ */
68
+ style:PropTypes.objectOf(PropTypes.string),
69
+
70
+ /**
71
+ * The selectors to use to extract the navigation tree from the content.
72
+ */
73
+ selectors:PropTypes.arrayOf(PropTypes.string),
74
+
75
+ /**
76
+ * The reference to the parent element.
77
+ */
78
+ target:PropTypes.oneOfType([
79
+ PropTypes.func,
80
+ PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
81
+ ]),
82
+ }
83
+
84
+ ContentTree.defaultProps = {
85
+ selectors:['h2', 'h3'],
86
+ }
87
+
88
+ export default ContentTree
@@ -0,0 +1,138 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ import * as React from 'react'
3
+
4
+ import { useState, useEffect, useMemo } from 'react'
5
+
6
+ import PropTypes from 'prop-types'
7
+
8
+ import styleNames from '@pareto-engineering/bem'
9
+
10
+ // Local Definitions
11
+
12
+ import useFirstVisibleNode from '../useFirstVisibleNode'
13
+
14
+ const baseClassName = styleNames.base
15
+
16
+ const componentClassName = 'tree'
17
+
18
+ /**
19
+ * This is the component description.
20
+ */
21
+ const Tree = ({
22
+ id,
23
+ className:userClassName,
24
+ style,
25
+ tree,
26
+ displayDepth,
27
+ // ...otherProps
28
+ }) => {
29
+ // The nodeIds to be used to get the first visible node
30
+ const [nodeIds, setNodeIds] = useState([])
31
+
32
+ // current visible nodeId
33
+ const visibleNodeId = useFirstVisibleNode(nodeIds)
34
+
35
+ useEffect(() => {
36
+ const node = document.getElementsByClassName(visibleNodeId)[0]
37
+ node?.classList.add(styleNames.modifierActive)
38
+
39
+ return () => {
40
+ node?.classList.remove(styleNames.modifierActive)
41
+ }
42
+ }, [visibleNodeId])
43
+
44
+ // Generate the tree structure from the content tree data depending on the display depth
45
+ const getNestedTree = (node, depth) => {
46
+ setNodeIds((prev) => [...prev, node.id])
47
+ if (depth <= 1) {
48
+ return (
49
+ <li key={node.id}>
50
+ <a
51
+ className={node.id}
52
+ href={node.id}
53
+ >
54
+ {node.text}
55
+ </a>
56
+ </li>
57
+ )
58
+ }
59
+ return (
60
+ <li key={node.id}>
61
+ <p>
62
+ <a
63
+ href={node.id}
64
+ className={node.id}
65
+ >
66
+ {node.text}
67
+ </a>
68
+ </p>
69
+ {node.children.length > 0 && (
70
+ <ul>
71
+ {node.children.map((child) => getNestedTree(child, depth - 1))}
72
+ </ul>
73
+ )}
74
+ </li>
75
+ )
76
+ }
77
+
78
+ const ContentTree = useMemo(
79
+ () => tree.map((node) => getNestedTree(node, displayDepth)),
80
+ [tree],
81
+ )
82
+
83
+ return (
84
+ <ul
85
+ id={id}
86
+ className={[
87
+
88
+ baseClassName,
89
+
90
+ componentClassName,
91
+ userClassName,
92
+ ]
93
+ .filter((e) => e)
94
+ .join(' ')}
95
+ style={style}
96
+ >
97
+ {ContentTree}
98
+ </ul>
99
+ )
100
+ }
101
+ Tree.propTypes = {
102
+ /**
103
+ * The HTML id for this element
104
+ */
105
+ id:PropTypes.string,
106
+
107
+ /**
108
+ * The HTML class names for this element
109
+ */
110
+ className:PropTypes.string,
111
+
112
+ /**
113
+ * The React-written, css properties for this element.
114
+ */
115
+ style:PropTypes.objectOf(PropTypes.string),
116
+
117
+ /**
118
+ * The tree to render.
119
+ */
120
+ tree:PropTypes.arrayOf(PropTypes.shape({
121
+ text :PropTypes.string,
122
+ id :PropTypes.string,
123
+ children:PropTypes.arrayOf(PropTypes.shape({
124
+ text:PropTypes.string, id:PropTypes.string,
125
+ })),
126
+ })),
127
+
128
+ /**
129
+ * The levels of the tree to display.
130
+ */
131
+ displayDepth:PropTypes.number,
132
+ }
133
+
134
+ Tree.defaultProps = {
135
+ displayDepth:4,
136
+ }
137
+
138
+ export default Tree
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ export { default as Tree } from './Tree'
@@ -0,0 +1,3 @@
1
+ export { default as useContentTree } from './useContentTree'
2
+ export { default as useFirstVisibleNode } from './useFirstVisibleNode'
3
+ export { Tree } from './Tree'
@@ -0,0 +1,83 @@
1
+ import { useState, useEffect, useCallback } from 'react'
2
+
3
+ const useContentTree = (target, selectors) => {
4
+ const [contentTree, setContentTree] = useState([])
5
+
6
+ const getNodes = useCallback((parentTag) => {
7
+ const nodeList = parentTag.querySelectorAll(selectors.join(', '))
8
+ const nodes = []
9
+
10
+ nodeList.forEach((nodeNode) => {
11
+ const { id, innerText, tagName } = nodeNode
12
+
13
+ nodes.push({
14
+ id :`#${id}`,
15
+ text :innerText,
16
+ level:selectors.indexOf(tagName.toLowerCase()),
17
+ })
18
+ })
19
+
20
+ return nodes
21
+ }, [selectors])
22
+
23
+ const buildTree = useCallback((nodes) => {
24
+ // Track the nodes we've seen so far in the same level
25
+ const currentSameLevelNodes = []
26
+ // Track nodes of the next level which will be children of the current node
27
+ let nextLevelNodes = []
28
+ // Track the current node level
29
+ let lastLevel = -1
30
+
31
+ // If the nodes are empty, return an empty tree
32
+ if (nodes.length === 0) {
33
+ return []
34
+ }
35
+
36
+ const buildSubTree = () => {
37
+ if (nextLevelNodes.length > 0) {
38
+ currentSameLevelNodes[currentSameLevelNodes.length - 1].children.push(
39
+ ...buildTree(nextLevelNodes),
40
+ )
41
+ }
42
+ }
43
+
44
+ nodes.forEach((node) => {
45
+ // If the node is of a greater level, we need to build the sub tree
46
+ if (lastLevel !== -1 && lastLevel < node.level) {
47
+ nextLevelNodes.push(node)
48
+ return
49
+ }
50
+
51
+ // build a subtree
52
+ buildSubTree()
53
+
54
+ // reset the next level nodes
55
+ lastLevel = node.level
56
+ // add the current node to the current level nodes
57
+ currentSameLevelNodes.push({
58
+ id :node.id,
59
+ text :node.text,
60
+ children:[],
61
+ })
62
+ // reset the next level nodes after building the subtree
63
+ nextLevelNodes = []
64
+ })
65
+
66
+ // build subtree
67
+ buildSubTree()
68
+
69
+ return currentSameLevelNodes
70
+ }, [])
71
+
72
+ useEffect(() => {
73
+ if (target.current) {
74
+ const nodes = getNodes(target.current)
75
+ const tree = buildTree(nodes)
76
+ setContentTree(tree)
77
+ }
78
+ }, [target.current])
79
+
80
+ return contentTree
81
+ }
82
+
83
+ export default useContentTree
@@ -0,0 +1,59 @@
1
+ import {
2
+ useState, useEffect, useMemo, useCallback,
3
+ } from 'react'
4
+ import debounce from 'lodash/debounce'
5
+
6
+ const useFirstVisibleNode = (nodeIds, config) => {
7
+ const [visibleNodeId, setVisibleNodeId] = useState(null)
8
+
9
+ const { debounceMs = 25 } = config || {}
10
+
11
+ // get headlines nodes - Should only be recalculated when the ids change
12
+ const nodes = useMemo(() => {
13
+ if (nodeIds?.length > 0) {
14
+ return Array.from(document.querySelectorAll(nodeIds.join(',')))
15
+ }
16
+ return []
17
+ }, [nodeIds])
18
+
19
+ const getVisibleNodeId = useCallback(debounce(() => {
20
+ // Get the top postion of each headline node relative to the viewport
21
+ const topOffsets = nodes.map((node) => node.getBoundingClientRect().top)
22
+
23
+ // Get the node closer to zero (the top of the viewport)
24
+ // We have the default offset as Infinity so it's always greater that the first node Y position
25
+ const visibleNode = topOffsets.reduce(
26
+ (prev, currentOffset, currentIndex) => {
27
+ const node = Math.abs(currentOffset - 0) < Math.abs(prev.offset - 0)
28
+ ? { node: nodes[currentIndex], offset: currentOffset }
29
+ : prev
30
+
31
+ return node
32
+ }, { node: nodes[0], offset: Infinity },
33
+ )
34
+
35
+ // Set the visible node id
36
+ setVisibleNodeId(`#${visibleNode?.node?.id}`)
37
+ }, debounceMs), [nodes])
38
+
39
+ // Recalculate the visible node id when the page is scrolled
40
+ useEffect(() => {
41
+ window.addEventListener('scroll', getVisibleNodeId)
42
+
43
+ return () => {
44
+ window.removeEventListener('scroll', getVisibleNodeId)
45
+ }
46
+ }, [getVisibleNodeId])
47
+
48
+ // Recalculate the visible node id when the page is resized
49
+ useEffect(() => {
50
+ window.addEventListener('resize', getVisibleNodeId)
51
+ return () => {
52
+ window.removeEventListener('resize', getVisibleNodeId)
53
+ }
54
+ }, [getVisibleNodeId])
55
+
56
+ return visibleNodeId
57
+ }
58
+
59
+ export default useFirstVisibleNode
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ export { default as ContentTree } from './ContentTree'
@@ -0,0 +1,33 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ @use "@pareto-engineering/bem";
3
+
4
+ $default-margin: 1em;
5
+ $default-padding: 1em;
6
+
7
+ .#{bem.$base}.content-tree{
8
+
9
+ ul {
10
+ list-style: none;
11
+ }
12
+
13
+ .#{bem.$base}.tree {
14
+ position: sticky;
15
+ top: 0;
16
+ padding: 0;
17
+
18
+ .#{bem.$modifier-active} {
19
+ color: var(--main2);
20
+ transition: color 0.2s;
21
+ }
22
+
23
+ li:not(:last-child) {
24
+ margin-bottom: $default-margin;
25
+ ul {
26
+ padding-left: $default-padding;
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+
33
+
package/src/ui/a/index.js CHANGED
@@ -27,3 +27,4 @@ export { Shapes } from './Shapes'
27
27
  export { OvalIllustration } from './OvalIllustration'
28
28
  export { SnapScroller } from './SnapScroller'
29
29
  export { BackgroundGradient } from './BackgroundGradient'
30
+ export { ContentTree } from './ContentTree'
@@ -0,0 +1,111 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ import * as React from 'react'
3
+
4
+ import { useLayoutEffect } from 'react'
5
+
6
+ import PropTypes from 'prop-types'
7
+
8
+ import styleNames from '@pareto-engineering/bem'
9
+
10
+ // Local Definitions
11
+
12
+ const baseClassName = styleNames.base
13
+
14
+ const componentClassName = 'social-media-share-button'
15
+
16
+ /**
17
+ * This is the component description.
18
+ */
19
+ const SocialMediaShareButton = ({
20
+ id,
21
+ className:userClassName,
22
+ style,
23
+ icon,
24
+ // children,
25
+ type,
26
+ color,
27
+ }) => {
28
+ useLayoutEffect(() => {
29
+ import('./styles.scss')
30
+ }, [])
31
+
32
+ const { title } = document
33
+ const link = window.location.href
34
+
35
+ const defaultsMap = {
36
+ facebook:{
37
+ icon:'f',
38
+ link:`https://www.facebook.com/sharer/sharer.php?u=${link}&quote=${title}`,
39
+ },
40
+ twitter:{
41
+ icon:'t',
42
+ link:`https://twitter.com/intent/tweet?text=${title}&url=${link}`,
43
+ },
44
+ }
45
+
46
+ return (
47
+ <a
48
+ href={defaultsMap[type].link}
49
+ target="_blank"
50
+ rel="noreferrer"
51
+ id={id}
52
+ className={[
53
+ baseClassName,
54
+ componentClassName,
55
+ `x-${color || type}`,
56
+ userClassName,
57
+ ]
58
+ .filter((e) => e)
59
+ .join(' ')}
60
+ style={style}
61
+ >
62
+ <button type="button">
63
+ { icon || defaultsMap[type].icon }
64
+ </button>
65
+ </a>
66
+ )
67
+ }
68
+
69
+ SocialMediaShareButton.propTypes = {
70
+ /**
71
+ * The HTML id for this element
72
+ */
73
+ id:PropTypes.string,
74
+
75
+ /**
76
+ * The HTML class names for this element
77
+ */
78
+ className:PropTypes.string,
79
+
80
+ /**
81
+ * The React-written, css properties for this element.
82
+ */
83
+ style:PropTypes.objectOf(PropTypes.string),
84
+
85
+ /**
86
+ * The children JSX
87
+ */
88
+ // children:PropTypes.node,
89
+
90
+ /**
91
+ * The Social Media to Target.
92
+ * If blank, you need to provide both an icon letter (from glyphter) and a link.
93
+ */
94
+ type:PropTypes.oneOf(['twitter', 'facebook']),
95
+
96
+ /**
97
+ * The icon of the social media
98
+ */
99
+ icon:PropTypes.string,
100
+
101
+ /**
102
+ * The button color
103
+ */
104
+ color:PropTypes.string,
105
+ }
106
+
107
+ SocialMediaShareButton.defaultProps = {
108
+ // someProp:false
109
+ }
110
+
111
+ export default SocialMediaShareButton
@@ -0,0 +1,2 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+ export { default as SocialMediaShareButton } from './SocialMediaShareButton'
@@ -0,0 +1,39 @@
1
+ /* @pareto-engineering/generator-front 1.0.12 */
2
+
3
+ @use "@pareto-engineering/bem";
4
+ @use "@aztlan/stylebook/src/mixins";
5
+ @use "@aztlan/stylebook/src/globals" as *;
6
+
7
+ $default-dimensions: 2em;
8
+ $mobile-dimensions: 2.75em;
9
+
10
+ .#{bem.$base}.social-media-share-button{
11
+
12
+ > button {
13
+ appearance: none;
14
+ background: transparent;
15
+ border: 0;
16
+ border-radius: 3em;
17
+ color: var(--x);
18
+ cursor: pointer;
19
+ font-family: "icons", sans-serif;
20
+ height: $default-dimensions;
21
+ transition: all .3s;
22
+ width: $default-dimensions;
23
+
24
+ @include mixins.media($to:$sm-md) {
25
+ height: $mobile-dimensions;
26
+ width: $mobile-dimensions;
27
+ }
28
+
29
+ &:hover {
30
+ background: var(--x);
31
+ color: var(--on-x);
32
+ }
33
+
34
+ &:focus, &:active {
35
+ background: var(--light-x);
36
+ color: var(--on-x);
37
+ }
38
+ }
39
+ }
package/src/ui/c/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { Hero } from './Hero'
2
2
  export { ContentSlides, useContentSlides } from './ContentSlides'
3
3
  export { Shortener } from './Shortener'
4
+ export { SocialMediaShareButton } from './SocialMediaShareButton'