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,236 @@
1
+ // ___ ___
2
+ // ___ / /\ _____ / /\
3
+ // / /\ / /::\ / /::\ / /:/_
4
+ // / /:/ / /:/\:\ / /:/\:\ ___ ___ / /:/ /\
5
+ // / /:/ / /:/~/::\ / /:/~/::\/__/\ / /\/ /:/ /:/_
6
+ // / /::\/__/:/ /:/\:\/__/:/ /:/\:\ \:\ / /:/__/:/ /:/ /\
7
+ // /__/:/\:\ \:\/:/__\/\ \:\/:/~/:/\ \:\ /:/\ \:\/:/ /:/
8
+ // \__\/ \:\ \::/ \ \::/ /:/ \ \:\/:/ \ \::/ /:/
9
+ // \ \:\ \:\ \ \:\/:/ \ \::/ \ \:\/:/
10
+ // \__\/\ \:\ \ \::/ \__\/ \ \::/
11
+ // \__\/ \__\/ \__\/
12
+
13
+ //*
14
+ // @pattern Table
15
+ //
16
+ // Tables are used to present tabular data only — never for layout! These tables
17
+ // are smarter than the average table, however. They will determine their
18
+ // intrinsic size (up to a stylesheet-specified maximum) and, when the space
19
+ // available to them is less than that size, they will begin to scroll.
20
+ //
21
+ // As you scroll, the first column will stay fixed, so you always have context,
22
+ // and the any given column will always be small enough to be fully visible. Not
23
+ // only that, but the table provides both keyboard input and interface elements
24
+ // that allow you to quickly shift to overscrolled columns.
25
+ //
26
+ // @since 1.0.0
27
+
28
+
29
+
30
+ $table--border: 1px solid color(gray-light);
31
+ $table--border-radius: double(default(border-radius));
32
+ $table--box-shadow-size: 3px 0 4px -1px;
33
+
34
+
35
+
36
+ //*
37
+ // A smarter `table` element. The `min-width` placed here will not actually
38
+ // be the minimum width of the rendered table; the provided minimum width will
39
+ // instead be used as the maximum width allowed in determining the preferred/
40
+ // intrinsic size of the table cells. That is, a table whose natural size is
41
+ // less than this amount will not overflow until the space it has is less than
42
+ // its intrinsic size, where a table with large cells will overflow at the
43
+ // provided `min-width` breakpoint.
44
+ //
45
+ // Visually, all tables will be 100% width. As noted above, tables will overflow
46
+ // once the lesser of the `min-width` below or the intrisic size of the table
47
+ // is reached. This overflowing is managed by the JavaScript component.
48
+ //
49
+ // @helper docks_table
50
+
51
+ .table {
52
+ width: 100%;
53
+ min-width: rem(500);
54
+
55
+ border-collapse: separate;
56
+ border-spacing: 0;
57
+ }
58
+
59
+
60
+
61
+ //*
62
+ // The outermost wrapper around a table. This wrapper is used to signal to the
63
+ // entire component when the table is in overflow (via the
64
+ // `table__container--is-overflowing` state). Doing so on the outmost container
65
+ // allows adjustments to both the table itself and the `table__actions`.
66
+ //
67
+ // In addition, `table__containers` will automatically create the required
68
+ // space between themselves and other content.
69
+
70
+ .table__container {
71
+ margin: default(spacing) auto;
72
+ }
73
+
74
+ //*
75
+ // Signals to the `table` that it should force the first cell in each row
76
+ // to float about the rest of the rows (the required left padding on the
77
+ // second cell in each row to account for the absolutely-positioned first cell
78
+ // is done automatically by the JavaScript side of things, as is the
79
+ // equalization of all of the first cells' widths). Additionally, this
80
+ // state causes the `table__actions` to be made visible.
81
+ //
82
+ // @set_by Table#check_for_overflow
83
+ // @demo_type none
84
+
85
+ .table__container--is-overflowing {
86
+ .table__cell:first-child {
87
+ position: absolute;
88
+ @include z-index(cell-persistant, table);
89
+ left: 0;
90
+
91
+ height: 100%;
92
+
93
+ // Start box shadow as transparent so it can be animated into view when
94
+ // the table is actually being scrolled.
95
+ box-shadow: $table--box-shadow-size rgba(color(black), 0);
96
+ transition: box-shadow 0.2s ease;
97
+ }
98
+
99
+ .table__actions {
100
+ transform: translateY(0);
101
+ max-height: 3rem;
102
+ padding-bottom: half(default(spacing));
103
+ }
104
+ }
105
+
106
+
107
+
108
+ //*
109
+ // A wrapper around the `table` that allows it to scroll when the minimum
110
+ // width of its columns (their intrinsic width or their width when the table
111
+ // is larger than the `min-width` set on `table`) is larger than the space
112
+ // available to this container.
113
+
114
+ .table__scroller {
115
+ max-width: 100%;
116
+ overflow-x: auto;
117
+ overflow-y: hidden;
118
+ -webkit-overflow-scrolling: touch;
119
+ }
120
+
121
+ //*
122
+ // The state that is added by the JavaScript component when the table is
123
+ // overflowed **and** the scroll position of this subcomponent is not fully
124
+ // pinned to the left.
125
+ //
126
+ // @set_by Table#handle_scroll, Table#shift_table
127
+
128
+ .table__scroller--is-scrolled {
129
+ .table__cell:first-child {
130
+ box-shadow: $table--box-shadow-size rgba(color(black), 0.1);
131
+ }
132
+ }
133
+
134
+
135
+
136
+ //*
137
+ // The wrapper around a `table` that provides a few visual pieces. Most
138
+ // noticeably, this adds the border and border radius on the outside of the
139
+ // table. These pieces must be on this container rather than the
140
+ // `table__container` subcomponent because it should only wrap around the
141
+ // `table`, and not the `table__actions`. These rules can't be on the
142
+ // `table__scroller` subcomponent, either, because it must be `overflow: hidden`
143
+ // (to hide the parts outside the rounded corners), where the scroller must
144
+ // be allowed to scroll.
145
+ //
146
+ // More importantly, however, this container is `position: relative` and, as
147
+ // such, acts as the element against which the persistant cells are
148
+ // positioned. These rules can't be on the `table__scroller` because it scrolls,
149
+ // where the persistant cells should be fixed even as the rest of the table
150
+ // scrolls.
151
+
152
+ .table__backdrop {
153
+ // position
154
+ position: relative;
155
+ @include z-index(backdrop, table);
156
+
157
+ // box model
158
+ max-width: 100%;
159
+ clear: both;
160
+
161
+ // backdrop
162
+ overflow: hidden;
163
+ border: $table--border;
164
+ border-radius: $table--border-radius;
165
+ }
166
+
167
+
168
+
169
+ //*
170
+ // A container around the actions that can be performed on the `table`. For now,
171
+ // this includes only the segmented button to shift the table right/ left by
172
+ // one column.
173
+
174
+ .table__actions {
175
+ transform: translateY(140%);
176
+ max-height: 0;
177
+ float: right;
178
+
179
+ transition: transform 0.3s $bouncy-transition,
180
+ max-height 0.3s ease,
181
+ padding-bottom 0.3s ease;
182
+ }
183
+
184
+
185
+
186
+ //*
187
+ // The header row of the table.
188
+
189
+ .table__header {
190
+ font-weight: 400;
191
+ padding: 0;
192
+ text-align: left;
193
+ }
194
+
195
+
196
+
197
+ //*
198
+ // The container around the rows that make up the body of the table.
199
+
200
+ .table__body {
201
+ // Striped rows!
202
+ > .table__row:nth-child(odd) > .table__cell {
203
+ background-color: color(gray-lighter);
204
+ }
205
+ }
206
+
207
+
208
+
209
+ //*
210
+ // A row of table cells.
211
+
212
+ .table__row {
213
+ // Hide the overflow so that box shadows on persistant cells don't bleed
214
+ // between rows.
215
+ overflow: hidden;
216
+ }
217
+
218
+
219
+
220
+ //*
221
+ // An individual table cell.
222
+
223
+ .table__cell {
224
+ position: relative;
225
+ @include z-index(cell, table);
226
+ vertical-align: top;
227
+ padding: half(default(spacing));
228
+ background-color: color(white);
229
+ }
230
+
231
+ //*
232
+ // A table cell with center-aligned text.
233
+
234
+ .table__cell--centered {
235
+ text-align: center;
236
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "main": "tablist.js"
3
+ }
@@ -0,0 +1,13 @@
1
+ <%
2
+ tablist.configure do |config|
3
+ config.defaults(manage_url?: false, size: :regular)
4
+ config.classes(base: "tablist")
5
+
6
+ config.conditional_classes(from: :size, large: { base: "tablist--large" })
7
+ config.conditional_classes(if: :manage_url?, base: "tablist--manages-url")
8
+ end
9
+ %>
10
+
11
+ <div class="<%= tablist.classes_for(:base) %>" role="tablist">
12
+ <%= capture(tablist, &tablist.block) %>
13
+ </div>
@@ -0,0 +1,246 @@
1
+ import QueryString from "~utilities/query_string";
2
+ import Builder from "~utilities/builder";
3
+ import Cache from "~utilities/dom_cache";
4
+
5
+ const classes = {
6
+ root: "tablist",
7
+ tab: "tablist__tab",
8
+ panel: "tablist__panel"
9
+ };
10
+
11
+ const variants = {
12
+ root: { manages_url: "tablist--manages-url" }
13
+ };
14
+
15
+ const states = {
16
+ tab: { active: "tablist__tab--is-active" },
17
+ panel: { active: "tablist__panel--is-active" }
18
+ };
19
+
20
+ var Tablist, tab_click, panel_for_tab, tab_for_panel, tablist_for_node, a11y,
21
+ apply_activation_markup, remove_activation_markup, panel_containing_node;
22
+
23
+ //*
24
+ // Manages a click on a tab by finding the associated `Tablist` and activating
25
+ // the tab that was clicked on.
26
+ //
27
+ // @param {Object} event - The `click` event.
28
+ // @private
29
+
30
+ tab_click = (event) => {
31
+ var tablist;
32
+
33
+ event.preventDefault();
34
+
35
+ tablist = Tablist.for(event.target);
36
+ if(!tablist) { return; }
37
+ tablist.activate_tab($(event.currentTarget).closest(`.${classes.tab}`)[0]);
38
+ };
39
+
40
+ //*
41
+ // Finds the tab panel associated with the passed tab. The association is based
42
+ // on the ID of the tab panel matching the `href` of the tab.
43
+ //
44
+ // @param {HTMLElement} tab - The tab for which you want the associated panel.
45
+ // @private
46
+ //
47
+ // @returns {HTMLElement | null} The associated tab panel or, if no matching
48
+ // panel was found, `null`.
49
+
50
+ panel_for_tab = (tab) => {
51
+ return tab && document.getElementById(tab.getAttribute("href").replace("#", ""));
52
+ };
53
+
54
+ //*
55
+ // Finds the tab associated with the passed panel. The association is based
56
+ // on the ID of the tab panel matching the `href` of the tab.
57
+ //
58
+ // @param {HTMLElement} tab - The tab for which you want the associated panel.
59
+ // @private
60
+ //
61
+ // @returns {HTMLElement | null} The associated tab or, if no matching panel
62
+ // was found, `null`.
63
+
64
+ tab_for_panel = (panel) => {
65
+ return panel && document.querySelector(`.${classes.tab}[href='#${panel.id}']`);
66
+ };
67
+
68
+ //*
69
+ // Writes all of the required accessibility markup to the tablist and its
70
+ // subcomponents. This includes IDs for the tablist and its tabs/ panels,
71
+ // roles for the same, and the `aria-` associations between tabs and their
72
+ // corresponding panels.
73
+ //
74
+ // @param {HTMLElement} tablist - The root node of the tablist.
75
+ // @private
76
+
77
+ a11y = (() => {
78
+ var current_ids, id_for;
79
+
80
+ current_ids = {
81
+ [classes.root]: 1,
82
+ [classes.tab]: 1,
83
+ [classes.panel]: 1
84
+ };
85
+
86
+ id_for = (node) => {
87
+ var type = node.className.split(" ")[0];
88
+ return `${type}--${current_ids[type]++}`;
89
+ };
90
+
91
+ return (tablist) => {
92
+ var panel, tab_id, panel_id, tab;
93
+
94
+ tablist.id = tablist.id || id_for(tablist);
95
+ tablist.setAttribute("role", "tablist");
96
+
97
+ for(tab of Array.from(tablist.querySelectorAll(`.${classes.tab}`))) {
98
+ panel = panel_for_tab(tab);
99
+ if(!panel) { continue; }
100
+
101
+ tab_id = tab.id || id_for(tab);
102
+ panel_id = panel.id || id_for(panel);
103
+
104
+ tab.id = tab_id;
105
+ tab.setAttribute("role", "tab");
106
+ tab.setAttribute("aria-controls", panel_id);
107
+ tab.setAttribute("href", `#${panel_id}`);
108
+
109
+ panel.id = panel_id;
110
+ panel.setAttribute("role", "tab-panel");
111
+ panel.setAttribute("aria-labelledby", tab_id);
112
+ panel.setAttribute("aria-hidden", !panel.classList.contains(states.panel.active));
113
+ }
114
+ };
115
+ })();
116
+
117
+ apply_activation_markup = (node) => {
118
+ if(!node) { return; }
119
+
120
+ if(node.classList.contains(classes.tab)) {
121
+ node.classList.add(states.tab.active);
122
+ } else {
123
+ node.classList.add(states.panel.active);
124
+ }
125
+ };
126
+
127
+ remove_activation_markup = (node) => {
128
+ if(!node) { return; }
129
+
130
+ if(node.classList.contains(classes.tab)) {
131
+ node.classList.remove(states.tab.active);
132
+ } else {
133
+ node.classList.remove(states.panel.active);
134
+ }
135
+ };
136
+
137
+ panel_containing_node = (node) => {
138
+ return $(node).closest(`.${classes.panel}`)[0];
139
+ };
140
+
141
+ tablist_for_node = (node) => {
142
+ if(node.classList.contains(classes.panel)) {
143
+ node = tab_for_panel(node);
144
+ }
145
+
146
+ return $(node).closest(`.${classes.root}`)[0];
147
+ };
148
+
149
+ //*
150
+ // The constructor around a `Tablist` component. This constructor returns a very
151
+ // small API: only an `activate_tab` method is exposed, which will activate the
152
+ // passed tab in the tablist. This constructor will also ensure that all the
153
+ // aria properties and associations are hooked up correctly.
154
+
155
+ Tablist = (root) => {
156
+ var active_tab = root.querySelector(`.${states.tab.active}`),
157
+ active_panel = panel_for_tab(active_tab),
158
+ saved_tab, api, self;
159
+
160
+ a11y(root);
161
+
162
+ self = {
163
+ root,
164
+ id: root.id,
165
+ active_panel: panel_for_tab(active_tab),
166
+ manages_url: root.classList.contains(variants.root.manages_url)
167
+ };
168
+
169
+ api = {
170
+ //*
171
+ // Activates the passed tab, deactivating the currently-active tab, if there
172
+ // is one (and it is not the passed tab).
173
+ //
174
+ // @param {HTMLElement} tab - The tab to activate.
175
+
176
+ activate_tab(tab) { this.active_tab = tab; },
177
+
178
+ get active_tab() { return active_tab; },
179
+ set active_tab(tab) {
180
+ var panel = panel_for_tab(tab);
181
+
182
+ apply_activation_markup(tab);
183
+ apply_activation_markup(panel);
184
+
185
+ if(!tab || tab === active_tab) { return; }
186
+
187
+ remove_activation_markup(active_tab);
188
+ remove_activation_markup(active_panel);
189
+
190
+ active_tab = tab;
191
+ active_panel = panel;
192
+
193
+ if(this.manages_url && QueryString.get(this.id) !== tab.id) {
194
+ QueryString.set(this.id, tab.id);
195
+ }
196
+ },
197
+
198
+ get active_panel() { return active_panel; },
199
+ set active_panel(panel) {
200
+ this.active_tab = panel_for_tab(panel);
201
+ }
202
+ };
203
+
204
+ if(self.manages_url) {
205
+ saved_tab = QueryString.get(self.id);
206
+ if(saved_tab) { api.active_tab = document.getElementById(saved_tab); }
207
+ } else {
208
+ api.active_tab = active_tab;
209
+ }
210
+
211
+ return api;
212
+ };
213
+
214
+ Tablist.for = (node) => {
215
+ var tablist_node = $(node).closest(`.${classes.root}`)[0],
216
+ containing_panel;
217
+
218
+ if(!tablist_node) {
219
+ containing_panel = node.classList.contains(classes.panel) ? node : panel_containing_node(node);
220
+ if(!containing_panel) { return false; }
221
+ tablist_node = tablist_for_node(containing_panel);
222
+ }
223
+
224
+ if(!tablist_node) { return false; }
225
+ return Cache(tablist_node).get(classes.root);
226
+ };
227
+
228
+ Tablist.init = () => {
229
+ Builder.build_and_cache(Tablist, { name: classes.root });
230
+ $(document).on("click", `.${classes.tab}`, tab_click);
231
+ };
232
+
233
+ Tablist.activate_panel_containing = (node) => {
234
+ var panel = $(node).closest(`.${classes.panel}`)[0],
235
+ tablist = Tablist.for(panel);
236
+
237
+ if(tablist) { tablist.active_tab = tab_for_panel(panel); }
238
+ return !!tablist;
239
+ };
240
+
241
+ Tablist.is_in_active_panel = (node) => {
242
+ var panel = panel_containing_node(node);
243
+ return !!panel && panel.classList.contains(states.panel.active);
244
+ };
245
+
246
+ export default Tablist;
@@ -0,0 +1,191 @@
1
+ // ___ ___
2
+ // ___ / /\ _____ ___ / /\ ___
3
+ // / /\ / /::\ / /::\ / /\ / /:/_ / /\
4
+ // / /:/ / /:/\:\ / /:/\:\ ___ ___ / /:/ / /:/ /\ / /:/
5
+ // / /:/ / /:/~/::\ / /:/~/::\/__/\ / /\/__/::\ / /:/ /::\ / /:/
6
+ // / /::\/__/:/ /:/\:\/__/:/ /:/\:\ \:\ / /:/\__\/\:\__/__/:/ /:/\:\/ /::\
7
+ // /__/:/\:\ \:\/:/__\/\ \:\/:/~/:/\ \:\ /:/ \ \:\/\ \:\/:/~/:/__/:/\:\
8
+ // \__\/ \:\ \::/ \ \::/ /:/ \ \:\/:/ \__\::/\ \::/ /:/\__\/ \:\
9
+ // \ \:\ \:\ \ \:\/:/ \ \::/ /__/:/ \__\/ /:/ \ \:\
10
+ // \__\/\ \:\ \ \::/ \__\/ \__\/ /__/:/ \__\/
11
+ // \__\/ \__\/ \__\/
12
+
13
+ //*
14
+ // @pattern Tablist
15
+ //
16
+ // Tablists are used to create groups of related content that is conditionally
17
+ // shown or hidden based on the selected tab. The JavaScript part of this
18
+ // components ensures that the required accessibility-related markup is added
19
+ // to the tabs, and a number of different styles are available depending on
20
+ // the context in which the tabs are being used.
21
+ //
22
+ // @since 1.0.0
23
+
24
+ //*
25
+ // The container for a set of tabs. While panels and tabs both use `tablist`
26
+ // as their base component, only `tablist__tab`s are child nodes of this
27
+ // component; `tablist__panel`s (and their container) are separate from the
28
+ // document to allow tabs to move around the page independently of the content
29
+ // they actually activate.
30
+ //
31
+ // The `tablist--manages-url` variant is unique in that it has no visual
32
+ // differences, but will preserve the selected tab via a query string parameter
33
+ // and restore the selected tab on subsequent page loads. To add this
34
+ // functionality, simply pass `true` for the `:manage_url` option of the
35
+ // `docks_tablist` view helper.
36
+ //
37
+ // @helper docks_tablist
38
+
39
+ .tablist {
40
+ // box model
41
+ margin: 0;
42
+ padding: 0;
43
+ display: flex;
44
+ justify-content: center;
45
+ padding: half(default(spacing));
46
+ margin-right: negative(half(default(spacing)));
47
+
48
+ // backdrop
49
+ list-style: none;
50
+ }
51
+
52
+ //*
53
+ // A larger set of tabs (both in terms of the font size of the tabs and the
54
+ // padding within each tab).
55
+ //
56
+ // @set_by :size (:large)
57
+
58
+ .tablist--large {
59
+ margin: double(default(spacing)) 0;
60
+
61
+ > .tablist__tab {
62
+ @include font-size(tablist--large);
63
+ line-height: 1;
64
+ padding: default(spacing) multiply(default(spacing), 2.5) multiply(default(spacing), 1.2);
65
+ border: none !important;
66
+ opacity: 1 !important;
67
+ }
68
+
69
+ > .tablist__tab--is-active {
70
+ background-color: ui-color(gray, light);
71
+ }
72
+ }
73
+
74
+ //*
75
+ // A variation of the tablist that forces the JavaScript to preserve the
76
+ // selected tab and reload that tab on subsequent page loads.
77
+ //
78
+ // @demo_type none
79
+ // @set_by :manage_url
80
+
81
+ .tablist--manages-url {}
82
+
83
+
84
+
85
+
86
+ //*
87
+ // A single tab in the tablist. Tabs will be a relatively light color by
88
+ // default with progressively darker shades used on `:focus`/`:hover` and when
89
+ // `:active`.
90
+ //
91
+ // Tabs should still work even when there is no JavaScript or CSS. To make this
92
+ // work, ensure that each `tablist__tab` is an `a` tag with an `href` that
93
+ // points to the `id` of the relevant `tablist__panel`. Likewise, the `id`s of
94
+ // the tab and panel should match up vai the `aria-controls` and
95
+ // `aria-labelledby` properties. The JavaScript component, if run, will
96
+ // guarantee all of these associations.
97
+ //
98
+ // @helper docks_tablist_tab
99
+
100
+ .tablist__tab {
101
+ // box model
102
+ display: inline-block;
103
+ padding: 0 default(control-padding);
104
+
105
+ // backdrop
106
+ opacity: 0.3;
107
+ overflow: hidden;
108
+ border: 1px solid transparent;
109
+ border-radius: default(border-radius);
110
+ transition: opacity 0.3s ease, border-color 0.3s ease;
111
+
112
+ // type
113
+ @include font-size(control);
114
+ line-height: default(control-size);
115
+ color: ui-color(gray, darker);
116
+ text-align: center;
117
+ text-decoration: none;
118
+ white-space: nowrap;
119
+ text-overflow: ellipsis;
120
+
121
+ &:focus,
122
+ &:hover {
123
+ outline: none;
124
+ opacity: 0.5;
125
+ }
126
+
127
+ > .icon {
128
+ height: double(default(spacing));
129
+ width: double(default(spacing));
130
+ @include icon--recolor(gray, dark);
131
+ stroke-width: 3px;
132
+ }
133
+ }
134
+
135
+ //*
136
+ // The variation added to the tab whose corresponding tab is currently
137
+ // visible. This class can either be applied manually by setting the `:active?`
138
+ // argument to the helper method to `true` or by clicking on the correct tab
139
+ // (which does all the required attribute manipulations through the JavaScript
140
+ // component).
141
+ //
142
+ // @demo_type none
143
+ // @set_by :active?
144
+
145
+ .tablist__tab--is-active {
146
+ &, &:hover, &:focus {
147
+ border-color: ui-color(gray, darker);
148
+ opacity: 1;
149
+ }
150
+ }
151
+
152
+ //*
153
+ // The text within a tab. This container is required in order for the text and
154
+ // (optional) icon to be above one another.
155
+
156
+ .tablist__tab__text {
157
+ display: block;
158
+ }
159
+
160
+
161
+
162
+ //*
163
+ // A panel that contains the content that should be shown when the associated
164
+ // tab is active.
165
+ //
166
+ // @helper docks_tablist_panel
167
+
168
+ .tablist__panel {
169
+ display: none;
170
+ }
171
+
172
+ //*
173
+ // A state indicating that the associated tab is active and the contents of
174
+ // this panel should be made visible. There is no animation when switching
175
+ // between tabs — they simply pop in and out of view.
176
+ //
177
+ // @demo_type none
178
+ // @set_by :active?
179
+
180
+ .tablist__panel--is-active {
181
+ display: block;
182
+ }
183
+
184
+
185
+
186
+ //*
187
+ // A container around all of the panels for a tablist. This container
188
+ // technically does nothing, but is a nice way to isolate a set of tab panels
189
+ // from the surrounding content.
190
+
191
+ .tablist__panel-container {}
@@ -0,0 +1,14 @@
1
+ <%
2
+ panel.configure do |config|
3
+ config.defaults active?: false,
4
+ id: "panel",
5
+ tab_id: "tab"
6
+
7
+ config.classes(base: "tablist__panel")
8
+ config.conditional_classes(if: :active?, base: "tablist__panel--is-active")
9
+ end
10
+ %>
11
+
12
+ <div class="<%= panel.classes_for(:base) %>" role="tab-panel" id="<%= panel.id %>" aria-hidden="<%= !panel.active? %>" aria-labelledby="<%= panel.tab_id %>">
13
+ <%= capture(panel, &panel.block) %>
14
+ </div>