playbook_ui 5.2.0 → 5.3.0.pre.alpha1
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 +4 -4
- data/app/pb_kits/playbook/_playbook.scss +1 -0
- data/app/pb_kits/playbook/data/menu.yml +3 -2
- data/app/pb_kits/playbook/index.js +1 -0
- data/app/pb_kits/playbook/packs/examples.js +2 -0
- data/app/pb_kits/playbook/packs/site_styles/docs/_all.scss +1 -0
- data/app/pb_kits/playbook/packs/site_styles/docs/_bg_light_doc_example.scss +7 -0
- data/app/pb_kits/playbook/pb_badge/_badge.html.erb +1 -0
- data/app/pb_kits/playbook/pb_badge/_badge.jsx +17 -4
- data/app/pb_kits/playbook/pb_card/_card.jsx +4 -1
- data/app/pb_kits/playbook/pb_card/_card.scss +4 -0
- data/app/pb_kits/playbook/pb_card/card.rb +8 -1
- data/app/pb_kits/playbook/pb_card/docs/_card_border_none.html.erb +7 -0
- data/app/pb_kits/playbook/pb_card/docs/_card_border_none.jsx +14 -0
- data/app/pb_kits/playbook/pb_card/docs/_card_border_none.md +1 -0
- data/app/pb_kits/playbook/pb_card/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_card/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_dashboard/pbChartsLightTheme.js +26 -0
- data/app/pb_kits/playbook/pb_gauge/_gauge.html.erb +12 -0
- data/app/pb_kits/playbook/pb_gauge/_gauge.jsx +105 -0
- data/app/pb_kits/playbook/pb_gauge/_gauge.scss +11 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_default.html.erb +11 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_default.jsx +17 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_full_circle.html.erb +14 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_full_circle.jsx +20 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_height.html.erb +15 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_height.jsx +23 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_live_data.jsx +25 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_min_max.html.erb +21 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_min_max.jsx +26 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_sizing.html.erb +33 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_sizing.jsx +42 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_title.html.erb +14 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_title.jsx +20 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_units.html.erb +29 -0
- data/app/pb_kits/playbook/pb_gauge/docs/_gauge_units.jsx +29 -0
- data/app/pb_kits/playbook/pb_gauge/docs/example.yml +22 -0
- data/app/pb_kits/playbook/pb_gauge/docs/index.js +8 -0
- data/app/pb_kits/playbook/pb_gauge/gauge.rb +55 -0
- data/app/pb_kits/playbook/plugins/pb_chart.js +71 -0
- data/lib/playbook/version.rb +1 -1
- metadata +31 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 456edb4112fd56741c0aca0d3e8bcd4b9709abc6a8245dc635bbb9de8a453476
|
|
4
|
+
data.tar.gz: d1f53edaa77edf61e3af081c6b4bd6bf4723214a0d99158ddec934aba8974229
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ddc39e458c5783a6cf0a231399d51b0168a1342fe80ebfba6b8814854c764420e332af64871b8294536d46864f85ab91cd58dd07400df32be10d9cc833fdd0f
|
|
7
|
+
data.tar.gz: 74656e11978287baba8979c672840258db5410f1e744c5377b9fbe831dcd753b3b03c2b1e48f0c759b959d948f042bcf28debca416747c65278a9a9b76920558
|
|
@@ -25,6 +25,7 @@ export FixedConfirmationToast from './pb_fixed_confirmation_toast/_fixed_confirm
|
|
|
25
25
|
export Flex from './pb_flex/_flex.jsx'
|
|
26
26
|
export FlexItem from './pb_flex/_flex_item.jsx'
|
|
27
27
|
export FormPill from './pb_form_pill/_form_pill.jsx'
|
|
28
|
+
export Gauge from './pb_gauge/_gauge.jsx'
|
|
28
29
|
export Hashtag from './pb_hashtag/_hashtag.jsx'
|
|
29
30
|
export Highlight from './pb_highlight/_highlight.jsx'
|
|
30
31
|
export HomeAddressStreet from './pb_home_address_street/_home_address_street.jsx'
|
|
@@ -38,6 +38,7 @@ import * as Filter from 'pb_filter/docs'
|
|
|
38
38
|
import * as FixedConfirmationToast from 'pb_fixed_confirmation_toast/docs'
|
|
39
39
|
import * as Flex from 'pb_flex/docs'
|
|
40
40
|
import * as FormPill from 'pb_form_pill/docs'
|
|
41
|
+
import * as Gauge from 'pb_gauge/docs'
|
|
41
42
|
import * as Hashtag from 'pb_hashtag/docs'
|
|
42
43
|
import * as Highlight from 'pb_highlight/docs'
|
|
43
44
|
import * as HomeAddressStreet from 'pb_home_address_street/docs'
|
|
@@ -118,6 +119,7 @@ WebpackerReact.setup({
|
|
|
118
119
|
...FixedConfirmationToast,
|
|
119
120
|
...Flex,
|
|
120
121
|
...FormPill,
|
|
122
|
+
...Gauge,
|
|
121
123
|
...Hashtag,
|
|
122
124
|
...Highlight,
|
|
123
125
|
...HomeAddressStreet,
|
|
@@ -2,26 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import classnames from 'classnames'
|
|
5
|
-
import { buildCss } from '../utilities/props'
|
|
6
5
|
import { spacing } from '../utilities/spacing.js'
|
|
7
6
|
|
|
7
|
+
import {
|
|
8
|
+
buildAriaProps,
|
|
9
|
+
buildCss,
|
|
10
|
+
buildDataProps,
|
|
11
|
+
} from '../utilities/props'
|
|
12
|
+
|
|
8
13
|
type BadgeProps = {
|
|
14
|
+
aria?: object,
|
|
9
15
|
className?: String,
|
|
10
16
|
dark?: Boolean,
|
|
17
|
+
data?: object,
|
|
11
18
|
id?: String,
|
|
12
|
-
text?: String,
|
|
13
|
-
variant?: "success" | "warning" | "error" | "info" | "neutral",
|
|
14
19
|
rounded?: Boolean,
|
|
20
|
+
text?: String,
|
|
21
|
+
variant?: "error" | "info" | "neutral" | "primary" | "success" | "warning",
|
|
15
22
|
}
|
|
16
23
|
const Badge = (props: BadgeProps) => {
|
|
17
24
|
const {
|
|
25
|
+
aria = {},
|
|
18
26
|
className,
|
|
19
27
|
dark = false,
|
|
28
|
+
data = {},
|
|
20
29
|
id,
|
|
30
|
+
rounded = false,
|
|
21
31
|
text,
|
|
22
32
|
variant = 'neutral',
|
|
23
|
-
rounded = false,
|
|
24
33
|
} = props
|
|
34
|
+
const ariaProps = buildAriaProps(aria)
|
|
35
|
+
const dataProps = buildDataProps(data)
|
|
25
36
|
const css = classnames(
|
|
26
37
|
className,
|
|
27
38
|
buildCss('pb_badge_kit', variant, {
|
|
@@ -33,6 +44,8 @@ const Badge = (props: BadgeProps) => {
|
|
|
33
44
|
|
|
34
45
|
return (
|
|
35
46
|
<div
|
|
47
|
+
{...ariaProps}
|
|
48
|
+
{...dataProps}
|
|
36
49
|
className={css}
|
|
37
50
|
id={id}
|
|
38
51
|
>
|
|
@@ -16,6 +16,7 @@ type CardPropTypes = {
|
|
|
16
16
|
selected?: Boolean,
|
|
17
17
|
shadow?: "none" | "deep" | "deeper" | "deepest",
|
|
18
18
|
dark?: Boolean,
|
|
19
|
+
borderNone?: Boolean,
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
type CardHeaderProps = {
|
|
@@ -62,9 +63,11 @@ const Card = (props: CardPropTypes) => {
|
|
|
62
63
|
highlight = {},
|
|
63
64
|
selected = false,
|
|
64
65
|
shadow = 'none',
|
|
66
|
+
borderNone = false,
|
|
65
67
|
} = props
|
|
66
68
|
const bodyCSS = buildCss('pb_card_body_kit')
|
|
67
|
-
const
|
|
69
|
+
const borderCSS = borderNone == true ? 'border_none' : ''
|
|
70
|
+
const cardCss = buildCss('pb_card_kit', `shadow_${shadow}`, `${borderCSS}`, {
|
|
68
71
|
dark: dark,
|
|
69
72
|
selected,
|
|
70
73
|
deselected: !selected,
|
|
@@ -15,6 +15,8 @@ module Playbook
|
|
|
15
15
|
default: {}
|
|
16
16
|
prop :dark, type: Playbook::Props::Boolean,
|
|
17
17
|
default: false
|
|
18
|
+
prop :border_none, type: Playbook::Props::Boolean,
|
|
19
|
+
default: false
|
|
18
20
|
|
|
19
21
|
def classname
|
|
20
22
|
generate_classname("pb_card_kit",
|
|
@@ -22,7 +24,8 @@ module Playbook
|
|
|
22
24
|
shadow_class,
|
|
23
25
|
highlight_position_class,
|
|
24
26
|
highlight_color_class,
|
|
25
|
-
dark_class
|
|
27
|
+
dark_class,
|
|
28
|
+
border_class)
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def body_padding
|
|
@@ -55,6 +58,10 @@ module Playbook
|
|
|
55
58
|
def dark_class
|
|
56
59
|
dark ? "dark" : nil
|
|
57
60
|
end
|
|
61
|
+
|
|
62
|
+
def border_class
|
|
63
|
+
border_none == true ? "border_none" : nil
|
|
64
|
+
end
|
|
58
65
|
end
|
|
59
66
|
end
|
|
60
67
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Remove card border <b>only</b> for dashboard cards.
|
|
@@ -9,6 +9,7 @@ examples:
|
|
|
9
9
|
- card_shadow: Shadow Size
|
|
10
10
|
- card_content: Content Size
|
|
11
11
|
- card_separator: Separator Card
|
|
12
|
+
- card_border_none: No Border
|
|
12
13
|
react:
|
|
13
14
|
- card_light: Default
|
|
14
15
|
- card_dark: Dark Cards
|
|
@@ -19,3 +20,4 @@ examples:
|
|
|
19
20
|
- card_shadow: Shadow Size
|
|
20
21
|
- card_content: Content Size
|
|
21
22
|
- card_separator: Separator Card
|
|
23
|
+
- card_border_none: No Border
|
|
@@ -7,3 +7,4 @@ export { default as CardShadow } from './_card_shadow.jsx'
|
|
|
7
7
|
export { default as CardContent } from './_card_content.jsx'
|
|
8
8
|
export { default as CardSeparator } from './_card_separator.jsx'
|
|
9
9
|
export { default as CardDark } from './_card_dark.jsx'
|
|
10
|
+
export { default as CardBorderNone } from './_card_border_none.jsx'
|
|
@@ -124,6 +124,13 @@ const highchartsTheme = {
|
|
|
124
124
|
fontSize: typography.text_smaller,
|
|
125
125
|
},
|
|
126
126
|
},
|
|
127
|
+
// specific to gauge
|
|
128
|
+
// unfilled gauge color
|
|
129
|
+
pane: {
|
|
130
|
+
background: {
|
|
131
|
+
borderColor: colors.border_light,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
127
134
|
|
|
128
135
|
plotOptions: {
|
|
129
136
|
series: {
|
|
@@ -144,6 +151,25 @@ const highchartsTheme = {
|
|
|
144
151
|
threshold: null,
|
|
145
152
|
},
|
|
146
153
|
|
|
154
|
+
// GAUGE STYLES
|
|
155
|
+
solidgauge: {
|
|
156
|
+
borderColor: colors.primary,
|
|
157
|
+
borderWidth: 20,
|
|
158
|
+
radius: 90,
|
|
159
|
+
innerRadius: '90%',
|
|
160
|
+
dataLabels: {
|
|
161
|
+
borderWidth: 0,
|
|
162
|
+
color: colors.text_lt_default,
|
|
163
|
+
enabled: true,
|
|
164
|
+
style: {
|
|
165
|
+
fontFamily: typography.font_family_base,
|
|
166
|
+
fontWeight: typography.regular,
|
|
167
|
+
fontSize: typography.heading_2,
|
|
168
|
+
},
|
|
169
|
+
y: -26,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
|
|
147
173
|
// PIE STYLES
|
|
148
174
|
pie: {
|
|
149
175
|
colors: [
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%= content_tag(:div, "",
|
|
2
|
+
id: object.id,
|
|
3
|
+
data: object.data,
|
|
4
|
+
class: object.classname) %>
|
|
5
|
+
<% content_for :pb_js do %>
|
|
6
|
+
<%= javascript_tag do %>
|
|
7
|
+
window.addEventListener('DOMContentLoaded', function() {
|
|
8
|
+
new pbChart('.selector', <%= object.chart_options %>)
|
|
9
|
+
})
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/* @flow */
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useRef } from 'react'
|
|
4
|
+
import { pbChart } from '../'
|
|
5
|
+
import { spacing } from '../utilities/spacing.js'
|
|
6
|
+
import classnames from 'classnames'
|
|
7
|
+
import Highcharts from 'highcharts'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
buildAriaProps,
|
|
11
|
+
buildCss,
|
|
12
|
+
buildDataProps,
|
|
13
|
+
} from '../utilities/props'
|
|
14
|
+
|
|
15
|
+
type GaugeProps = {
|
|
16
|
+
aria: Object,
|
|
17
|
+
className?: String,
|
|
18
|
+
chartData?: Array,
|
|
19
|
+
data?: Object,
|
|
20
|
+
fullCircle: Boolean,
|
|
21
|
+
height: String,
|
|
22
|
+
id?: String,
|
|
23
|
+
max: Number,
|
|
24
|
+
min: Number,
|
|
25
|
+
prefix: String,
|
|
26
|
+
showLabels: Boolean,
|
|
27
|
+
style: String,
|
|
28
|
+
suffix: String,
|
|
29
|
+
title: String,
|
|
30
|
+
tooltipHtml: String,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Gauge = (props: GaugeProps) => {
|
|
34
|
+
const {
|
|
35
|
+
aria = {},
|
|
36
|
+
className,
|
|
37
|
+
chartData = [{ name: 'Name', value: 0 }],
|
|
38
|
+
data = {},
|
|
39
|
+
fullCircle = false,
|
|
40
|
+
height = null,
|
|
41
|
+
id,
|
|
42
|
+
max = 100,
|
|
43
|
+
min = 0,
|
|
44
|
+
prefix = '',
|
|
45
|
+
showLabels = false,
|
|
46
|
+
style = 'solidgauge',
|
|
47
|
+
suffix = '',
|
|
48
|
+
title = '',
|
|
49
|
+
tooltipHtml = '<span style="font-weight: bold; color:{point.color};">●</span>{point.name}: ' + '<b>{point.y}</b>',
|
|
50
|
+
} = props
|
|
51
|
+
|
|
52
|
+
const ariaProps = buildAriaProps(aria)
|
|
53
|
+
const dataProps = buildDataProps(data)
|
|
54
|
+
|
|
55
|
+
const css = buildCss({
|
|
56
|
+
'pb_gauge_kit': true,
|
|
57
|
+
})
|
|
58
|
+
// Runs first time component Renders
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
chartData.forEach((obj) => {
|
|
61
|
+
obj.y = obj.value
|
|
62
|
+
delete obj.value
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
new pbChart('.selector', {
|
|
66
|
+
id: id,
|
|
67
|
+
chartData: chartData,
|
|
68
|
+
circumference: fullCircle ? [0, 360] : [-100, 100],
|
|
69
|
+
height: height,
|
|
70
|
+
min: min,
|
|
71
|
+
max: max,
|
|
72
|
+
prefix: prefix,
|
|
73
|
+
title: title,
|
|
74
|
+
suffix: suffix,
|
|
75
|
+
showLabels: showLabels,
|
|
76
|
+
style: style,
|
|
77
|
+
tooltipHtml: tooltipHtml,
|
|
78
|
+
type: 'gauge',
|
|
79
|
+
})
|
|
80
|
+
}, [])
|
|
81
|
+
|
|
82
|
+
const componentDidMount = useRef(false)
|
|
83
|
+
// Doesn't run the first time but runs every subsequent render
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (componentDidMount.current) {
|
|
86
|
+
Highcharts.charts.forEach((chart) => {
|
|
87
|
+
if (chart.renderTo.id === id) {
|
|
88
|
+
chart.series[0].setData([chartData[0].value])
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
} else {
|
|
92
|
+
componentDidMount.current = true
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
{...ariaProps}
|
|
98
|
+
{...dataProps}
|
|
99
|
+
className={classnames(css, className, spacing(props))}
|
|
100
|
+
id={id}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export default Gauge
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const data = [
|
|
5
|
+
{ name: 'Point1', value: 45 },
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const GaugeDefault = () => (
|
|
9
|
+
<div>
|
|
10
|
+
<Gauge
|
|
11
|
+
chartData={data}
|
|
12
|
+
id="gauge-default"
|
|
13
|
+
/>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
export default GaugeDefault
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const data = [
|
|
5
|
+
{ name: 'Capacity', value: 75 },
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const GaugeFullCircle = () => (
|
|
9
|
+
<div>
|
|
10
|
+
<Gauge
|
|
11
|
+
chartData={data}
|
|
12
|
+
fullCircle
|
|
13
|
+
id="gauge-full-circle"
|
|
14
|
+
suffix="%"
|
|
15
|
+
title="Seating Capacity"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
export default GaugeFullCircle
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<%= pb_rails("gauge", props: {
|
|
2
|
+
id: "gauge-height-px",
|
|
3
|
+
chart_data: [{ name: "Pixel Height", value: 400 }],
|
|
4
|
+
height: '400',
|
|
5
|
+
title: "Fixed Height in Pixels",
|
|
6
|
+
suffix: "px"
|
|
7
|
+
}) %>
|
|
8
|
+
|
|
9
|
+
<%= pb_rails("gauge", props: {
|
|
10
|
+
id: "gauge-height-percent",
|
|
11
|
+
chart_data: [{ name: "Percentage Height", value: 45 }],
|
|
12
|
+
height: '45%',
|
|
13
|
+
title: "Height as Percentage of Width",
|
|
14
|
+
suffix: "%"
|
|
15
|
+
}) %>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const GaugeHeight = () => (
|
|
5
|
+
<div>
|
|
6
|
+
<Gauge
|
|
7
|
+
chartData={[ { name: 'Name', value: 400 } ]}
|
|
8
|
+
height="400"
|
|
9
|
+
id="gauge-height-px"
|
|
10
|
+
suffix="px"
|
|
11
|
+
title="Fixed Height in Pixels"
|
|
12
|
+
/>
|
|
13
|
+
<Gauge
|
|
14
|
+
chartData={[ { name: 'Name', value: 45 } ]}
|
|
15
|
+
height="45%"
|
|
16
|
+
id="gauge-height-percent"
|
|
17
|
+
suffix="%"
|
|
18
|
+
title="Height as Percentage of Width"
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export default GaugeHeight
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { Button, Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const GaugeLiveData = () => {
|
|
5
|
+
const [value, setValue] = useState(50)
|
|
6
|
+
|
|
7
|
+
const updateState = () => {
|
|
8
|
+
setValue(Math.floor(Math.random() * 100))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div>
|
|
13
|
+
<Button
|
|
14
|
+
onClick={updateState}
|
|
15
|
+
text="Update State"
|
|
16
|
+
/>
|
|
17
|
+
<Gauge
|
|
18
|
+
chartData={[{ name: 'Name', value: value }]}
|
|
19
|
+
id="gauge-live-data"
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default GaugeLiveData
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%= pb_rails("title", props: {
|
|
2
|
+
text: "Min defaults to 0, Max to 100.",
|
|
3
|
+
tag: "h3",
|
|
4
|
+
size: 3
|
|
5
|
+
}) %>
|
|
6
|
+
|
|
7
|
+
<% data = [
|
|
8
|
+
{
|
|
9
|
+
name: 'Rating',
|
|
10
|
+
value: 4.5,
|
|
11
|
+
}
|
|
12
|
+
]%>
|
|
13
|
+
|
|
14
|
+
<%= pb_rails("gauge", props: {
|
|
15
|
+
id: "gauge-min-max",
|
|
16
|
+
chart_data: data,
|
|
17
|
+
min: 0,
|
|
18
|
+
max: 5,
|
|
19
|
+
show_labels: true,
|
|
20
|
+
title: "Product Rating"
|
|
21
|
+
}) %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge, Title } from '../../'
|
|
3
|
+
|
|
4
|
+
const data = [{
|
|
5
|
+
name: 'Rating', value: 4.5,
|
|
6
|
+
}]
|
|
7
|
+
|
|
8
|
+
const GaugeMinMax = () => (
|
|
9
|
+
<div>
|
|
10
|
+
<Title
|
|
11
|
+
size={3}
|
|
12
|
+
tag="h3"
|
|
13
|
+
text="Min defaults to 0, Max to 100."
|
|
14
|
+
/>
|
|
15
|
+
<Gauge
|
|
16
|
+
chartData={data}
|
|
17
|
+
id="gauge-min-max"
|
|
18
|
+
max={5}
|
|
19
|
+
min={0}
|
|
20
|
+
showLabels
|
|
21
|
+
title="Product Rating"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export default GaugeMinMax
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<%= pb_rails("title", props: {
|
|
2
|
+
text: "Gauge resizes dynamically to fit whatever element it's placed within.",
|
|
3
|
+
tag: "h3",
|
|
4
|
+
size: 3
|
|
5
|
+
}) %>
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
<%= pb_rails("flex", props: {wrap: true}) do %>
|
|
9
|
+
<%= pb_rails("flex/flex_item", props: {fixed_size: "400px"}) do %>
|
|
10
|
+
<%= pb_rails("gauge", props: {
|
|
11
|
+
id: "gauge-sizing4",
|
|
12
|
+
chart_data: [{ name: "Name", value: 100 }],
|
|
13
|
+
}) %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<%= pb_rails("flex/flex_item", props: {fixed_size: "300px"}) do %>
|
|
16
|
+
<%= pb_rails("gauge", props: {
|
|
17
|
+
id: "gauge-sizing3",
|
|
18
|
+
chart_data: [{ name: "Name", value: 75 }],
|
|
19
|
+
}) %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<%= pb_rails("flex/flex_item", props: {fixed_size: "200px"}) do %>
|
|
22
|
+
<%= pb_rails("gauge", props: {
|
|
23
|
+
id: "gauge-sizing2",
|
|
24
|
+
chart_data: [{ name: "Name", value: 50 }],
|
|
25
|
+
}) %>
|
|
26
|
+
<% end %>
|
|
27
|
+
<%= pb_rails("flex/flex_item", props: {fixed_size: "125px"}) do %>
|
|
28
|
+
<%= pb_rails("gauge", props: {
|
|
29
|
+
id: "gauge-sizing1",
|
|
30
|
+
chart_data: [{ name: "Name", value: 25 }],
|
|
31
|
+
}) %>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Flex, FlexItem, Gauge, Title } from '../../'
|
|
3
|
+
|
|
4
|
+
const GaugeSizing = () => (
|
|
5
|
+
<div>
|
|
6
|
+
<Title
|
|
7
|
+
size={3}
|
|
8
|
+
tag="h3"
|
|
9
|
+
text="Gauge resizes dynamically to fit whatever element it's placed within."
|
|
10
|
+
/>
|
|
11
|
+
<Flex
|
|
12
|
+
wrap
|
|
13
|
+
>
|
|
14
|
+
<FlexItem fixedSize="400px">
|
|
15
|
+
<Gauge
|
|
16
|
+
chartData={[ { name: 'Point1', value: 100 } ]}
|
|
17
|
+
id="gauge-sizing4"
|
|
18
|
+
/>
|
|
19
|
+
</FlexItem>
|
|
20
|
+
<FlexItem fixedSize="300px">
|
|
21
|
+
<Gauge
|
|
22
|
+
chartData={[ { name: 'Point1', value: 75 } ]}
|
|
23
|
+
id="gauge-sizing3"
|
|
24
|
+
/>
|
|
25
|
+
</FlexItem>
|
|
26
|
+
<FlexItem fixedSize="200px">
|
|
27
|
+
<Gauge
|
|
28
|
+
chartData={[ { name: 'Point1', value: 50 } ]}
|
|
29
|
+
id="gauge-sizing2"
|
|
30
|
+
/>
|
|
31
|
+
</FlexItem>
|
|
32
|
+
<FlexItem fixedSize="125px">
|
|
33
|
+
<Gauge
|
|
34
|
+
chartData={[ { name: 'Point1', value: 25 } ]}
|
|
35
|
+
id="gauge-sizing1"
|
|
36
|
+
/>
|
|
37
|
+
</FlexItem>
|
|
38
|
+
</Flex>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export default GaugeSizing
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const data = [
|
|
5
|
+
{ name: 'Score', value: 780 },
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const GaugeTitle = () => (
|
|
9
|
+
<div>
|
|
10
|
+
<Gauge
|
|
11
|
+
chartData={data}
|
|
12
|
+
id="gauge-title"
|
|
13
|
+
max={850}
|
|
14
|
+
min={300}
|
|
15
|
+
title="Credit Score"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
export default GaugeTitle
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<% data1 = [
|
|
2
|
+
{
|
|
3
|
+
name: 'Data Used',
|
|
4
|
+
value: 32,
|
|
5
|
+
}
|
|
6
|
+
]%>
|
|
7
|
+
|
|
8
|
+
<%= pb_rails("gauge", props: {
|
|
9
|
+
id: "gauge-units1",
|
|
10
|
+
chart_data: data1,
|
|
11
|
+
title: "Data Usage",
|
|
12
|
+
suffix: "GB"
|
|
13
|
+
}) %>
|
|
14
|
+
|
|
15
|
+
<% data2 = [
|
|
16
|
+
{
|
|
17
|
+
name: 'Sales to Date',
|
|
18
|
+
value: 65,
|
|
19
|
+
}
|
|
20
|
+
]%>
|
|
21
|
+
|
|
22
|
+
<%= pb_rails("gauge", props: {
|
|
23
|
+
id: "gauge-units2",
|
|
24
|
+
chart_data: data2,
|
|
25
|
+
title: "Sales Goal",
|
|
26
|
+
prefix: "$",
|
|
27
|
+
suffix: "k",
|
|
28
|
+
|
|
29
|
+
}) %>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Gauge } from '../../'
|
|
3
|
+
|
|
4
|
+
const data1 = [
|
|
5
|
+
{ name: 'Data Used', value: 32 },
|
|
6
|
+
]
|
|
7
|
+
const data2 = [
|
|
8
|
+
{ name: 'Sales to Date', value: 65 },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
const GaugeUnits = () => (
|
|
12
|
+
<div>
|
|
13
|
+
<Gauge
|
|
14
|
+
chartData={data1}
|
|
15
|
+
id="gauge-units1"
|
|
16
|
+
suffix="GB"
|
|
17
|
+
title="Data Usage"
|
|
18
|
+
/>
|
|
19
|
+
<Gauge
|
|
20
|
+
chartData={data2}
|
|
21
|
+
id="gauge-units2"
|
|
22
|
+
prefix="$"
|
|
23
|
+
suffix="k"
|
|
24
|
+
title="Sales Goal"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export default GaugeUnits
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
examples:
|
|
2
|
+
|
|
3
|
+
rails:
|
|
4
|
+
- gauge_default: Default
|
|
5
|
+
- gauge_title: Title
|
|
6
|
+
- gauge_units: Units
|
|
7
|
+
- gauge_full_circle: Full Circle
|
|
8
|
+
- gauge_min_max: Min Max Labels
|
|
9
|
+
- gauge_sizing: Sizing
|
|
10
|
+
- gauge_height: Height
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
react:
|
|
14
|
+
- gauge_default: Default
|
|
15
|
+
- gauge_title: Title
|
|
16
|
+
- gauge_units: Units
|
|
17
|
+
- gauge_full_circle: Full Circle
|
|
18
|
+
- gauge_min_max: Min Max Labels
|
|
19
|
+
- gauge_sizing: Sizing
|
|
20
|
+
- gauge_height: Height
|
|
21
|
+
- gauge_live_data: Live Data
|
|
22
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { default as GaugeDefault } from './_gauge_default.jsx'
|
|
2
|
+
export { default as GaugeFullCircle } from './_gauge_full_circle.jsx'
|
|
3
|
+
export { default as GaugeHeight } from './_gauge_height.jsx'
|
|
4
|
+
export { default as GaugeMinMax } from './_gauge_min_max.jsx'
|
|
5
|
+
export { default as GaugeSizing } from './_gauge_sizing.jsx'
|
|
6
|
+
export { default as GaugeTitle } from './_gauge_title.jsx'
|
|
7
|
+
export { default as GaugeUnits } from './_gauge_units.jsx'
|
|
8
|
+
export { default as GaugeLiveData } from './_gauge_live_data.jsx'
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Playbook
|
|
4
|
+
module PbGauge
|
|
5
|
+
class Gauge
|
|
6
|
+
include Playbook::Props
|
|
7
|
+
|
|
8
|
+
partial "pb_gauge/gauge"
|
|
9
|
+
|
|
10
|
+
prop :chart_data, type: Playbook::Props::Array,
|
|
11
|
+
default: [{ name: "Name", value: 0 }]
|
|
12
|
+
prop :style, type: Playbook::Props::Enum,
|
|
13
|
+
values: %w[solidgauge],
|
|
14
|
+
default: "solidgauge"
|
|
15
|
+
prop :title, type: Playbook::Props::String, default: ""
|
|
16
|
+
prop :prefix, type: Playbook::Props::String, default: ""
|
|
17
|
+
prop :suffix, type: Playbook::Props::String, default: ""
|
|
18
|
+
prop :height, type: Playbook::Props::String, default: nil
|
|
19
|
+
prop :tooltip_html, default: '<span style="font-weight: bold; color:{point.color};">●</span>
|
|
20
|
+
{point.name}: ' + '<b>{point.y}
|
|
21
|
+
</b>'
|
|
22
|
+
prop :full_circle, type: Playbook::Props::Boolean, default: false
|
|
23
|
+
prop :show_labels, type: Playbook::Props::Boolean, default: false
|
|
24
|
+
prop :min, type: Playbook::Props::Numeric, default: 0
|
|
25
|
+
prop :max, type: Playbook::Props::Numeric, default: 100
|
|
26
|
+
|
|
27
|
+
def chart_data_formatted
|
|
28
|
+
chart_data.map { |hash| hash[:y] = hash.delete :value }
|
|
29
|
+
chart_data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def chart_options
|
|
33
|
+
{
|
|
34
|
+
id: id,
|
|
35
|
+
chartData: chart_data_formatted,
|
|
36
|
+
circumference: full_circle ? [0, 360] : [-100, 100],
|
|
37
|
+
height: height,
|
|
38
|
+
min: min,
|
|
39
|
+
max: max,
|
|
40
|
+
title: title,
|
|
41
|
+
suffix: suffix,
|
|
42
|
+
prefix: prefix,
|
|
43
|
+
showLabels: show_labels,
|
|
44
|
+
style: style,
|
|
45
|
+
tooltipHtml: tooltip_html,
|
|
46
|
+
type: "gauge",
|
|
47
|
+
}.to_json.html_safe
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def classname
|
|
51
|
+
generate_classname("pb_gauge_kit")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -3,6 +3,8 @@ import Highcharts from 'highcharts'
|
|
|
3
3
|
import { highchartsTheme } from '../pb_dashboard/pbChartsLightTheme.js'
|
|
4
4
|
|
|
5
5
|
require('highcharts/modules/variable-pie')(Highcharts)
|
|
6
|
+
import highchartsMore from 'highcharts/highcharts-more.js'
|
|
7
|
+
import solidGauge from 'highcharts/modules/solid-gauge.js'
|
|
6
8
|
|
|
7
9
|
class pbChart {
|
|
8
10
|
defaults = {
|
|
@@ -31,11 +33,80 @@ class pbChart {
|
|
|
31
33
|
|
|
32
34
|
if (this.options.type == 'variablepie' || this.options.type == 'pie'){
|
|
33
35
|
this.setupPieChart()
|
|
36
|
+
} else if (this.options.type == 'gauge') {
|
|
37
|
+
this.setupGauge()
|
|
34
38
|
} else {
|
|
35
39
|
this.setupChart()
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
setupGauge() {
|
|
44
|
+
highchartsMore(Highcharts)
|
|
45
|
+
solidGauge(Highcharts)
|
|
46
|
+
Highcharts.setOptions(highchartsTheme)
|
|
47
|
+
|
|
48
|
+
Highcharts.chart(this.defaults.id, {
|
|
49
|
+
chart: {
|
|
50
|
+
type: this.defaults.style,
|
|
51
|
+
height: this.defaults.height,
|
|
52
|
+
},
|
|
53
|
+
title: {
|
|
54
|
+
text: this.defaults.title,
|
|
55
|
+
},
|
|
56
|
+
yAxis: {
|
|
57
|
+
min: this.defaults.min,
|
|
58
|
+
max: this.defaults.max,
|
|
59
|
+
lineWidth: 0,
|
|
60
|
+
tickWidth: 0,
|
|
61
|
+
minorTickInterval: null,
|
|
62
|
+
tickAmount: 2,
|
|
63
|
+
tickPositions: [this.defaults.min, this.defaults.max],
|
|
64
|
+
labels: {
|
|
65
|
+
y: 26,
|
|
66
|
+
enabled: this.defaults.showLabels,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
credits: false,
|
|
70
|
+
series: [
|
|
71
|
+
{
|
|
72
|
+
data: this.defaults.chartData,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
pane: {
|
|
76
|
+
center: ['50%', '50%'],
|
|
77
|
+
size: '90%',
|
|
78
|
+
startAngle: this.defaults.circumference[0],
|
|
79
|
+
endAngle: this.defaults.circumference[1],
|
|
80
|
+
background: {
|
|
81
|
+
borderWidth: 20,
|
|
82
|
+
innerRadius: '90%',
|
|
83
|
+
outerRadius: '90%',
|
|
84
|
+
shape: 'arc',
|
|
85
|
+
className: 'gauge-pane',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
tooltip: {
|
|
89
|
+
headerFormat: '',
|
|
90
|
+
pointFormat: this.defaults.tooltipHtml,
|
|
91
|
+
followPointer: true,
|
|
92
|
+
},
|
|
93
|
+
plotOptions: {
|
|
94
|
+
solidgauge: {
|
|
95
|
+
dataLabels: {
|
|
96
|
+
format: `<span class="prefix">${this.defaults.prefix}</span>` +
|
|
97
|
+
'<span class="fix">{y:,f}</span>' +
|
|
98
|
+
`<span class="suffix">${this.defaults.suffix}</span>`,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
document.querySelectorAll('.gauge-pane').forEach((pane) => pane.setAttribute('stroke-linejoin', 'round'))
|
|
104
|
+
if (document.querySelector('.prefix')) {
|
|
105
|
+
document.querySelectorAll('.prefix').forEach((prefix) => prefix.setAttribute('y', '28'))
|
|
106
|
+
document.querySelectorAll('.fix').forEach((fix) => fix.setAttribute('y', '38'))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
39
110
|
setupPieChart() {
|
|
40
111
|
Highcharts.setOptions(highchartsTheme)
|
|
41
112
|
|
data/lib/playbook/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: playbook_ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.3.0.pre.alpha1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Power UX
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2020-07-
|
|
12
|
+
date: 2020-07-17 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: actionpack
|
|
@@ -411,6 +411,7 @@ files:
|
|
|
411
411
|
- app/pb_kits/playbook/packs/site_styles/_scaffold.scss
|
|
412
412
|
- app/pb_kits/playbook/packs/site_styles/_site-style.scss
|
|
413
413
|
- app/pb_kits/playbook/packs/site_styles/docs/_all.scss
|
|
414
|
+
- app/pb_kits/playbook/packs/site_styles/docs/_bg_light_doc_example.scss
|
|
414
415
|
- app/pb_kits/playbook/packs/site_styles/docs/_code_snippet.scss
|
|
415
416
|
- app/pb_kits/playbook/packs/site_styles/docs/_color_utilities.scss
|
|
416
417
|
- app/pb_kits/playbook/packs/site_styles/docs/_flex_examples.scss
|
|
@@ -543,6 +544,9 @@ files:
|
|
|
543
544
|
- app/pb_kits/playbook/pb_card/card_header.rb
|
|
544
545
|
- app/pb_kits/playbook/pb_card/child_kits/_card_body.html.erb
|
|
545
546
|
- app/pb_kits/playbook/pb_card/child_kits/_card_header.html.erb
|
|
547
|
+
- app/pb_kits/playbook/pb_card/docs/_card_border_none.html.erb
|
|
548
|
+
- app/pb_kits/playbook/pb_card/docs/_card_border_none.jsx
|
|
549
|
+
- app/pb_kits/playbook/pb_card/docs/_card_border_none.md
|
|
546
550
|
- app/pb_kits/playbook/pb_card/docs/_card_content.html.erb
|
|
547
551
|
- app/pb_kits/playbook/pb_card/docs/_card_content.jsx
|
|
548
552
|
- app/pb_kits/playbook/pb_card/docs/_card_dark.html.erb
|
|
@@ -838,6 +842,27 @@ files:
|
|
|
838
842
|
- app/pb_kits/playbook/pb_form_pill/docs/example.yml
|
|
839
843
|
- app/pb_kits/playbook/pb_form_pill/docs/index.js
|
|
840
844
|
- app/pb_kits/playbook/pb_form_pill/form_pill.rb
|
|
845
|
+
- app/pb_kits/playbook/pb_gauge/_gauge.html.erb
|
|
846
|
+
- app/pb_kits/playbook/pb_gauge/_gauge.jsx
|
|
847
|
+
- app/pb_kits/playbook/pb_gauge/_gauge.scss
|
|
848
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_default.html.erb
|
|
849
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_default.jsx
|
|
850
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_full_circle.html.erb
|
|
851
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_full_circle.jsx
|
|
852
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_height.html.erb
|
|
853
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_height.jsx
|
|
854
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_live_data.jsx
|
|
855
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_min_max.html.erb
|
|
856
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_min_max.jsx
|
|
857
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_sizing.html.erb
|
|
858
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_sizing.jsx
|
|
859
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_title.html.erb
|
|
860
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_title.jsx
|
|
861
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_units.html.erb
|
|
862
|
+
- app/pb_kits/playbook/pb_gauge/docs/_gauge_units.jsx
|
|
863
|
+
- app/pb_kits/playbook/pb_gauge/docs/example.yml
|
|
864
|
+
- app/pb_kits/playbook/pb_gauge/docs/index.js
|
|
865
|
+
- app/pb_kits/playbook/pb_gauge/gauge.rb
|
|
841
866
|
- app/pb_kits/playbook/pb_hashtag/_hashtag.html.erb
|
|
842
867
|
- app/pb_kits/playbook/pb_hashtag/_hashtag.jsx
|
|
843
868
|
- app/pb_kits/playbook/pb_hashtag/_hashtag.scss
|
|
@@ -1809,11 +1834,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
1809
1834
|
version: '0'
|
|
1810
1835
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1811
1836
|
requirements:
|
|
1812
|
-
- - "
|
|
1837
|
+
- - ">"
|
|
1813
1838
|
- !ruby/object:Gem::Version
|
|
1814
|
-
version:
|
|
1839
|
+
version: 1.3.1
|
|
1815
1840
|
requirements: []
|
|
1816
|
-
|
|
1841
|
+
rubyforge_project:
|
|
1842
|
+
rubygems_version: 2.7.3
|
|
1817
1843
|
signing_key:
|
|
1818
1844
|
specification_version: 4
|
|
1819
1845
|
summary: Playbook Design System
|