docks_theme_api 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +15 -0
  2. data/.babelrc +4 -0
  3. data/.editorconfig +8 -0
  4. data/.eslintrc +115 -0
  5. data/.gitignore +24 -0
  6. data/.rubocop.yml +20 -0
  7. data/.travis.yml +16 -0
  8. data/Gemfile +4 -0
  9. data/README.md +5 -0
  10. data/Rakefile +3 -0
  11. data/assets/images/icons.svg +63 -0
  12. data/assets/scripts/coffeescript/pattern_library_helpers.coffee +8 -0
  13. data/assets/scripts/javascript/pattern_library_helpers.js +11 -0
  14. data/assets/scripts/pattern_library.js +10380 -0
  15. data/assets/scripts/pattern_library_demo.js +0 -0
  16. data/assets/styles/less/pattern-library-helpers.less +103 -0
  17. data/assets/styles/pattern-library-demo.css +1882 -0
  18. data/assets/styles/pattern-library.css +1882 -0
  19. data/assets/styles/sass/pattern-library-helpers.sass +90 -0
  20. data/assets/styles/scss/pattern-library-helpers.scss +99 -0
  21. data/assets/styles/stylus/pattern-library-helpers.styl +90 -0
  22. data/assets/templates/erb/demo.erb +26 -0
  23. data/assets/templates/erb/layouts/demo.erb +17 -0
  24. data/assets/templates/erb/layouts/pattern.erb +76 -0
  25. data/assets/templates/erb/partials/sidebar.erb +124 -0
  26. data/assets/templates/erb/partials/symbols/class.erb +1 -0
  27. data/assets/templates/erb/partials/symbols/demo.erb +40 -0
  28. data/assets/templates/erb/partials/symbols/factory.erb +70 -0
  29. data/assets/templates/erb/partials/symbols/function.erb +103 -0
  30. data/assets/templates/erb/partials/symbols/mixin.erb +62 -0
  31. data/assets/templates/erb/partials/symbols/variable.erb +59 -0
  32. data/assets/templates/erb/pattern.erb +102 -0
  33. data/assets/templates/haml/demo.haml +14 -0
  34. data/assets/templates/haml/layouts/demo.haml +6 -0
  35. data/assets/templates/haml/layouts/pattern.haml +38 -0
  36. data/assets/templates/haml/partials/sidebar.haml +68 -0
  37. data/assets/templates/haml/partials/symbols/class.haml +1 -0
  38. data/assets/templates/haml/partials/symbols/demo.haml +23 -0
  39. data/assets/templates/haml/partials/symbols/factory.haml +38 -0
  40. data/assets/templates/haml/partials/symbols/function.haml +54 -0
  41. data/assets/templates/haml/partials/symbols/mixin.haml +31 -0
  42. data/assets/templates/haml/partials/symbols/variable.haml +22 -0
  43. data/assets/templates/haml/pattern.haml +54 -0
  44. data/assets/templates/slim/demo.slim +24 -0
  45. data/assets/templates/slim/layouts/demo.slim +5 -0
  46. data/assets/templates/slim/layouts/pattern.slim +48 -0
  47. data/assets/templates/slim/partials/sidebar.slim +112 -0
  48. data/assets/templates/slim/partials/symbols/class.slim +1 -0
  49. data/assets/templates/slim/partials/symbols/demo.slim +30 -0
  50. data/assets/templates/slim/partials/symbols/factory.slim +57 -0
  51. data/assets/templates/slim/partials/symbols/function.slim +81 -0
  52. data/assets/templates/slim/partials/symbols/mixin.slim +45 -0
  53. data/assets/templates/slim/partials/symbols/variable.slim +35 -0
  54. data/assets/templates/slim/pattern.slim +63 -0
  55. data/docks_config.rb +32 -0
  56. data/docks_theme_api.gemspec +37 -0
  57. data/gulpfile.js +88 -0
  58. data/karma.conf.js +6 -0
  59. data/lib/docks_theme_api/components/base_component.rb +99 -0
  60. data/lib/docks_theme_api/components/code_block_component.rb +10 -0
  61. data/lib/docks_theme_api/components/popover_component.rb +15 -0
  62. data/lib/docks_theme_api/components/table_component.rb +34 -0
  63. data/lib/docks_theme_api/components/tablist_component.rb +11 -0
  64. data/lib/docks_theme_api/components.rb +21 -0
  65. data/lib/docks_theme_api/helpers/ui_helper.rb +69 -0
  66. data/lib/docks_theme_api/theme.rb +21 -0
  67. data/lib/docks_theme_api.rb +1 -0
  68. data/package.json +60 -0
  69. data/source/behaviors/filterable/filterable.coffee +353 -0
  70. data/source/behaviors/filterable/filterable.js +0 -0
  71. data/source/behaviors/filterable/filterable.scss +34 -0
  72. data/source/behaviors/filterable/package.json +3 -0
  73. data/source/behaviors/index.js +0 -0
  74. data/source/components/avatar/avatar.erb +20 -0
  75. data/source/components/avatar/avatar.js +142 -0
  76. data/source/components/avatar/avatar.scss +200 -0
  77. data/source/components/avatar/avatar_container.erb +13 -0
  78. data/source/components/avatar/package.json +3 -0
  79. data/source/components/avatar/spec/avatar_spec.js +81 -0
  80. data/source/components/badge/badge.scss +158 -0
  81. data/source/components/button/button.scss +213 -0
  82. data/source/components/card/card.scss +32 -0
  83. data/source/components/code_block/code-block.scss +353 -0
  84. data/source/components/code_block/code_block.erb +95 -0
  85. data/source/components/code_block/code_block.js +444 -0
  86. data/source/components/code_block/package.json +3 -0
  87. data/source/components/code_block/spec/code_block_spec.js +10 -0
  88. data/source/components/demo/demo.js +244 -0
  89. data/source/components/demo/demo.scss +90 -0
  90. data/source/components/demo/package.json +3 -0
  91. data/source/components/exploded/exploded.erb +25 -0
  92. data/source/components/exploded/exploded.js +694 -0
  93. data/source/components/exploded/exploded.scss +166 -0
  94. data/source/components/exploded/package.json +3 -0
  95. data/source/components/field/field.js +24 -0
  96. data/source/components/field/field.scss +101 -0
  97. data/source/components/field/package.json +3 -0
  98. data/source/components/header/header.scss +33 -0
  99. data/source/components/iframe/iframe.erb +12 -0
  100. data/source/components/iframe/iframe.js +381 -0
  101. data/source/components/iframe/package.json +3 -0
  102. data/source/components/index.js +37 -0
  103. data/source/components/inline_group/inline-group.scss +14 -0
  104. data/source/components/internal_link/internal_link.js +49 -0
  105. data/source/components/internal_link/package.json +3 -0
  106. data/source/components/list/list.scss +230 -0
  107. data/source/components/modal/modal.coffee +84 -0
  108. data/source/components/modal/modal.erb +19 -0
  109. data/source/components/modal/modal.js +0 -0
  110. data/source/components/modal/modal.scss +57 -0
  111. data/source/components/modal/package.json +3 -0
  112. data/source/components/notice/notice.scss +48 -0
  113. data/source/components/popover/package.json +3 -0
  114. data/source/components/popover/popover.coffee +562 -0
  115. data/source/components/popover/popover.erb +21 -0
  116. data/source/components/popover/popover.js +0 -0
  117. data/source/components/popover/popover.scss +139 -0
  118. data/source/components/range/range.scss +78 -0
  119. data/source/components/resizable/package.json +3 -0
  120. data/source/components/resizable/resizable.erb +30 -0
  121. data/source/components/resizable/resizable.js +250 -0
  122. data/source/components/resizable/resizable.scss +245 -0
  123. data/source/components/resizable/size_buttons.js +249 -0
  124. data/source/components/scroll_container/package.json +3 -0
  125. data/source/components/scroll_container/scroll-container.scss +4 -0
  126. data/source/components/scroll_container/scroll_container.js +24 -0
  127. data/source/components/section/section.scss +99 -0
  128. data/source/components/select/package.json +3 -0
  129. data/source/components/select/select.erb +21 -0
  130. data/source/components/select/select.js +35 -0
  131. data/source/components/select/select.scss +163 -0
  132. data/source/components/table/package.json +3 -0
  133. data/source/components/table/table.erb +16 -0
  134. data/source/components/table/table.js +351 -0
  135. data/source/components/table/table.scss +236 -0
  136. data/source/components/tablist/package.json +3 -0
  137. data/source/components/tablist/tablist.erb +13 -0
  138. data/source/components/tablist/tablist.js +246 -0
  139. data/source/components/tablist/tablist.scss +191 -0
  140. data/source/components/tablist/tablist_panel.erb +14 -0
  141. data/source/components/tablist/tablist_tab.erb +20 -0
  142. data/source/components/toggle/package.json +3 -0
  143. data/source/components/toggle/toggle.erb +11 -0
  144. data/source/components/toggle/toggle.js +211 -0
  145. data/source/components/toggle/toggle_container.erb +30 -0
  146. data/source/components/vertical_spacer/vertical-spacer.scss +3 -0
  147. data/source/components/vertical_stack/vertical-stack.scss +19 -0
  148. data/source/components/xray/package.json +3 -0
  149. data/source/components/xray/xray.erb +50 -0
  150. data/source/components/xray/xray.js +123 -0
  151. data/source/components/xray/xray.scss +79 -0
  152. data/source/foundation/app/app.js +15 -0
  153. data/source/foundation/app/package.json +3 -0
  154. data/source/pattern-library-demo.scss +13 -0
  155. data/source/pattern-library.scss +13 -0
  156. data/source/pattern_library.js +8 -0
  157. data/source/pattern_library_demo.js +8 -0
  158. data/source/structures/index.js +11 -0
  159. data/source/structures/sidebar/package.json +3 -0
  160. data/source/structures/sidebar/sidebar.js +69 -0
  161. data/source/structures/sidebar/sidebar.scss +79 -0
  162. data/source/utilities/builder/builder.js +138 -0
  163. data/source/utilities/builder/package.json +3 -0
  164. data/source/utilities/client/client.js +7 -0
  165. data/source/utilities/client/package.json +3 -0
  166. data/source/utilities/colors/colors.scss +112 -0
  167. data/source/utilities/defaults/defaults.scss +38 -0
  168. data/source/utilities/dom_cache/dom_cache.js +24 -0
  169. data/source/utilities/dom_cache/package.json +3 -0
  170. data/source/utilities/events/events.js +25 -0
  171. data/source/utilities/events/package.json +3 -0
  172. data/source/utilities/font_sizes/font-sizes.scss +85 -0
  173. data/source/utilities/foundation/a11y.scss +10 -0
  174. data/source/utilities/foundation/base.scss +29 -0
  175. data/source/utilities/foundation/icon.scss +114 -0
  176. data/source/utilities/foundation/layout.scss +67 -0
  177. data/source/utilities/foundation/page.scss +39 -0
  178. data/source/utilities/foundation/type.scss +208 -0
  179. data/source/utilities/functions/functions.scss +127 -0
  180. data/source/utilities/keycodes/keycodes.js +23 -0
  181. data/source/utilities/keycodes/package.json +3 -0
  182. data/source/utilities/markup/markup.js +90 -0
  183. data/source/utilities/markup/package.json +3 -0
  184. data/source/utilities/media/media.scss +172 -0
  185. data/source/utilities/mixins/mixins.scss +89 -0
  186. data/source/utilities/naming_convention/naming_convention.js +3 -0
  187. data/source/utilities/naming_convention/package.json +3 -0
  188. data/source/utilities/numbers/numbers.js +14 -0
  189. data/source/utilities/numbers/package.json +3 -0
  190. data/source/utilities/painting/package.json +3 -0
  191. data/source/utilities/painting/painting.js +7 -0
  192. data/source/utilities/pattern/package.json +3 -0
  193. data/source/utilities/pattern/pattern.js +50 -0
  194. data/source/utilities/query_string/package.json +3 -0
  195. data/source/utilities/query_string/query_string.js +24 -0
  196. data/source/utilities/template/package.json +3 -0
  197. data/source/utilities/template/template.js +10 -0
  198. data/source/utilities/text_range/package.json +3 -0
  199. data/source/utilities/text_range/text_range.js +30 -0
  200. data/source/utilities/ui_events/package.json +3 -0
  201. data/source/utilities/ui_events/ui_events.js +85 -0
  202. data/source/utilities/variables/variables.scss +18 -0
  203. data/source/utilities/z_indexes/z-indexes.scss +88 -0
  204. data/source/vendor/array_includes.js +28 -0
  205. data/source/vendor/highlight.js +1142 -0
  206. data/source/vendor/index.js +1 -0
  207. data/source/vendor/matrix.js +399 -0
  208. data/source/vendor/query_string.js +66 -0
  209. data/spec/assets/.eslintrc +9 -0
  210. data/spec/assets/spec_fixture.js +33 -0
  211. data/spec/assets/spec_helper.js +19 -0
  212. data/spec/lib/components/base_component_spec.rb +156 -0
  213. data/spec/lib/components_spec.rb +30 -0
  214. data/spec/lib/helpers/ui_helper_spec.rb +62 -0
  215. data/spec/lib/theme_spec.rb +25 -0
  216. data/spec/spec_helper.rb +15 -0
  217. data/tasks/gulp/.eslintrc +6 -0
  218. data/tasks/gulp/browser_sync.js +8 -0
  219. data/tasks/gulp/code_quality/scripts.js +10 -0
  220. data/tasks/gulp/config/index.js +116 -0
  221. data/tasks/gulp/minify/scripts.js +13 -0
  222. data/tasks/gulp/minify/styles.js +13 -0
  223. data/tasks/gulp/pattern_library/index.js +5 -0
  224. data/tasks/gulp/pattern_library/scripts.js +10 -0
  225. data/tasks/gulp/pattern_library/styles.js +10 -0
  226. data/tasks/gulp/scripts.js +8 -0
  227. data/tasks/gulp/spec/scripts.js +11 -0
  228. data/tasks/gulp/styles.js +17 -0
  229. data/tasks/gulp/utilities/babel/relative_require.js +22 -0
  230. data/tasks/gulp/utilities/babel/spec_helper.js +20 -0
  231. data/tasks/gulp/utilities/browserify_bundler.js +22 -0
  232. data/tasks/gulp/utilities/handle_errors.js +13 -0
  233. data/tasks/gulp/watch.js +9 -0
  234. data/tasks/rake/rspec.rake +7 -0
  235. data/tasks/rake/rubocop.rake +8 -0
  236. data/tasks/rake/templates.rake +50 -0
  237. 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