Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <script>
- export default {
- props: {
- open: {
- type: Boolean,
- required: false,
- default: false
- },
- },
- data() {
- return {
- identifier: null,
- level: null,
- title: null,
- content: [],
- expanded: this.open
- }
- },
- watch: {
- expanded: function (value) {
- // Update the hash if the collapsible section's
- // heading has an `id` and we are opening, not closing
- if (this.identifier && value) {
- history.pushState(null, null, '#' + this.identifier);
- }
- this.$emit('expanded', value)
- }
- },
- created() {
- let nodes = this.$slots.default
- // The first element MUST a heading with appropriate level, otherwise warn the user.
- if (! this.checkHeading(nodes[0])) {
- this.warn()
- }
- // Extract heading level.
- this.level = this.getHeadingLevel(nodes[0])
- // Extract title
- this.title = this.getTitle(nodes[0])
- // Extract the heading identifier
- this.identifier = this.getHeadingIdentifier(nodes[0])
- // Get contents except heading
- for (let i = 1; i < nodes.length; i++) {
- if (! this.checkHeading(nodes[i]) && undefined !== nodes[i].tag) {
- this.content.push(nodes[i])
- }
- }
- },
- mounted() {
- // Defines if section MUST expanded and focused
- this.mustLoadExpanded()
- },
- render: function (createElement) {
- // Render template with data
- return createElement('div', {attrs: {role: 'region'}, class: 'toggle-section'}, [
- createElement('h2', {attrs: {id: this.identifier, 'aria-level': this.level}}, [
- createElement('button', {ref: this.identifier + '-button', attrs: {'aria-expanded': this.expanded.toString()}, on: {click: this.toggle}}, [
- this.title,
- createElement('svg', {attrs: {'aria-hidden': 'true', focusable: 'false', viewBox: '0 0 10 10'}}, [
- createElement('rect', {attrs: {height: 8, width: 1, y: 1, x: 4.5}, class: 'vert'}),
- createElement('rect', {attrs: {height: 1, width: 8, y: 4.5, x: 1}}),
- ])
- ])
- ]),
- createElement('div', {class: 'content ' + this.classExpanded}, this.content)
- ]);
- },
- computed: {
- // Defines the class to show or not the content block
- classExpanded: function () {
- return this.expanded ? '' : 'hidden'
- }
- },
- methods: {
- /**
- * @throws Exception
- */
- warn() {
- console.warn('The first element inside each <toggle-section> should be a heading of an appropriate level.')
- },
- /**
- * @param node Vnode
- * @returns Boolean
- */
- checkHeading(node) {
- return /h[1-6]/i.test(node.tag)
- },
- /**
- * @param node Vnode
- * @returns Number
- */
- getHeadingLevel(node) {
- return parseInt(node.tag.substr(1))
- },
- /**
- * @param node Vnode
- * @returns String
- */
- getTitle(node) {
- return node.children[0].text
- },
- /**
- * @param node Vnode
- * @returns String
- */
- getHeadingIdentifier(node) {
- return undefined !== node.data ? node.data.attrs.id : this.kebabCase(this.title)
- },
- /**
- * @param string String
- * @returns String
- */
- kebabCase(string) {
- return string.toLowerCase().replace(/\W+/g, '-').replace(/(^-|-$)/g, '')
- },
- /**
- * Expand or collapse the section
- */
- toggle() {
- this.expanded = !this.expanded
- },
- /**
- * Defines if the section must expanded and focused
- */
- mustLoadExpanded() {
- if (window.location.hash.substr(1) === this.identifier) {
- this.expanded = true
- this.$refs[this.identifier + '-button'].focus()
- }
- }
- }
- }
- </script>
- <style>
- .toggle-section {
- padding: 0.5rem 0;
- }
- h2 button {
- all: inherit;
- box-sizing: border-box;
- display: flex;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- padding: 0.25rem;
- }
- button svg {
- height: 1rem;
- margin-left: 1rem;
- }
- .hidden, [aria-expanded="true"] .vert {
- display: none;
- }
- [aria-expanded] rect {
- fill: currentColor;
- }
- </style>
Add Comment
Please, Sign In to add comment