docks_theme_api 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.babelrc +4 -0
- data/.editorconfig +8 -0
- data/.eslintrc +115 -0
- data/.gitignore +24 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/README.md +5 -0
- data/Rakefile +3 -0
- data/assets/images/icons.svg +63 -0
- data/assets/scripts/coffeescript/pattern_library_helpers.coffee +8 -0
- data/assets/scripts/javascript/pattern_library_helpers.js +11 -0
- data/assets/scripts/pattern_library.js +10380 -0
- data/assets/scripts/pattern_library_demo.js +0 -0
- data/assets/styles/less/pattern-library-helpers.less +103 -0
- data/assets/styles/pattern-library-demo.css +1882 -0
- data/assets/styles/pattern-library.css +1882 -0
- data/assets/styles/sass/pattern-library-helpers.sass +90 -0
- data/assets/styles/scss/pattern-library-helpers.scss +99 -0
- data/assets/styles/stylus/pattern-library-helpers.styl +90 -0
- data/assets/templates/erb/demo.erb +26 -0
- data/assets/templates/erb/layouts/demo.erb +17 -0
- data/assets/templates/erb/layouts/pattern.erb +76 -0
- data/assets/templates/erb/partials/sidebar.erb +124 -0
- data/assets/templates/erb/partials/symbols/class.erb +1 -0
- data/assets/templates/erb/partials/symbols/demo.erb +40 -0
- data/assets/templates/erb/partials/symbols/factory.erb +70 -0
- data/assets/templates/erb/partials/symbols/function.erb +103 -0
- data/assets/templates/erb/partials/symbols/mixin.erb +62 -0
- data/assets/templates/erb/partials/symbols/variable.erb +59 -0
- data/assets/templates/erb/pattern.erb +102 -0
- data/assets/templates/haml/demo.haml +14 -0
- data/assets/templates/haml/layouts/demo.haml +6 -0
- data/assets/templates/haml/layouts/pattern.haml +38 -0
- data/assets/templates/haml/partials/sidebar.haml +68 -0
- data/assets/templates/haml/partials/symbols/class.haml +1 -0
- data/assets/templates/haml/partials/symbols/demo.haml +23 -0
- data/assets/templates/haml/partials/symbols/factory.haml +38 -0
- data/assets/templates/haml/partials/symbols/function.haml +54 -0
- data/assets/templates/haml/partials/symbols/mixin.haml +31 -0
- data/assets/templates/haml/partials/symbols/variable.haml +22 -0
- data/assets/templates/haml/pattern.haml +54 -0
- data/assets/templates/slim/demo.slim +24 -0
- data/assets/templates/slim/layouts/demo.slim +5 -0
- data/assets/templates/slim/layouts/pattern.slim +48 -0
- data/assets/templates/slim/partials/sidebar.slim +112 -0
- data/assets/templates/slim/partials/symbols/class.slim +1 -0
- data/assets/templates/slim/partials/symbols/demo.slim +30 -0
- data/assets/templates/slim/partials/symbols/factory.slim +57 -0
- data/assets/templates/slim/partials/symbols/function.slim +81 -0
- data/assets/templates/slim/partials/symbols/mixin.slim +45 -0
- data/assets/templates/slim/partials/symbols/variable.slim +35 -0
- data/assets/templates/slim/pattern.slim +63 -0
- data/docks_config.rb +32 -0
- data/docks_theme_api.gemspec +37 -0
- data/gulpfile.js +88 -0
- data/karma.conf.js +6 -0
- data/lib/docks_theme_api/components/base_component.rb +99 -0
- data/lib/docks_theme_api/components/code_block_component.rb +10 -0
- data/lib/docks_theme_api/components/popover_component.rb +15 -0
- data/lib/docks_theme_api/components/table_component.rb +34 -0
- data/lib/docks_theme_api/components/tablist_component.rb +11 -0
- data/lib/docks_theme_api/components.rb +21 -0
- data/lib/docks_theme_api/helpers/ui_helper.rb +69 -0
- data/lib/docks_theme_api/theme.rb +21 -0
- data/lib/docks_theme_api.rb +1 -0
- data/package.json +60 -0
- data/source/behaviors/filterable/filterable.coffee +353 -0
- data/source/behaviors/filterable/filterable.js +0 -0
- data/source/behaviors/filterable/filterable.scss +34 -0
- data/source/behaviors/filterable/package.json +3 -0
- data/source/behaviors/index.js +0 -0
- data/source/components/avatar/avatar.erb +20 -0
- data/source/components/avatar/avatar.js +142 -0
- data/source/components/avatar/avatar.scss +200 -0
- data/source/components/avatar/avatar_container.erb +13 -0
- data/source/components/avatar/package.json +3 -0
- data/source/components/avatar/spec/avatar_spec.js +81 -0
- data/source/components/badge/badge.scss +158 -0
- data/source/components/button/button.scss +213 -0
- data/source/components/card/card.scss +32 -0
- data/source/components/code_block/code-block.scss +353 -0
- data/source/components/code_block/code_block.erb +95 -0
- data/source/components/code_block/code_block.js +444 -0
- data/source/components/code_block/package.json +3 -0
- data/source/components/code_block/spec/code_block_spec.js +10 -0
- data/source/components/demo/demo.js +244 -0
- data/source/components/demo/demo.scss +90 -0
- data/source/components/demo/package.json +3 -0
- data/source/components/exploded/exploded.erb +25 -0
- data/source/components/exploded/exploded.js +694 -0
- data/source/components/exploded/exploded.scss +166 -0
- data/source/components/exploded/package.json +3 -0
- data/source/components/field/field.js +24 -0
- data/source/components/field/field.scss +101 -0
- data/source/components/field/package.json +3 -0
- data/source/components/header/header.scss +33 -0
- data/source/components/iframe/iframe.erb +12 -0
- data/source/components/iframe/iframe.js +381 -0
- data/source/components/iframe/package.json +3 -0
- data/source/components/index.js +37 -0
- data/source/components/inline_group/inline-group.scss +14 -0
- data/source/components/internal_link/internal_link.js +49 -0
- data/source/components/internal_link/package.json +3 -0
- data/source/components/list/list.scss +230 -0
- data/source/components/modal/modal.coffee +84 -0
- data/source/components/modal/modal.erb +19 -0
- data/source/components/modal/modal.js +0 -0
- data/source/components/modal/modal.scss +57 -0
- data/source/components/modal/package.json +3 -0
- data/source/components/notice/notice.scss +48 -0
- data/source/components/popover/package.json +3 -0
- data/source/components/popover/popover.coffee +562 -0
- data/source/components/popover/popover.erb +21 -0
- data/source/components/popover/popover.js +0 -0
- data/source/components/popover/popover.scss +139 -0
- data/source/components/range/range.scss +78 -0
- data/source/components/resizable/package.json +3 -0
- data/source/components/resizable/resizable.erb +30 -0
- data/source/components/resizable/resizable.js +250 -0
- data/source/components/resizable/resizable.scss +245 -0
- data/source/components/resizable/size_buttons.js +249 -0
- data/source/components/scroll_container/package.json +3 -0
- data/source/components/scroll_container/scroll-container.scss +4 -0
- data/source/components/scroll_container/scroll_container.js +24 -0
- data/source/components/section/section.scss +99 -0
- data/source/components/select/package.json +3 -0
- data/source/components/select/select.erb +21 -0
- data/source/components/select/select.js +35 -0
- data/source/components/select/select.scss +163 -0
- data/source/components/table/package.json +3 -0
- data/source/components/table/table.erb +16 -0
- data/source/components/table/table.js +351 -0
- data/source/components/table/table.scss +236 -0
- data/source/components/tablist/package.json +3 -0
- data/source/components/tablist/tablist.erb +13 -0
- data/source/components/tablist/tablist.js +246 -0
- data/source/components/tablist/tablist.scss +191 -0
- data/source/components/tablist/tablist_panel.erb +14 -0
- data/source/components/tablist/tablist_tab.erb +20 -0
- data/source/components/toggle/package.json +3 -0
- data/source/components/toggle/toggle.erb +11 -0
- data/source/components/toggle/toggle.js +211 -0
- data/source/components/toggle/toggle_container.erb +30 -0
- data/source/components/vertical_spacer/vertical-spacer.scss +3 -0
- data/source/components/vertical_stack/vertical-stack.scss +19 -0
- data/source/components/xray/package.json +3 -0
- data/source/components/xray/xray.erb +50 -0
- data/source/components/xray/xray.js +123 -0
- data/source/components/xray/xray.scss +79 -0
- data/source/foundation/app/app.js +15 -0
- data/source/foundation/app/package.json +3 -0
- data/source/pattern-library-demo.scss +13 -0
- data/source/pattern-library.scss +13 -0
- data/source/pattern_library.js +8 -0
- data/source/pattern_library_demo.js +8 -0
- data/source/structures/index.js +11 -0
- data/source/structures/sidebar/package.json +3 -0
- data/source/structures/sidebar/sidebar.js +69 -0
- data/source/structures/sidebar/sidebar.scss +79 -0
- data/source/utilities/builder/builder.js +138 -0
- data/source/utilities/builder/package.json +3 -0
- data/source/utilities/client/client.js +7 -0
- data/source/utilities/client/package.json +3 -0
- data/source/utilities/colors/colors.scss +112 -0
- data/source/utilities/defaults/defaults.scss +38 -0
- data/source/utilities/dom_cache/dom_cache.js +24 -0
- data/source/utilities/dom_cache/package.json +3 -0
- data/source/utilities/events/events.js +25 -0
- data/source/utilities/events/package.json +3 -0
- data/source/utilities/font_sizes/font-sizes.scss +85 -0
- data/source/utilities/foundation/a11y.scss +10 -0
- data/source/utilities/foundation/base.scss +29 -0
- data/source/utilities/foundation/icon.scss +114 -0
- data/source/utilities/foundation/layout.scss +67 -0
- data/source/utilities/foundation/page.scss +39 -0
- data/source/utilities/foundation/type.scss +208 -0
- data/source/utilities/functions/functions.scss +127 -0
- data/source/utilities/keycodes/keycodes.js +23 -0
- data/source/utilities/keycodes/package.json +3 -0
- data/source/utilities/markup/markup.js +90 -0
- data/source/utilities/markup/package.json +3 -0
- data/source/utilities/media/media.scss +172 -0
- data/source/utilities/mixins/mixins.scss +89 -0
- data/source/utilities/naming_convention/naming_convention.js +3 -0
- data/source/utilities/naming_convention/package.json +3 -0
- data/source/utilities/numbers/numbers.js +14 -0
- data/source/utilities/numbers/package.json +3 -0
- data/source/utilities/painting/package.json +3 -0
- data/source/utilities/painting/painting.js +7 -0
- data/source/utilities/pattern/package.json +3 -0
- data/source/utilities/pattern/pattern.js +50 -0
- data/source/utilities/query_string/package.json +3 -0
- data/source/utilities/query_string/query_string.js +24 -0
- data/source/utilities/template/package.json +3 -0
- data/source/utilities/template/template.js +10 -0
- data/source/utilities/text_range/package.json +3 -0
- data/source/utilities/text_range/text_range.js +30 -0
- data/source/utilities/ui_events/package.json +3 -0
- data/source/utilities/ui_events/ui_events.js +85 -0
- data/source/utilities/variables/variables.scss +18 -0
- data/source/utilities/z_indexes/z-indexes.scss +88 -0
- data/source/vendor/array_includes.js +28 -0
- data/source/vendor/highlight.js +1142 -0
- data/source/vendor/index.js +1 -0
- data/source/vendor/matrix.js +399 -0
- data/source/vendor/query_string.js +66 -0
- data/spec/assets/.eslintrc +9 -0
- data/spec/assets/spec_fixture.js +33 -0
- data/spec/assets/spec_helper.js +19 -0
- data/spec/lib/components/base_component_spec.rb +156 -0
- data/spec/lib/components_spec.rb +30 -0
- data/spec/lib/helpers/ui_helper_spec.rb +62 -0
- data/spec/lib/theme_spec.rb +25 -0
- data/spec/spec_helper.rb +15 -0
- data/tasks/gulp/.eslintrc +6 -0
- data/tasks/gulp/browser_sync.js +8 -0
- data/tasks/gulp/code_quality/scripts.js +10 -0
- data/tasks/gulp/config/index.js +116 -0
- data/tasks/gulp/minify/scripts.js +13 -0
- data/tasks/gulp/minify/styles.js +13 -0
- data/tasks/gulp/pattern_library/index.js +5 -0
- data/tasks/gulp/pattern_library/scripts.js +10 -0
- data/tasks/gulp/pattern_library/styles.js +10 -0
- data/tasks/gulp/scripts.js +8 -0
- data/tasks/gulp/spec/scripts.js +11 -0
- data/tasks/gulp/styles.js +17 -0
- data/tasks/gulp/utilities/babel/relative_require.js +22 -0
- data/tasks/gulp/utilities/babel/spec_helper.js +20 -0
- data/tasks/gulp/utilities/browserify_bundler.js +22 -0
- data/tasks/gulp/utilities/handle_errors.js +13 -0
- data/tasks/gulp/watch.js +9 -0
- data/tasks/rake/rspec.rake +7 -0
- data/tasks/rake/rubocop.rake +8 -0
- data/tasks/rake/templates.rake +50 -0
- metadata +470 -0
@@ -0,0 +1,562 @@
|
|
1
|
+
BASE_CLASS = "popover"
|
2
|
+
|
3
|
+
CLASSES =
|
4
|
+
BASE: BASE_CLASS
|
5
|
+
CONTAINER: "#{BASE_CLASS}__container"
|
6
|
+
CONTENT_WRAPPER: "#{BASE_CLASS}__content-wrapper"
|
7
|
+
CONTENT: "#{BASE_CLASS}__content"
|
8
|
+
TOOLTIP: "#{BASE_CLASS}__tooltip"
|
9
|
+
PANE: "#{BASE_CLASS}__pane"
|
10
|
+
|
11
|
+
STATES =
|
12
|
+
BASE:
|
13
|
+
ACTIVE: "#{CLASSES.BASE}--is-active"
|
14
|
+
POSITIONED_BELOW: "#{CLASSES.BASE}--is-positioned-beneath"
|
15
|
+
POSITIONED_ABOVE: "#{CLASSES.BASE}--is-positioned-above"
|
16
|
+
CONTAINER:
|
17
|
+
ACTIVE: "#{CLASSES.CONTAINER}--contains-active-popover"
|
18
|
+
DEACTIVATING: "#{CLASSES.CONTAINER}--is-deactivating"
|
19
|
+
|
20
|
+
VARIANTS =
|
21
|
+
BASE:
|
22
|
+
ALIGN_TO_EDGE: "#{CLASSES.BASE}--align-to-edge"
|
23
|
+
FULL_WIDTH: "#{CLASSES.BASE}--full-width"
|
24
|
+
|
25
|
+
EVENTS =
|
26
|
+
ACTIVATED: "#{CLASSES.BASE}:activated"
|
27
|
+
DEACTIVATED: "#{CLASSES.BASE}:deactivated"
|
28
|
+
|
29
|
+
ATTRS =
|
30
|
+
PREFERRED_POSITION: "data-popover-preferred-position"
|
31
|
+
HORIZONTALLY_RELATIVE_TO: "data-popover-horizontally-relative-to-closest"
|
32
|
+
ACTIVATE_FROM: "data-popover-activate-from"
|
33
|
+
CSS_CACHE:
|
34
|
+
MAX_HEIGHT: "data-popover-css-max-height"
|
35
|
+
MAX_WIDTH: "data-popover-css-max-width"
|
36
|
+
VERTICAL_MARGIN: "data-popover-css-vertical-margin"
|
37
|
+
HORIZONTAL_MARGIN: "data-popover-css-horizontal-margin"
|
38
|
+
|
39
|
+
POSITIONS =
|
40
|
+
TOP: "top"
|
41
|
+
BOTTOM: "bottom"
|
42
|
+
|
43
|
+
TOOLTIP_SIZE = 5
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
# PUBLIC API AND HANDLERS
|
48
|
+
|
49
|
+
Popover = (node) ->
|
50
|
+
existingObject = Popover.for(node)
|
51
|
+
return existingObject if existingObject
|
52
|
+
|
53
|
+
activator = $(node).siblings(":not(#{CLASSES.BASE})")[0]
|
54
|
+
cachePopoverCSSProperties(node)
|
55
|
+
a11yPopovers(node, activator)
|
56
|
+
|
57
|
+
popoverManipulator =
|
58
|
+
activate: ->
|
59
|
+
activate(node)
|
60
|
+
return
|
61
|
+
|
62
|
+
deactivate: ->
|
63
|
+
deactivate(node)
|
64
|
+
return
|
65
|
+
|
66
|
+
toggle: ->
|
67
|
+
toggle(node)
|
68
|
+
return
|
69
|
+
|
70
|
+
reposition: ->
|
71
|
+
positionPopover()
|
72
|
+
return
|
73
|
+
|
74
|
+
$(node).data(CLASSES.BASE, popoverManipulator)
|
75
|
+
popoverManipulator
|
76
|
+
|
77
|
+
Popover.for = (node) ->
|
78
|
+
$(node).closest(".#{CLASSES.BASE}").data(CLASSES.BASE)
|
79
|
+
|
80
|
+
Popover.send = (method, args...) ->
|
81
|
+
if args.length
|
82
|
+
secrets[method]?.apply(null, args)
|
83
|
+
else
|
84
|
+
secrets[method]
|
85
|
+
|
86
|
+
Popover.CLASSES = CLASSES
|
87
|
+
Popover.EVENTS = EVENTS
|
88
|
+
Popover.STATES = STATES
|
89
|
+
Popover.VARIANTS = VARIANTS
|
90
|
+
Popover.ATTRS = ATTRS
|
91
|
+
|
92
|
+
window.Docks.Popover = Popover
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
# INITIALIZERS
|
97
|
+
|
98
|
+
popoverIndex = 1
|
99
|
+
|
100
|
+
joinToExistingIDs = (id, node) ->
|
101
|
+
currentID = node.id
|
102
|
+
node.id = "#{id}#{if currentID.length then " " else ""}#{currentID}"
|
103
|
+
|
104
|
+
a11yPopovers = (popover, activator) ->
|
105
|
+
# Define initial accessibility concerns
|
106
|
+
popoverID = "#{CLASSES.BASE}--#{popoverIndex++}"
|
107
|
+
activatorID = popoverID.replace(CLASSES.BASE, "#{CLASSES.BASE}-activator")
|
108
|
+
|
109
|
+
$(popover).attr
|
110
|
+
"id": joinToExistingIDs(popoverID, popover)
|
111
|
+
"aria-labelledby": activatorID
|
112
|
+
"aria-expanded": "false"
|
113
|
+
|
114
|
+
popover.style.display = "none"
|
115
|
+
|
116
|
+
$(activator).attr
|
117
|
+
"id": joinToExistingIDs(activatorID, activator)
|
118
|
+
"aria-expanded": "false"
|
119
|
+
"aria-haspopup": "true"
|
120
|
+
"aria-owns": popoverID
|
121
|
+
"aria-controls": popoverID
|
122
|
+
|
123
|
+
cachePopoverCSSProperties = (popover) ->
|
124
|
+
styles = window.getComputedStyle(popover)
|
125
|
+
|
126
|
+
popover.setAttribute(ATTRS.CSS_CACHE.VERTICAL_MARGIN, calculatePixelDimension(styles.marginTop, popover) || 0)
|
127
|
+
popover.setAttribute(ATTRS.CSS_CACHE.HORIZONTAL_MARGIN, calculatePixelDimension(styles.marginLeft, popover) || 0)
|
128
|
+
|
129
|
+
contentWrapper = $(popover).children(".#{CLASSES.CONTENT_WRAPPER}")[0]
|
130
|
+
contentWrapperStyles = window.getComputedStyle(contentWrapper)
|
131
|
+
popover.setAttribute(ATTRS.CSS_CACHE.MAX_HEIGHT, calculatePixelDimension(contentWrapperStyles.maxHeight, contentWrapper) || 10000)
|
132
|
+
popover.setAttribute(ATTRS.CSS_CACHE.MAX_WIDTH, calculatePixelDimension(contentWrapperStyles.maxWidth, contentWrapper) || 10000)
|
133
|
+
|
134
|
+
popover.style.maxWidth = "none" unless popover.classList.contains(VARIANTS.BASE.FULL_WIDTH)
|
135
|
+
popover.style.marginLeft = popover.style.marginRight = "0px"
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
# MEASUREMENT CONVERTERS
|
140
|
+
|
141
|
+
baseFontSize = do ->
|
142
|
+
fontSize = undefined
|
143
|
+
|
144
|
+
->
|
145
|
+
return fontSize if fontSize?
|
146
|
+
|
147
|
+
$el = $("<div>M</div>").appendTo("body")
|
148
|
+
$el.css
|
149
|
+
display: "inline-block"
|
150
|
+
padding: "0"
|
151
|
+
lineHeight: "1"
|
152
|
+
position: "absolute"
|
153
|
+
visibility: "hidden"
|
154
|
+
fontSize: "1em"
|
155
|
+
|
156
|
+
size = $el.height()
|
157
|
+
$el.remove()
|
158
|
+
fontSize
|
159
|
+
|
160
|
+
calculatePixelDimension = (dim, node) ->
|
161
|
+
# No value set, make it non-restricting
|
162
|
+
return false if dim == "none"
|
163
|
+
|
164
|
+
float = parseFloat(dim)
|
165
|
+
return float * baseFontSize() if dim.indexOf("rem") >= 0
|
166
|
+
return float * parseFloat(window.getComputedStyle(node).fontSize) if dim.indexOf("em") >= 0
|
167
|
+
return float / 100 if dim.indexOf("%") >= 0
|
168
|
+
|
169
|
+
# Set in px
|
170
|
+
float
|
171
|
+
|
172
|
+
|
173
|
+
|
174
|
+
# CHANGING STATE
|
175
|
+
|
176
|
+
activeCache = {}
|
177
|
+
|
178
|
+
activate = (popover) ->
|
179
|
+
return if popover == activeCache.popover?.base
|
180
|
+
deactivate() if activeCache.popover
|
181
|
+
|
182
|
+
tooltip = popover.querySelector(".#{CLASSES.TOOLTIP}")
|
183
|
+
activator = popover.nextElementSibling || popover.previousElementSibling
|
184
|
+
|
185
|
+
relativeTo = popover.getAttribute(ATTRS.HORIZONTALLY_RELATIVE_TO)
|
186
|
+
activateFrom = popover.getAttribute(ATTRS.ACTIVATE_FROM)
|
187
|
+
|
188
|
+
activeCache =
|
189
|
+
container: popover.parentNode
|
190
|
+
activator: activator
|
191
|
+
source: if activateFrom then activator.querySelector(activateFrom) else activator
|
192
|
+
relativeTo: $(popover).closest(relativeTo)[0] if relativeTo
|
193
|
+
preferredPosition: popover.getAttribute(ATTRS.PREFERRED_POSITION) || POSITIONS.BOTTOM
|
194
|
+
horizontallyPosition: !popover.classList.contains(VARIANTS.BASE.FULL_WIDTH)
|
195
|
+
positionAgainstEdge: popover.classList.contains(VARIANTS.BASE.ALIGN_TO_EDGE)
|
196
|
+
popover:
|
197
|
+
base: popover
|
198
|
+
container: popover.parentNode
|
199
|
+
content: popover.querySelector(".#{CLASSES.CONTENT}")
|
200
|
+
contentWrapper: popover.querySelector(".#{CLASSES.CONTENT_WRAPPER}")
|
201
|
+
tooltip: tooltip
|
202
|
+
panes: Array::slice.call(popover.querySelectorAll(".#{CLASSES.PANE}"))
|
203
|
+
styles:
|
204
|
+
horizontalMargin: parseInt(popover.getAttribute(ATTRS.CSS_CACHE.HORIZONTAL_MARGIN))
|
205
|
+
verticalMargin: parseInt(popover.getAttribute(ATTRS.CSS_CACHE.VERTICAL_MARGIN))
|
206
|
+
maxHeight: parseInt(popover.getAttribute(ATTRS.CSS_CACHE.MAX_HEIGHT))
|
207
|
+
maxWidth: parseFloat(popover.getAttribute(ATTRS.CSS_CACHE.MAX_WIDTH))
|
208
|
+
|
209
|
+
ended = false
|
210
|
+
$(popover).one "transitionend webkitTransitionEnd", ->
|
211
|
+
return if ended
|
212
|
+
ended = true
|
213
|
+
$(popover).trigger(EVENTS.ACTIVATED)
|
214
|
+
|
215
|
+
attachActiveEventListeners(popover)
|
216
|
+
applyActivationMarkup()
|
217
|
+
positionPopover()
|
218
|
+
|
219
|
+
applyActivationMarkup = ->
|
220
|
+
popover = activeCache.popover.base
|
221
|
+
popover.style.backfaceVisibility = "hidden"
|
222
|
+
popover.style.display = ""
|
223
|
+
popover.setAttribute("aria-expanded", "true")
|
224
|
+
activeCache.activator.setAttribute("aria-expanded", "true")
|
225
|
+
|
226
|
+
popover.offsetHeight # force repaint
|
227
|
+
popover.classList.add(STATES.BASE.ACTIVE)
|
228
|
+
activeCache.popover.container.classList.add(STATES.CONTAINER.ACTIVE)
|
229
|
+
return
|
230
|
+
|
231
|
+
attachActiveEventListeners = (popover) ->
|
232
|
+
$(popover).on("wheel", ".#{CLASSES.PANE}", popoverPaneScroll)
|
233
|
+
|
234
|
+
toggle = (popover) ->
|
235
|
+
if activeCache.popover?.base != popover then activate(popover) else deactivate()
|
236
|
+
|
237
|
+
deactivate = (popover) ->
|
238
|
+
return unless activeCache.popover
|
239
|
+
return if popover && popover != activeCache.popover.base
|
240
|
+
|
241
|
+
oldPopover = activeCache.popover.base
|
242
|
+
oldContent = activeCache.popover.content
|
243
|
+
oldContainer = activeCache.popover.container
|
244
|
+
|
245
|
+
$(oldPopover).one "transitionend webkitTransitionEnd", do ->
|
246
|
+
# Fixes a Chrome bug where transitionend and webkitTransitionEnd
|
247
|
+
# events both fired
|
248
|
+
ended = false
|
249
|
+
|
250
|
+
->
|
251
|
+
return if ended
|
252
|
+
ended = true
|
253
|
+
oldContainer.classList.remove(STATES.CONTAINER.DEACTIVATING)
|
254
|
+
oldPopover.style.display = "none"
|
255
|
+
oldPopover.style.backfaceVisibility = ""
|
256
|
+
oldContent.style.width = ""
|
257
|
+
$(oldPopover).trigger(EVENTS.DEACTIVATED)
|
258
|
+
|
259
|
+
applyDeactivationMarkup()
|
260
|
+
detachActiveEventListeners(oldPopover)
|
261
|
+
|
262
|
+
# delete so we keep the reference to activeCache in secrets
|
263
|
+
delete activeCache[key] for key of activeCache
|
264
|
+
|
265
|
+
applyDeactivationMarkup = ->
|
266
|
+
popover = activeCache.popover
|
267
|
+
popover.container.classList.add(STATES.CONTAINER.DEACTIVATING)
|
268
|
+
popover.container.classList.remove(STATES.CONTAINER.ACTIVE)
|
269
|
+
popover.base.classList.remove(STATES.BASE.ACTIVE)
|
270
|
+
popover.base.setAttribute("aria-expanded", "false")
|
271
|
+
activeCache.activator.setAttribute("aria-expanded", "false")
|
272
|
+
|
273
|
+
detachActiveEventListeners = (popover) ->
|
274
|
+
$(popover).off("wheel", ".#{CLASSES.PANE}", popoverPaneScroll)
|
275
|
+
|
276
|
+
|
277
|
+
|
278
|
+
# SIZING AND POSITIONING
|
279
|
+
|
280
|
+
positionPopover = (event) ->
|
281
|
+
return unless activeCache.popover
|
282
|
+
|
283
|
+
resize = !event?.type || event.type == "resize"
|
284
|
+
newStyles =
|
285
|
+
base: {}
|
286
|
+
wrapper: {}
|
287
|
+
content: {}
|
288
|
+
tooltip: {}
|
289
|
+
|
290
|
+
rect = spaceDetailsForActivePopover()
|
291
|
+
determineVerticalPositioning({ rect: rect }, newStyles)
|
292
|
+
|
293
|
+
# Horizontal repositioning only needs to happen on resize
|
294
|
+
if resize && activeCache.horizontallyPosition
|
295
|
+
offsets = calculateHorizontalOffsets()
|
296
|
+
left = rect.leftRelative <= 0.5
|
297
|
+
width = calculateMaxWidth(rect.relativeToWidth)
|
298
|
+
newStyles.content.width = width
|
299
|
+
|
300
|
+
context =
|
301
|
+
offsets: offsets
|
302
|
+
width: width
|
303
|
+
left: left
|
304
|
+
rect: rect
|
305
|
+
|
306
|
+
if activeCache.positionAgainstEdge
|
307
|
+
horizontallyPositionWithEdgeAlignment(context, newStyles)
|
308
|
+
else
|
309
|
+
horizontallyPositionWithCenterAlignment(context, newStyles)
|
310
|
+
|
311
|
+
tooltipPosition = (rect.width / 2) + offsets.leftFromContainerToActivator + offsets.leftFromActivatorToSource - newStyles.base.left
|
312
|
+
newStyles.tooltip.left = tooltipPosition
|
313
|
+
|
314
|
+
popoverStyles = activeCache.popover.base.style
|
315
|
+
transformOriginY = newStyles.base.transformOrigin.split(" ")[1..-1].join(" ")
|
316
|
+
activeCache.styles.transformOriginX = "#{Math.round(tooltipPosition)}px"
|
317
|
+
newStyles.base.transformOrigin = "#{activeCache.styles.transformOriginX} #{transformOriginY}"
|
318
|
+
|
319
|
+
requestAnimationFrame ->
|
320
|
+
popover = activeCache.popover
|
321
|
+
$(popover.base).css(newStyles.base)
|
322
|
+
$(popover.content).css(newStyles.content)
|
323
|
+
$(popover.contentWrapper).css(newStyles.wrapper)
|
324
|
+
$(popover.tooltip).css(newStyles.tooltip)
|
325
|
+
|
326
|
+
if resize
|
327
|
+
# Safari and IE need an explicit height for the content, otherwise the flexbox
|
328
|
+
# will set non-fixed panes to be a height of 0. This **must** be done after
|
329
|
+
# the content's width is set since width dictates the height.
|
330
|
+
popover.content.style.height = calculateTotalPaneSize() + 2
|
331
|
+
|
332
|
+
# $.css doesn't apply the transformOrigin in IE for some reason :/
|
333
|
+
popover.base.style.transformOrigin = newStyles.base.transformOrigin
|
334
|
+
|
335
|
+
horizontallyPositionWithEdgeAlignment = (context, styles) ->
|
336
|
+
{offsets, width, rect, left} = context
|
337
|
+
|
338
|
+
oppositeSideSpace = rect[if left then "right" else "left"] + (rect.width / 2) - activeCache.styles.horizontalMargin
|
339
|
+
|
340
|
+
popoverPosition = if left then 0 else rect.width - width
|
341
|
+
popoverPosition += offsets.leftFromContainerToActivator
|
342
|
+
|
343
|
+
styles.base.left = if width > oppositeSideSpace
|
344
|
+
if left
|
345
|
+
popoverPosition - (width - oppositeSideSpace) - offsets.leftFromActivatorToSource
|
346
|
+
else
|
347
|
+
popoverPosition + (width - oppositeSideSpace)
|
348
|
+
else
|
349
|
+
if left
|
350
|
+
popoverPosition - offsets.leftFromActivatorToSource
|
351
|
+
else
|
352
|
+
popoverPosition - offsets.rightFromActivatorToSource
|
353
|
+
|
354
|
+
styles.base.left += offsets.leftFromActivatorToSource
|
355
|
+
|
356
|
+
horizontallyPositionWithCenterAlignment = (context, styles) ->
|
357
|
+
{offsets, width, rect, left} = context
|
358
|
+
|
359
|
+
spaceNeededPerSide = (width * 0.5) + activeCache.styles.horizontalMargin
|
360
|
+
|
361
|
+
styles.base.left = if left && rect.left < spaceNeededPerSide
|
362
|
+
(rect.width / 2) + activeCache.styles.horizontalMargin - rect.left
|
363
|
+
else if !left && rect.right < spaceNeededPerSide
|
364
|
+
(rect.width / 2) - width + rect.right - activeCache.styles.horizontalMargin
|
365
|
+
else
|
366
|
+
(rect.width / 2) - (width / 2)
|
367
|
+
|
368
|
+
styles.base.left = styles.base.left + offsets.leftFromContainerToActivator + offsets.leftFromActivatorToSource
|
369
|
+
|
370
|
+
calculateHorizontalOffsets = ->
|
371
|
+
activatorRect = activeCache.activator.getBoundingClientRect()
|
372
|
+
sourceIsActivator = activeCache.source == activeCache.activator
|
373
|
+
sourceRect = if sourceIsActivator then activatorRect else activeCache.source.getBoundingClientRect()
|
374
|
+
|
375
|
+
leftFromContainerToActivator: activatorRect.left - activeCache.container.getBoundingClientRect().left
|
376
|
+
leftFromActivatorToSource: sourceRect.left - activatorRect.left
|
377
|
+
rightFromActivatorToSource: sourceRect.right - activatorRect.right
|
378
|
+
|
379
|
+
spaceDetailsForActivePopover = ->
|
380
|
+
rect = activeCache.source.getBoundingClientRect()
|
381
|
+
topSpace = rect.top + (0.5 * rect.height)
|
382
|
+
leftSpace = rect.left + (0.5 * rect.width)
|
383
|
+
relativeToWidth = window.innerWidth
|
384
|
+
|
385
|
+
if activeCache.relativeTo
|
386
|
+
relativeTo = activeCache.relativeTo.getBoundingClientRect()
|
387
|
+
relativeToWidth = relativeTo.width
|
388
|
+
leftSpace -= relativeTo.left
|
389
|
+
|
390
|
+
height: rect.height
|
391
|
+
width: rect.width
|
392
|
+
top: topSpace
|
393
|
+
bottom: window.innerHeight - topSpace
|
394
|
+
left: leftSpace
|
395
|
+
leftRelative: leftSpace / relativeToWidth
|
396
|
+
right: relativeToWidth - leftSpace
|
397
|
+
relativeToWidth: relativeToWidth
|
398
|
+
|
399
|
+
calculateTotalPaneSize = do ->
|
400
|
+
reduction = (totalHeight, pane) -> totalHeight + pane.scrollHeight
|
401
|
+
|
402
|
+
-> activeCache.popover.panes.reduce(reduction, 0)
|
403
|
+
|
404
|
+
calculateMaxWidth = (relativeToWidth) ->
|
405
|
+
unless activeCache.styles.contentWidth
|
406
|
+
content = activeCache.popover.content
|
407
|
+
content.style.whiteSpace = "nowrap"
|
408
|
+
activeCache.styles.contentWidth = content.offsetWidth + 2
|
409
|
+
content.style.whiteSpace = ""
|
410
|
+
|
411
|
+
# Adjust for percentage-based max width
|
412
|
+
maxRelativeWidth = relativeToWidth - (2 * activeCache.styles.horizontalMargin)
|
413
|
+
Math.min(maxRelativeWidth, activeCache.styles.maxWidth, activeCache.styles.contentWidth)
|
414
|
+
|
415
|
+
determineVerticalPositioning = (context, styles) ->
|
416
|
+
actualHeight = activeCache.popover.base.offsetHeight + 2 * activeCache.styles.verticalMargin
|
417
|
+
spaceAvailable = context.rect
|
418
|
+
|
419
|
+
position = if activeCache.preferredPosition == POSITIONS.BOTTOM
|
420
|
+
if spaceAvailable.bottom > spaceAvailable.top ||
|
421
|
+
(spaceAvailable.bottom - spaceAvailable.height / 2) > actualHeight
|
422
|
+
POSITIONS.BOTTOM
|
423
|
+
else
|
424
|
+
POSITIONS.TOP
|
425
|
+
else
|
426
|
+
if spaceAvailable.top > spaceAvailable.bottom ||
|
427
|
+
(spaceAvailable.top - spaceAvailable.height / 2) > actualHeight
|
428
|
+
POSITIONS.TOP
|
429
|
+
else
|
430
|
+
POSITIONS.BOTTOM
|
431
|
+
|
432
|
+
positionedAbove = position == POSITIONS.TOP
|
433
|
+
defaultHorizontalTranslation = if activeCache.horizontallyPosition then 0 else "50%"
|
434
|
+
styles.base.transformOrigin = if positionedAbove
|
435
|
+
"#{activeCache.styles.transformOriginX || defaultHorizontalTranslation} calc(100% + #{TOOLTIP_SIZE}px)"
|
436
|
+
else
|
437
|
+
"#{activeCache.styles.transformOriginX || defaultHorizontalTranslation} -#{TOOLTIP_SIZE}px"
|
438
|
+
|
439
|
+
if position != activeCache.position
|
440
|
+
activeCache.position = position
|
441
|
+
activeCache.popover.base.classList[if positionedAbove then "remove" else "add"](STATES.BASE.POSITIONED_BELOW)
|
442
|
+
activeCache.popover.base.classList[if positionedAbove then "add" else "remove"](STATES.BASE.POSITIONED_ABOVE)
|
443
|
+
|
444
|
+
maxHeightOnScreen = spaceAvailable[position] - (spaceAvailable.height / 2) - (2 * activeCache.styles.verticalMargin)
|
445
|
+
styles.content.maxHeight = Math.min(maxHeightOnScreen, activeCache.styles.maxHeight)
|
446
|
+
|
447
|
+
|
448
|
+
|
449
|
+
# EVENT HANDLERS
|
450
|
+
|
451
|
+
popoverFocus = do ->
|
452
|
+
lastCallAt = 0
|
453
|
+
lastPopoverContainer = null
|
454
|
+
|
455
|
+
(event) ->
|
456
|
+
$target = $(event.target)
|
457
|
+
return if $target.closest(".#{CLASSES.BASE}").length
|
458
|
+
|
459
|
+
$popoverContainer = $target.closest(".#{CLASSES.CONTAINER}")
|
460
|
+
|
461
|
+
if $popoverContainer.length
|
462
|
+
|
463
|
+
# manual throttling only if the same popover is triggered multiple times in a row
|
464
|
+
currentPopoverContainer = $popoverContainer[0]
|
465
|
+
now = Date.now()
|
466
|
+
if lastPopoverContainer == currentPopoverContainer && (now - lastCallAt) < 300
|
467
|
+
return
|
468
|
+
lastCallAt = now
|
469
|
+
lastPopoverContainer = currentPopoverContainer
|
470
|
+
|
471
|
+
event.preventDefault()
|
472
|
+
node = $popoverContainer.children(".#{CLASSES.BASE}")[0]
|
473
|
+
if event.type == 'focusin'
|
474
|
+
activate(node)
|
475
|
+
else
|
476
|
+
toggle(node)
|
477
|
+
|
478
|
+
else
|
479
|
+
deactivate()
|
480
|
+
|
481
|
+
popoverKeydown = (event) ->
|
482
|
+
return if $(event.target).closest(".#{CLASSES.BASE}").length
|
483
|
+
popover = $(event.currentTarget).children(".#{CLASSES.BASE}")[0]
|
484
|
+
|
485
|
+
if event.which in [Shopify.Keycodes.ENTER, Shopify.Keycodes.SPACE]
|
486
|
+
toggle(popover)
|
487
|
+
event.preventDefault()
|
488
|
+
|
489
|
+
else if event.which == Shopify.Keycodes.ESCAPE
|
490
|
+
deactivate()
|
491
|
+
event.preventDefault()
|
492
|
+
|
493
|
+
return
|
494
|
+
|
495
|
+
popoverPaneScroll = (event) ->
|
496
|
+
pane = event.currentTarget
|
497
|
+
delta = event.originalEvent.deltaY
|
498
|
+
up = delta < 0
|
499
|
+
|
500
|
+
[height, scrollHeight, scrollTop] = [
|
501
|
+
pane.offsetHeight
|
502
|
+
pane.scrollHeight
|
503
|
+
pane.scrollTop
|
504
|
+
]
|
505
|
+
|
506
|
+
prevent = ->
|
507
|
+
event.stopPropagation()
|
508
|
+
event.preventDefault()
|
509
|
+
|
510
|
+
scrollingDownPastBottom = !up && delta > scrollHeight - height - scrollTop
|
511
|
+
scrollingUpPastTop = up && -delta > scrollTop
|
512
|
+
|
513
|
+
if scrollingDownPastBottom
|
514
|
+
pane.scrollTop = scrollHeight
|
515
|
+
prevent()
|
516
|
+
|
517
|
+
if scrollingUpPastTop
|
518
|
+
pane.scrollTop = 0
|
519
|
+
prevent()
|
520
|
+
|
521
|
+
return
|
522
|
+
|
523
|
+
|
524
|
+
|
525
|
+
# Store app private methods in a secret object so we can access them
|
526
|
+
# on Popover.send(method)
|
527
|
+
|
528
|
+
secrets =
|
529
|
+
activeCache: activeCache
|
530
|
+
|
531
|
+
joinToExistingIDs: joinToExistingIDs
|
532
|
+
a11yPopovers: a11yPopovers
|
533
|
+
cachePopoverCSSProperties: cachePopoverCSSProperties
|
534
|
+
|
535
|
+
baseFontSize: baseFontSize
|
536
|
+
calculatePixelDimension: calculatePixelDimension
|
537
|
+
|
538
|
+
activate: activate
|
539
|
+
applyActivationMarkup: applyActivationMarkup
|
540
|
+
attachActiveEventListeners: attachActiveEventListeners
|
541
|
+
toggle: toggle
|
542
|
+
deactivate: deactivate
|
543
|
+
applyDeactivationMarkup: applyDeactivationMarkup
|
544
|
+
detachActiveEventListeners: detachActiveEventListeners
|
545
|
+
|
546
|
+
positionPopover: positionPopover
|
547
|
+
horizontallyPositionWithCenterAlignment: horizontallyPositionWithCenterAlignment
|
548
|
+
horizontallyPositionWithEdgeAlignment: horizontallyPositionWithEdgeAlignment
|
549
|
+
calculateHorizontalOffsets: calculateHorizontalOffsets
|
550
|
+
spaceDetailsForActivePopover: spaceDetailsForActivePopover
|
551
|
+
calculateMaxWidth: calculateMaxWidth
|
552
|
+
determineVerticalPositioning: determineVerticalPositioning
|
553
|
+
|
554
|
+
popoverFocus: popoverFocus
|
555
|
+
popoverKeydown: popoverKeydown
|
556
|
+
popoverPaneScroll: popoverPaneScroll
|
557
|
+
|
558
|
+
Lemon.make(Popover)
|
559
|
+
|
560
|
+
$(document).on("click focusin", popoverFocus)
|
561
|
+
$(document).on("keydown", ".#{CLASSES.CONTAINER}", popoverKeydown)
|
562
|
+
$(window).on("resize scroll", positionPopover)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<%
|
2
|
+
popover.configure do |config|
|
3
|
+
config.defaults(align: :center, relative_to: nil)
|
4
|
+
config.classes(base: "popover")
|
5
|
+
|
6
|
+
config.conditional_classes with: :align do |alignment|
|
7
|
+
{ base: "popover--align-to-#{alignment}" }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
%>
|
11
|
+
|
12
|
+
<div class="<%= popover.classes_for(:base) %>"<% unless popover.relative_to.nil? %> data-popover-horizontally-relative-to-closest="<%= popover.relative_to %>"<% end %>>
|
13
|
+
<div class="popover__tooltip"></div>
|
14
|
+
<div class="popover__content-wrapper">
|
15
|
+
<div class="popover__content">
|
16
|
+
<% if popover.block %>
|
17
|
+
<%= capture(popover, &popover.block) %>
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
File without changes
|