playbook_ui 16.6.0.pre.alpha.play294216115 → 16.6.0.pre.alpha.play294216129
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/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +18 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +11 -0
- data/app/pb_kits/playbook/pb_icon/_icon.tsx +99 -12
- data/app/pb_kits/playbook/pb_icon/icon.rb +23 -11
- data/app/pb_kits/playbook/utilities/icons/generated/playbookIconPathIndex.ts +570 -0
- data/app/pb_kits/playbook/utilities/icons/playbookIconResolver.ts +94 -8
- data/dist/chunks/_typeahead-BCNQyt85.js +1 -0
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/lib/playbook/engine.rb +6 -0
- data/lib/playbook/version.rb +1 -1
- metadata +4 -3
- data/dist/chunks/_typeahead-BO3J1J3f.js +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1e05534a3f97a66180278f383ceebd88308b9ab9e457d114db537950d5635b1d
|
|
4
|
+
data.tar.gz: 39a298031f506647efb1e3236753c31dac0f2ee077f1c680d0ccb7f5762a6fba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04b9269da508cd3c91abf55b85c2d077e0fd49b56e68448e2a89c642ae5c3d0bf0dba05d0541c8f7363960dacc67129f44f72969805f88e1d87da54f15879b3c
|
|
7
|
+
data.tar.gz: e61a63d3488d8c7adb0123a3b0c4aba424ab2a220b48ed176881a8e5af9feb8dcb3847b11feb47c1a774a96b39fc91580c6bbef4f6f4dadda9f820649452654c
|
|
@@ -26,6 +26,24 @@ const DropdownWithConstrainHeight = (props) => {
|
|
|
26
26
|
options={options}
|
|
27
27
|
{...props}
|
|
28
28
|
/>
|
|
29
|
+
|
|
30
|
+
<br />
|
|
31
|
+
|
|
32
|
+
<Dropdown
|
|
33
|
+
label="Subcomponent With Constrain Height"
|
|
34
|
+
options={options}
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
<Dropdown.Trigger />
|
|
38
|
+
<Dropdown.Container constrainHeight>
|
|
39
|
+
{options.map((option) => (
|
|
40
|
+
<Dropdown.Option
|
|
41
|
+
key={option.id}
|
|
42
|
+
option={option}
|
|
43
|
+
/>
|
|
44
|
+
))}
|
|
45
|
+
</Dropdown.Container>
|
|
46
|
+
</Dropdown>
|
|
29
47
|
</>
|
|
30
48
|
)
|
|
31
49
|
}
|
|
@@ -18,3 +18,14 @@
|
|
|
18
18
|
constrain_height: true,
|
|
19
19
|
label: "With Constrain Height"
|
|
20
20
|
}) %>
|
|
21
|
+
|
|
22
|
+
<br>
|
|
23
|
+
|
|
24
|
+
<%= pb_rails("dropdown", props: {options: options, label: "Subcomponent With Constrain Height"}) do %>
|
|
25
|
+
<%= pb_rails("dropdown/dropdown_trigger") %>
|
|
26
|
+
<%= pb_rails("dropdown/dropdown_container", props: { constrain_height: true }) do %>
|
|
27
|
+
<% options.each do |option| %>
|
|
28
|
+
<%= pb_rails("dropdown/dropdown_option", props: {option: option}) %>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<% end %>
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import React, { ReactSVGElement } from 'react'
|
|
1
|
+
import React, { ReactSVGElement, useEffect, useState } from 'react'
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
import { buildAriaProps, buildDataProps, buildHtmlProps } from '../utilities/props'
|
|
4
4
|
import { GlobalProps, globalProps } from '../utilities/globalProps'
|
|
5
5
|
import { isValidEmoji } from '../utilities/validEmojiChecker'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getPlaybookIconClassName,
|
|
8
|
+
loadPlaybookIconSvg,
|
|
9
|
+
supportsPlaybookIcon,
|
|
10
|
+
supportsPlaybookIconFetch,
|
|
11
|
+
} from '../utilities/icons/playbookIconResolver'
|
|
7
12
|
|
|
8
13
|
export type IconSizes = "lg"
|
|
9
14
|
| "xs"
|
|
@@ -119,6 +124,38 @@ declare global {
|
|
|
119
124
|
var PB_ICONS: {[key: string]: React.FunctionComponent<any>}
|
|
120
125
|
}
|
|
121
126
|
|
|
127
|
+
const svgAttributeMap: {[key: string]: string} = {
|
|
128
|
+
'clip-path': 'clipPath',
|
|
129
|
+
'clip-rule': 'clipRule',
|
|
130
|
+
'fill-rule': 'fillRule',
|
|
131
|
+
'stroke-linecap': 'strokeLinecap',
|
|
132
|
+
'stroke-linejoin': 'strokeLinejoin',
|
|
133
|
+
'stroke-width': 'strokeWidth',
|
|
134
|
+
'xmlns:xlink': 'xmlnsXlink',
|
|
135
|
+
'xlink:href': 'xlinkHref',
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const convertAttributeName = (attributeName: string) => {
|
|
139
|
+
return svgAttributeMap[attributeName] || attributeName.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const parseSvgAttributes = (attributeSource: string) => {
|
|
143
|
+
const attributes: {[key: string]: string} = {}
|
|
144
|
+
const attributePattern = /([:@a-zA-Z0-9-]+)="([^"]*)"/g
|
|
145
|
+
let match = attributePattern.exec(attributeSource)
|
|
146
|
+
|
|
147
|
+
while (match) {
|
|
148
|
+
attributes[convertAttributeName(match[1])] = match[2]
|
|
149
|
+
match = attributePattern.exec(attributeSource)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return attributes
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const normalizeSvgInnerMarkup = (innerMarkup: string, fillColor: string) => {
|
|
156
|
+
return innerMarkup.replace(/<(path)\b([^>]*)fill="[^"]*"/gi, `<$1$2fill="${fillColor}"`)
|
|
157
|
+
}
|
|
158
|
+
|
|
122
159
|
const Icon = (props: IconProps) => {
|
|
123
160
|
const {
|
|
124
161
|
aria = {},
|
|
@@ -145,9 +182,35 @@ const Icon = (props: IconProps) => {
|
|
|
145
182
|
|
|
146
183
|
let iconElement: ReactSVGElement | null = typeof(icon) === "object" ? icon : null
|
|
147
184
|
const iconName = typeof(icon) === "string" ? icon : ""
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
185
|
+
const shouldLoadInlinePlaybookIcon = !customIcon && !iconElement && Boolean(iconName) && supportsPlaybookIconFetch(iconName)
|
|
186
|
+
const [inlinePlaybookSvgMarkup, setInlinePlaybookSvgMarkup] = useState<string | null | undefined>(undefined)
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
let isActive = true
|
|
190
|
+
|
|
191
|
+
if (!shouldLoadInlinePlaybookIcon) {
|
|
192
|
+
setInlinePlaybookSvgMarkup(undefined)
|
|
193
|
+
return () => {
|
|
194
|
+
isActive = false
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
setInlinePlaybookSvgMarkup(undefined)
|
|
199
|
+
|
|
200
|
+
loadPlaybookIconSvg(iconName).then((markup) => {
|
|
201
|
+
if (isActive) setInlinePlaybookSvgMarkup(markup)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
return () => {
|
|
205
|
+
isActive = false
|
|
206
|
+
}
|
|
207
|
+
}, [iconName, shouldLoadInlinePlaybookIcon])
|
|
208
|
+
|
|
209
|
+
const inlinePlaybookSvgReady = Boolean(inlinePlaybookSvgMarkup)
|
|
210
|
+
const inlinePlaybookSvgFailed = shouldLoadInlinePlaybookIcon && inlinePlaybookSvgMarkup === null
|
|
211
|
+
const legacyPowerIcon = !customIcon && !iconElement && (!shouldLoadInlinePlaybookIcon || inlinePlaybookSvgFailed) && window.PB_ICONS ? window.PB_ICONS[iconName] : null
|
|
212
|
+
const hasClassBasedPlaybookIcon = !inlinePlaybookSvgReady && !legacyPowerIcon && !customIcon && !iconElement && !shouldLoadInlinePlaybookIcon && Boolean(iconName) && supportsPlaybookIcon(iconName)
|
|
213
|
+
const playbookIconClassName = hasClassBasedPlaybookIcon ? getPlaybookIconClassName(iconName) : null
|
|
151
214
|
|
|
152
215
|
const faClasses = {
|
|
153
216
|
'fa-border': border,
|
|
@@ -161,7 +224,7 @@ const Icon = (props: IconProps) => {
|
|
|
161
224
|
[`fa-rotate-${rotation}`]: rotation,
|
|
162
225
|
}
|
|
163
226
|
|
|
164
|
-
if (!customIcon && !iconElement && !
|
|
227
|
+
if (!customIcon && !iconElement && !inlinePlaybookSvgReady && !hasClassBasedPlaybookIcon) {
|
|
165
228
|
if (legacyPowerIcon) {
|
|
166
229
|
const LegacyPowerIcon = legacyPowerIcon
|
|
167
230
|
iconElement = <LegacyPowerIcon /> as ReactSVGElement
|
|
@@ -170,13 +233,13 @@ const Icon = (props: IconProps) => {
|
|
|
170
233
|
}
|
|
171
234
|
}
|
|
172
235
|
|
|
173
|
-
const isFA = !iconElement && !customIcon && !
|
|
236
|
+
const isFA = !iconElement && !customIcon && !inlinePlaybookSvgReady && !hasClassBasedPlaybookIcon && !shouldLoadInlinePlaybookIcon
|
|
174
237
|
|
|
175
238
|
let classes = classnames(
|
|
176
|
-
(!iconElement && !customIcon && !
|
|
177
|
-
(iconElement || customIcon ||
|
|
178
|
-
(iconElement ||
|
|
179
|
-
|
|
239
|
+
(!iconElement && !customIcon && !inlinePlaybookSvgReady && !hasClassBasedPlaybookIcon) ? 'pb_icon_kit' : '',
|
|
240
|
+
(iconElement || customIcon || inlinePlaybookSvgReady || hasClassBasedPlaybookIcon) ? 'pb_custom_icon' : fontStyle,
|
|
241
|
+
(iconElement || inlinePlaybookSvgReady || hasClassBasedPlaybookIcon) ? 'svg-inline--fa' : '',
|
|
242
|
+
hasClassBasedPlaybookIcon ? 'pb_playbook_icon' : '',
|
|
180
243
|
playbookIconClassName,
|
|
181
244
|
color ? `color_${color}` : '',
|
|
182
245
|
globalProps(props),
|
|
@@ -230,7 +293,29 @@ const Icon = (props: IconProps) => {
|
|
|
230
293
|
|
|
231
294
|
// Add a conditional here to show only the SVG if custom
|
|
232
295
|
const displaySVG = (customIcon: any) => {
|
|
233
|
-
if (
|
|
296
|
+
if (inlinePlaybookSvgReady) {
|
|
297
|
+
const svgMatch = inlinePlaybookSvgMarkup?.match(/<svg([^>]*)>([\s\S]*)<\/svg>/i)
|
|
298
|
+
if (!svgMatch) return null
|
|
299
|
+
|
|
300
|
+
const [, rawAttributes, innerMarkup] = svgMatch
|
|
301
|
+
const svgAttributes = parseSvgAttributes(rawAttributes)
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<svg
|
|
305
|
+
{...svgAttributes}
|
|
306
|
+
{...ariaProps}
|
|
307
|
+
{...dataProps}
|
|
308
|
+
{...htmlProps}
|
|
309
|
+
className={classes}
|
|
310
|
+
color={color || 'currentColor'}
|
|
311
|
+
dangerouslySetInnerHTML={{ __html: normalizeSvgInnerMarkup(innerMarkup.trim(), color || 'currentColor') }}
|
|
312
|
+
height="auto"
|
|
313
|
+
id={id}
|
|
314
|
+
width="auto"
|
|
315
|
+
{...(props.tabIndex !== undefined && { tabIndex })}
|
|
316
|
+
/>
|
|
317
|
+
)
|
|
318
|
+
} else if (hasClassBasedPlaybookIcon)
|
|
234
319
|
return (
|
|
235
320
|
<i
|
|
236
321
|
{...ariaProps}
|
|
@@ -272,6 +357,8 @@ const Icon = (props: IconProps) => {
|
|
|
272
357
|
</span>
|
|
273
358
|
</>
|
|
274
359
|
)
|
|
360
|
+
else if (shouldLoadInlinePlaybookIcon)
|
|
361
|
+
return null
|
|
275
362
|
else
|
|
276
363
|
return (
|
|
277
364
|
<>
|
|
@@ -156,6 +156,28 @@ module Playbook
|
|
|
156
156
|
class << self
|
|
157
157
|
@cache_mutex = Mutex.new
|
|
158
158
|
|
|
159
|
+
def resolved_icon_name(icon_name)
|
|
160
|
+
return icon_name unless icon_alias_map
|
|
161
|
+
return icon_name if icon_name.nil?
|
|
162
|
+
|
|
163
|
+
aliases = icon_alias_map[icon_name]
|
|
164
|
+
return icon_name unless aliases
|
|
165
|
+
|
|
166
|
+
if aliases.is_a?(Array)
|
|
167
|
+
aliases.find { |alias_name| icon_path_index.key?(alias_name) } || icon_name
|
|
168
|
+
else
|
|
169
|
+
aliases
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def resolved_icon_path(icon_name)
|
|
174
|
+
return nil unless Rails.application.config.respond_to?(:icon_path)
|
|
175
|
+
|
|
176
|
+
resolved_name = resolved_icon_name(icon_name)
|
|
177
|
+
path = icon_path_index[resolved_name]
|
|
178
|
+
path if path && File.exist?(path)
|
|
179
|
+
end
|
|
180
|
+
|
|
159
181
|
# Cache aliases.json across the process, but invalidate when the file changes (dev-safe)
|
|
160
182
|
def icon_alias_map
|
|
161
183
|
return @icon_alias_map if alias_cache_fresh?
|
|
@@ -296,17 +318,7 @@ module Playbook
|
|
|
296
318
|
private
|
|
297
319
|
|
|
298
320
|
def resolve_alias(icon)
|
|
299
|
-
|
|
300
|
-
return icon if icon.nil?
|
|
301
|
-
|
|
302
|
-
aliases = icon_alias_map[icon]
|
|
303
|
-
return icon unless aliases
|
|
304
|
-
|
|
305
|
-
if aliases.is_a?(Array)
|
|
306
|
-
aliases.find { |alias_name| file_exists?(alias_name) } || icon
|
|
307
|
-
else
|
|
308
|
-
aliases
|
|
309
|
-
end
|
|
321
|
+
self.class.resolved_icon_name(icon)
|
|
310
322
|
end
|
|
311
323
|
|
|
312
324
|
def file_exists?(alias_name)
|