@woylie/doggo 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +421 -0
- package/assets/js/hooks/accordion.js +33 -0
- package/assets/js/hooks/carousel.js +315 -0
- package/assets/js/hooks/combobox.js +370 -0
- package/assets/js/hooks/dialog.js +77 -0
- package/assets/js/hooks/menu.js +138 -0
- package/assets/js/hooks/menu_button.js +35 -0
- package/assets/js/hooks/split_pane.js +100 -0
- package/assets/js/hooks/tabs.js +80 -0
- package/assets/js/hooks/toolbar.js +100 -0
- package/assets/js/hooks/tooltip.js +48 -0
- package/assets/js/hooks/tree.js +197 -0
- package/assets/js/index.js +11 -0
- package/assets/js/navigation.js +69 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Mathias Polligkeit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# Doggo
|
|
2
|
+
|
|
3
|
+
[](https://hex.pm/packages/doggo)  [](https://coveralls.io/github/woylie/doggo)
|
|
4
|
+
|
|
5
|
+
Headless UI component collection for Phoenix, focused on semantics and
|
|
6
|
+
accessibility.
|
|
7
|
+
|
|
8
|
+
For a full list of available components, please refer to the
|
|
9
|
+
[documentation](https://hexdocs.pm/doggo/Doggo.html).
|
|
10
|
+
|
|
11
|
+
Doggo ships as two packages: a Hex package called `doggo` that defines
|
|
12
|
+
Phoenix LiveView components and an npm package called `@woylie/doggo` that
|
|
13
|
+
defines the JavaScript hooks for those components. The hooks are described
|
|
14
|
+
below under [Phoenix LiveView Hooks](#phoenix-liveview-hooks).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
The package can be installed by adding `doggo` to your list of dependencies in
|
|
19
|
+
`mix.exs`:
|
|
20
|
+
|
|
21
|
+
```elixir
|
|
22
|
+
def deps do
|
|
23
|
+
[
|
|
24
|
+
{:doggo, "~> 0.15.0"}
|
|
25
|
+
]
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Compatibility
|
|
30
|
+
|
|
31
|
+
This package is tested against the Elixir and OTP versions that are still
|
|
32
|
+
supported upstream. Older versions down to the requirement in `mix.exs` may
|
|
33
|
+
still work, but they are not covered by CI and not officially supported.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Use `Doggo.Components` in your core components module or in a separate module.
|
|
38
|
+
`Doggo.Components` defines macros that generate Phoenix components.
|
|
39
|
+
|
|
40
|
+
```elixir
|
|
41
|
+
defmodule MyAppWeb.CoreComponents do
|
|
42
|
+
use Doggo.Components
|
|
43
|
+
use Phoenix.Component
|
|
44
|
+
|
|
45
|
+
build_alert()
|
|
46
|
+
build_alert_dialog()
|
|
47
|
+
|
|
48
|
+
build_button(
|
|
49
|
+
modifiers: [
|
|
50
|
+
size: [values: ["normal", "small"], default: "normal"]
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Each modifier results in an additional attribute that is translated into a
|
|
57
|
+
data attribute. You can use the button defined above like this:
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<.button size="small">Edit</.button>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The resulting HTML code will look similar to:
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<button data-size="small">Edit</button>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If no `type` option is set, a `string` attribute is added, but you can use any
|
|
70
|
+
attribute type, as long as the value can be converted to a string. Boolean
|
|
71
|
+
attributes result in a presence-only boolean data attribute.
|
|
72
|
+
|
|
73
|
+
```elixir
|
|
74
|
+
build_button(modifiers: [full_width: [type: :boolean]])
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If the value is `true`, the attribute is added:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<!-- code -->
|
|
81
|
+
<.button full_width>Edit</.button>
|
|
82
|
+
|
|
83
|
+
<!-- output -->
|
|
84
|
+
<button data-full-width>Edit</button>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If the attribute is omitted or the value is `false`, the attribute is omitted:
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<!-- code -->
|
|
91
|
+
<.button full_width={false}>Edit</.button>
|
|
92
|
+
|
|
93
|
+
<!-- output -->
|
|
94
|
+
<button>Edit</button>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Most of the components have a base class that matches the component name.
|
|
98
|
+
|
|
99
|
+
You can override the base class in the component options:
|
|
100
|
+
|
|
101
|
+
```elixir
|
|
102
|
+
defmodule MyAppWeb.CoreComponents do
|
|
103
|
+
use Doggo.Components
|
|
104
|
+
use Phoenix.Component
|
|
105
|
+
|
|
106
|
+
build_button(
|
|
107
|
+
base_class: "alt-button",
|
|
108
|
+
modifiers: [size: [values: ["normal", "small"], default: "normal"]]
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
To remove the base class, just set it to `nil`.
|
|
114
|
+
|
|
115
|
+
It is also possible to change the name of the generated component, which can be
|
|
116
|
+
useful if you want to compile multiple variants of the same component, or if
|
|
117
|
+
your design system uses different names.
|
|
118
|
+
|
|
119
|
+
```elixir
|
|
120
|
+
build_button(name: :alt_button, base_class: "alt-button")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This button could be used with:
|
|
124
|
+
|
|
125
|
+
```elixir
|
|
126
|
+
<.alt_button>Edit</.alt_button>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Refer to the `Doggo.Components` module documentation for more information about
|
|
130
|
+
the options and the individual components.
|
|
131
|
+
|
|
132
|
+
### Phoenix LiveView Hooks
|
|
133
|
+
|
|
134
|
+
Some components need a JavaScript hook. The hooks are ES modules, and there are
|
|
135
|
+
two ways to install them.
|
|
136
|
+
|
|
137
|
+
#### From your `deps` folder
|
|
138
|
+
|
|
139
|
+
The modules are included in the Hex package. You can point at them in the
|
|
140
|
+
`deps` folder:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"dependencies": {
|
|
145
|
+
"@woylie/doggo": "link:../deps/doggo"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### From npm
|
|
151
|
+
|
|
152
|
+
The modules are also published as
|
|
153
|
+
[@woylie/doggo](https://www.npmjs.com/package/@woylie/doggo). Install them with
|
|
154
|
+
your package manager:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm install @woylie/doggo
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The Hex package and the npm package share the version number. Pin the same
|
|
161
|
+
version for both packages. A version mismatch can lead to issues.
|
|
162
|
+
|
|
163
|
+
Then register the hooks for the components you build in your `app.js`:
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
import {
|
|
167
|
+
Accordion,
|
|
168
|
+
Carousel,
|
|
169
|
+
Combobox,
|
|
170
|
+
Dialog,
|
|
171
|
+
Menu,
|
|
172
|
+
MenuButton,
|
|
173
|
+
SplitPane,
|
|
174
|
+
Tabs,
|
|
175
|
+
Toolbar,
|
|
176
|
+
Tooltip,
|
|
177
|
+
Tree,
|
|
178
|
+
} from "@woylie/doggo";
|
|
179
|
+
|
|
180
|
+
const hooks = {
|
|
181
|
+
"Doggo.Accordion": Accordion,
|
|
182
|
+
"Doggo.Carousel": Carousel,
|
|
183
|
+
"Doggo.Combobox": Combobox,
|
|
184
|
+
"Doggo.Dialog": Dialog,
|
|
185
|
+
"Doggo.Menu": Menu,
|
|
186
|
+
"Doggo.MenuButton": MenuButton,
|
|
187
|
+
"Doggo.SplitPane": SplitPane,
|
|
188
|
+
"Doggo.Tabs": Tabs,
|
|
189
|
+
"Doggo.Toolbar": Toolbar,
|
|
190
|
+
"Doggo.Tooltip": Tooltip,
|
|
191
|
+
"Doggo.Tree": Tree,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const liveSocket = new LiveSocket("/live", Socket, {
|
|
195
|
+
// ...
|
|
196
|
+
hooks,
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The keys are the names the components render in `phx-hook`, so they have to
|
|
201
|
+
match exactly.
|
|
202
|
+
|
|
203
|
+
It is recommended to only import the hooks you need to keep your bundle size
|
|
204
|
+
small.
|
|
205
|
+
|
|
206
|
+
To use the hooks in your storybook, register the same map in `storybook.js`:
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
import {
|
|
210
|
+
Accordion,
|
|
211
|
+
Carousel,
|
|
212
|
+
Combobox,
|
|
213
|
+
Dialog,
|
|
214
|
+
Menu,
|
|
215
|
+
MenuButton,
|
|
216
|
+
SplitPane,
|
|
217
|
+
Tabs,
|
|
218
|
+
Toolbar,
|
|
219
|
+
Tooltip,
|
|
220
|
+
Tree,
|
|
221
|
+
} from "@woylie/doggo";
|
|
222
|
+
|
|
223
|
+
(function () {
|
|
224
|
+
window.storybook = {
|
|
225
|
+
Hooks: {
|
|
226
|
+
"Doggo.Accordion": Accordion,
|
|
227
|
+
"Doggo.Carousel": Carousel,
|
|
228
|
+
"Doggo.Combobox": Combobox,
|
|
229
|
+
"Doggo.Dialog": Dialog,
|
|
230
|
+
"Doggo.Menu": Menu,
|
|
231
|
+
"Doggo.MenuButton": MenuButton,
|
|
232
|
+
"Doggo.SplitPane": SplitPane,
|
|
233
|
+
"Doggo.Tabs": Tabs,
|
|
234
|
+
"Doggo.Toolbar": Toolbar,
|
|
235
|
+
"Doggo.Tooltip": Tooltip,
|
|
236
|
+
"Doggo.Tree": Tree,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
})();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Storybook
|
|
243
|
+
|
|
244
|
+
Doggo can generate
|
|
245
|
+
[Phoenix Storybook](https://hex.pm/packages/phoenix_storybook) stories for the
|
|
246
|
+
generated components. After you followed the installation instructions of
|
|
247
|
+
Phoenix Storybook, you can run a mix task to generate the stories:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
mix dog.gen.stories -m MyAppWeb.CoreComponents -o storybook --all
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Here, `MyAppWeb.CoreComponents` is the module in which you added
|
|
254
|
+
`use Doggo.Components`, and `storybook` is the path to the storybook folder.
|
|
255
|
+
|
|
256
|
+
The task will only generate story modules for the components that you
|
|
257
|
+
configured. The stories will include variations for all configured modifiers.
|
|
258
|
+
|
|
259
|
+
You don't need to update the stories after changing the modifiers of a
|
|
260
|
+
component. However, you'll need to run the task again after adding new
|
|
261
|
+
components to your module, or potentially after a new Doggo version was
|
|
262
|
+
released.
|
|
263
|
+
|
|
264
|
+
The task will ask for confirmation to overwrite existing stories. To only
|
|
265
|
+
write the story for a single component, you can run:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
mix dog.gen.stories -m MyAppWeb.CoreComponents -o storybook -c button
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### PurgeCSS
|
|
272
|
+
|
|
273
|
+
You can generate a safelist with the CSS classes and data attributes of all
|
|
274
|
+
configured components with:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
mix dog.safelist -m MyAppWeb.CoreComponents -o assets/doggo_safelist.txt
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Visually hidden text
|
|
281
|
+
|
|
282
|
+
Several components render text that is meant to be available to screen readers
|
|
283
|
+
but not shown on screen, and they mark it with the `data-visually-hidden`
|
|
284
|
+
attribute. The library ships no CSS, so you have to define the rule yourself.
|
|
285
|
+
|
|
286
|
+
```css
|
|
287
|
+
[data-visually-hidden] {
|
|
288
|
+
position: absolute;
|
|
289
|
+
width: 1px;
|
|
290
|
+
height: 1px;
|
|
291
|
+
overflow: hidden;
|
|
292
|
+
white-space: nowrap;
|
|
293
|
+
clip-path: inset(50%);
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Do not use `display: none` or `visibility: hidden` here. Both remove the element
|
|
298
|
+
from the accessibility tree, so the text is hidden from screen readers as well,
|
|
299
|
+
which is the opposite of what the attribute is for. The page looks correct while
|
|
300
|
+
the icon has lost its description and the field has lost its label.
|
|
301
|
+
|
|
302
|
+
### Field error announcements
|
|
303
|
+
|
|
304
|
+
The `field` component always renders its error list, even when the field has no
|
|
305
|
+
errors, because a live region that is added to the page at the same time as its
|
|
306
|
+
content is not reliably announced. The element has to be there first, so it
|
|
307
|
+
cannot be conditional.
|
|
308
|
+
|
|
309
|
+
Depending on your styles, an empty `.field-errors` element can cause a visual
|
|
310
|
+
gap. To take it out of the flow without taking it out of the accessibility tree:
|
|
311
|
+
|
|
312
|
+
```css
|
|
313
|
+
.field:not([data-invalid]) .field-errors {
|
|
314
|
+
position: absolute;
|
|
315
|
+
width: 1px;
|
|
316
|
+
height: 1px;
|
|
317
|
+
overflow: hidden;
|
|
318
|
+
white-space: nowrap;
|
|
319
|
+
clip-path: inset(50%);
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Do not use `display: none` or `visibility: hidden` here. Both remove the element
|
|
324
|
+
from the accessibility tree, so an error that appears later is not announced,
|
|
325
|
+
which is the whole reason the list is rendered up front.
|
|
326
|
+
|
|
327
|
+
The `data-invalid` attribute is set on the field wrapper whenever the field has
|
|
328
|
+
errors. The `:empty` selector does not work here, because the rendered list
|
|
329
|
+
contains whitespace.
|
|
330
|
+
|
|
331
|
+
## Design decisions
|
|
332
|
+
|
|
333
|
+
- Favor semantic HTML elements over CSS classes for structure and clarity.
|
|
334
|
+
- Adhere to accessibility guidelines with appropriate ARIA attributes and roles.
|
|
335
|
+
- Utilize semantic HTML and ARIA attributes for style bindings to states, rather
|
|
336
|
+
than relying on CSS classes.
|
|
337
|
+
- Where state or variations cannot be expressed semantically, use data
|
|
338
|
+
attributes.
|
|
339
|
+
- The library is designed without default styles and does not prefer any
|
|
340
|
+
particular CSS framework.
|
|
341
|
+
|
|
342
|
+
## Demo app
|
|
343
|
+
|
|
344
|
+
The repository contains a demo application that renders a storybook with all
|
|
345
|
+
components using their default options. For some of the components, CSS was
|
|
346
|
+
added, while others are still unstyled.
|
|
347
|
+
|
|
348
|
+
The demo application is deployed at: https://doggo.wlyx.dev
|
|
349
|
+
|
|
350
|
+
To run the application locally:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
git clone git@github.com:woylie/doggo.git
|
|
354
|
+
cd doggo/demo
|
|
355
|
+
mix setup
|
|
356
|
+
mix phx.server
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The storybook can be accessed at http://localhost:4000.
|
|
360
|
+
|
|
361
|
+
## Status
|
|
362
|
+
|
|
363
|
+
The library is actively developed. Being in its early stages, the library may
|
|
364
|
+
still undergo significant changes, including potential breaking changes.
|
|
365
|
+
|
|
366
|
+
### Maturity Levels
|
|
367
|
+
|
|
368
|
+
Each component is marked with one of four maturity levels.
|
|
369
|
+
|
|
370
|
+
- **Experimental**: In early development. Incomplete, with an unstable API, and
|
|
371
|
+
subject to significant change. Not recommended for production use.
|
|
372
|
+
- **Developing**: Complete semantics, but interactivity may still be missing.
|
|
373
|
+
The API may still change based on feedback and testing. Suitable for internal
|
|
374
|
+
testing and early feedback.
|
|
375
|
+
- **Refining**: Feature-complete, with a stable API, full configurability, and
|
|
376
|
+
all keyboard interactivity required for accessibility. The focus is on finding
|
|
377
|
+
and fixing remaining issues. Suitable for broader testing and cautious
|
|
378
|
+
production use.
|
|
379
|
+
- **Stable**: Fully developed, tested, and ready for production use. A stable
|
|
380
|
+
API, fully interactive, a complete storybook module, and exemplary CSS styles
|
|
381
|
+
defined.
|
|
382
|
+
|
|
383
|
+
### What counts as a breaking change
|
|
384
|
+
|
|
385
|
+
The markup a component renders is part of its API. You write your styles against
|
|
386
|
+
that markup, so a change to it can break your application just as surely
|
|
387
|
+
as a renamed attribute, and it is versioned accordingly.
|
|
388
|
+
|
|
389
|
+
Stability is declared per component, not per release. Which release a
|
|
390
|
+
breaking change can appear in depends on the maturity level of the component it
|
|
391
|
+
affects:
|
|
392
|
+
|
|
393
|
+
- **Experimental** and **Developing** components may change in a **patch**
|
|
394
|
+
release, including in ways that break you.
|
|
395
|
+
- **Refining** and **Stable** components get a **minor** release for a breaking
|
|
396
|
+
change while the library is pre 1.0, with the change named in the changelog.
|
|
397
|
+
|
|
398
|
+
These changes are **not** breaking, and can appear in a patch release for a
|
|
399
|
+
component at any level:
|
|
400
|
+
|
|
401
|
+
- Adding an attribute.
|
|
402
|
+
- Adding a class.
|
|
403
|
+
- Removing an attribute that normally no CSS styles are attached to.
|
|
404
|
+
|
|
405
|
+
These changes **are** breaking:
|
|
406
|
+
|
|
407
|
+
- Removing or renaming an `aria-*` or `data-*` attribute, since you may have
|
|
408
|
+
attached styles to it.
|
|
409
|
+
- Removing or renaming a class.
|
|
410
|
+
- Adding or removing nesting, since it changes which descendant and child
|
|
411
|
+
selectors match.
|
|
412
|
+
- Changing an element type.
|
|
413
|
+
- Reordering elements.
|
|
414
|
+
- Renaming a JavaScript hook.
|
|
415
|
+
|
|
416
|
+
## Feedback
|
|
417
|
+
|
|
418
|
+
If you encounter any issues with a component, have suggestions for improvements,
|
|
419
|
+
or need a component for a specific use case that isn't currently available,
|
|
420
|
+
please don't hesitate to open a
|
|
421
|
+
[Github issue](https://github.com/woylie/doggo/issues).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { targetIndex } from "../navigation.js";
|
|
2
|
+
|
|
3
|
+
export function initAccordion(accordion) {
|
|
4
|
+
// Read on demand, so that headers added by a patch are found.
|
|
5
|
+
const getHeaders = () =>
|
|
6
|
+
Array.from(accordion.querySelectorAll("button[aria-expanded]")).filter(
|
|
7
|
+
(header) => header.closest('[phx-hook="Doggo.Accordion"]') === accordion,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
accordion.addEventListener("keydown", (e) => {
|
|
11
|
+
const headers = getHeaders();
|
|
12
|
+
const currentIdx = headers.indexOf(
|
|
13
|
+
e.target.closest("button[aria-expanded]"),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (currentIdx < 0) return;
|
|
17
|
+
|
|
18
|
+
const nextIdx = targetIndex(e.key, currentIdx, headers.length, {
|
|
19
|
+
orientation: "vertical",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (nextIdx === null) return;
|
|
23
|
+
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
headers[nextIdx].focus();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
mounted() {
|
|
31
|
+
initAccordion(this.el);
|
|
32
|
+
},
|
|
33
|
+
};
|