1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-28 05:15:54 +00:00

The Big Linting Commit.

* Add linting for Vue files.
* Fix a bunch of linting problems in Vue files.
* Update README with notes about configuring editors.
This commit is contained in:
SirStendec 2018-03-30 17:58:56 -04:00
parent 8dbcf434cd
commit f9f5f0affa
23 changed files with 1114 additions and 971 deletions

View file

@ -3,9 +3,13 @@ module.exports = {
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"extends": "eslint:recommended", "extends": [
"parser": "babel-eslint", "eslint:recommended",
"plugin:vue/recommended"
],
"plugins": ["vue"],
"parserOptions": { "parserOptions": {
"parser": "babel-eslint",
"ecmaVersion": 8, "ecmaVersion": 8,
"sourceType": "module" "sourceType": "module"
}, },
@ -62,6 +66,7 @@ module.exports = {
"no-useless-constructor": ["error"], "no-useless-constructor": ["error"],
"no-useless-rename": ["error"], "no-useless-rename": ["error"],
"no-var": ["error"], "no-var": ["error"],
"no-cond-assign": ["warn"],
"object-shorthand": ["warn"], "object-shorthand": ["warn"],
"prefer-arrow-callback": ["warn", {"allowUnboundThis": true}], "prefer-arrow-callback": ["warn", {"allowUnboundThis": true}],
"prefer-const": ["warn", {"ignoreReadBeforeAssign": true}], "prefer-const": ["warn", {"ignoreReadBeforeAssign": true}],
@ -72,7 +77,7 @@ module.exports = {
"yield-star-spacing": ["warn"], "yield-star-spacing": ["warn"],
"indent": [ "indent": [
"error", "warn",
"tab", "tab",
{ {
"SwitchCase": 1 "SwitchCase": 1
@ -89,6 +94,20 @@ module.exports = {
"avoidEscape": true, "avoidEscape": true,
"allowTemplateLiterals": true "allowTemplateLiterals": true
} }
],
"vue/html-indent": [
"warn",
"tab"
],
"vue/max-attributes-per-line": "off",
"vue/require-prop-types": "off",
"vue/require-default-prop": "off",
"vue/html-closing-bracket-newline": [
"error",
{
"singleline": "never",
"multiline": "always"
}
] ]
} }
}; };

View file

@ -1,7 +1,7 @@
FrankerFaceZ FrankerFaceZ
============ ============
Copyright (c) 2017 Dan Salvato LLC Copyright (c) 2018 Dan Salvato LLC
Licensed under the Apache License, Version 2.0. See LICENSE. Licensed under the Apache License, Version 2.0. See LICENSE.
@ -13,17 +13,40 @@ FrankerFaceZ uses node.js to manage development dependencies and to run an HTTP
server for development. To get everything you need: server for development. To get everything you need:
1. Install node.js and npm 1. Install node.js and npm
2. Run ```npm install``` within the FrankerFaceZ directory. 2. Run `npm install` within the FrankerFaceZ directory.
From there, you can use npm to build the extension from source simply by From there, you can use npm to build FrankerFaceZ from source simply by
running ```npm run build```. For development, you can instruct gulp to watch running `npm run build`. For development, you can instruct gulp to watch
the source files for changes and re-build automatically with ```npm start``` the source files for changes and re-build automatically with `npm start`
FrankerFaceZ comes with a local development server that listens on port 8000 FrankerFaceZ comes with a local development server that listens on port 8000
and it serves up local development copies of files, falling back to the CDN and it serves up local development copies of files, falling back to the CDN
when a local copy of a file isn't present. when a local copy of a file isn't present.
To make FrankerFaceZ load from your local development server, you must set To make FrankerFaceZ load from your local development server, you must set
the local storage variable ```ffzDebugMode``` to true. Just run the following the local storage variable `ffzDebugMode` to true. Just run the following
in your console on Twitch: ```localStorage.ffzDebugMode = true;``` in your console on Twitch: `localStorage.ffzDebugMode = true;`
It should be noted that this project is not a browser extension that you
would load in your browser's extensions system. You still need the FrankerFaceZ
extension or user-script for your browser.
Editor Settings
===============
Please make sure that your editor is configured to use tabs rather than spaces
for indentation and that lines are ended with `\n`. It's recommended that you
configure linting support for your editor as well.
If you're using Visual Studio Code, make sure to install the ESLint extension
and add the following to your workspace settings:
```json
{
"eslint.validate": [
"javascript",
"vue"
]
}```

23
package-lock.json generated
View file

@ -3037,6 +3037,15 @@
} }
} }
}, },
"eslint-plugin-vue": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.4.0.tgz",
"integrity": "sha512-UHeE0aTEv9A/9xe8J6X7rDLMbwV6GuQFKAscMyLEv49Y4wK4KwQiifr2X0MsNsVlmccrDUyjI9KO4DuFTkPP9A==",
"dev": true,
"requires": {
"vue-eslint-parser": "2.0.3"
}
},
"eslint-scope": { "eslint-scope": {
"version": "3.7.1", "version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
@ -9358,6 +9367,20 @@
"loose-envify": "1.3.1" "loose-envify": "1.3.1"
} }
}, },
"vue-eslint-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
"integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==",
"dev": true,
"requires": {
"debug": "3.1.0",
"eslint-scope": "3.7.1",
"eslint-visitor-keys": "1.0.0",
"espree": "3.5.4",
"esquery": "1.0.0",
"lodash": "4.17.4"
}
},
"vue-hot-reload-api": { "vue-hot-reload-api": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz",

View file

@ -6,6 +6,7 @@
"main": "script.js", "main": "script.js",
"scripts": { "scripts": {
"start": "webpack-dev-server --config webpack.web.dev.js", "start": "webpack-dev-server --config webpack.web.dev.js",
"eslint": "eslint \"src/**/*.{js,vue}\"",
"build": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'", "build": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'",
"build:babel": "webpack --config webpack.web.babel.js --define process.env.NODE_ENV='production'", "build:babel": "webpack --config webpack.web.babel.js --define process.env.NODE_ENV='production'",
"build:prod": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'", "build:prod": "webpack --config webpack.web.prod.js --define process.env.NODE_ENV='production'",
@ -22,6 +23,7 @@
"copy-webpack-plugin": "^4.5.1", "copy-webpack-plugin": "^4.5.1",
"css-loader": "^0.28.10", "css-loader": "^0.28.10",
"eslint": "^4.18.2", "eslint": "^4.18.2",
"eslint-plugin-vue": "^4.4.0",
"extract-loader": "^1.0.2", "extract-loader": "^1.0.2",
"file-loader": "^1.1.11", "file-loader": "^1.1.11",
"less": "^2.7.3", "less": "^2.7.3",

View file

@ -1,62 +1,84 @@
<template lang="html"> <template lang="html">
<div class="ffz--badge-visibility tw-pd-t-05"> <div class="ffz--badge-visibility tw-pd-t-05">
<div <div
class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1" v-if="source && source !== profile"
v-if="source && source !== profile" class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1"
>
<span class="ffz-i-info" />
{{ t('setting.badge-inheritence', 'These values are being overridden by another profile and may not take effect.') }}
</div>
<div class="tw-mg-b-2 tw-align-right">
<button
class="tw-mg-l-05 tw-button tw-button--hollow tw-tooltip-wrapper"
@click="clear"
:disabled="! has_value"
> >
<span class="tw-button__icon tw-button__icon--left"> <span class="ffz-i-info" />
<figure class="ffz-i-cancel" /> {{ t('setting.badge-inheritence', 'These values are being overridden by another profile and may not take effect.') }}
</span> </div>
<span class="tw-button__text">
{{ t('setting.reset-all', 'Reset All to Default') }}
</span>
</button>
</div>
<section class="ffz--menu-container tw-border-t" v-for="sec in data"> <div class="tw-mg-b-2 tw-align-right">
<header>{{ sec.title }}</header> <button
<ul class="tw-flex tw-flex-wrap tw-align-content-start"> :disabled="! has_value"
<li v-for="i in sort(sec.badges)" class="ffz--badge-info tw-pd-y-1 tw-pd-r-1 tw-flex" :class="{default: badgeDefault(i.id)}"> class="tw-mg-l-05 tw-button tw-button--hollow tw-tooltip-wrapper"
<input @click="clear"
type="checkbox" >
class="tw-checkbox__input" <span class="tw-button__icon tw-button__icon--left">
:checked="badgeChecked(i.id)" <figure class="ffz-i-cancel" />
:id="i.id" </span>
@click="onChange(i.id, $event)" <span class="tw-button__text">
{{ t('setting.reset-all', 'Reset All to Default') }}
</span>
</button>
</div>
<section
v-for="sec in data"
:key="sec.title"
class="ffz--menu-container tw-border-t"
>
<header>{{ sec.title }}</header>
<ul class="tw-flex tw-flex-wrap tw-align-content-start">
<li
v-for="i in sort(sec.badges)"
:key="i.id"
:class="{default: badgeDefault(i.id)}"
class="ffz--badge-info tw-pd-y-1 tw-pd-r-1 tw-flex"
>
<input
:checked="badgeChecked(i.id)"
:id="i.id"
type="checkbox"
class="tw-checkbox__input"
@click="onChange(i.id, $event)"
> >
<label class="tw-checkbox__label tw-flex" :for="i.id"> <label :for="i.id" class="tw-checkbox__label tw-flex">
<div class="preview-image ffz-badge tw-mg-r-1 tw-flex-shrink-0" :style="{backgroundColor: i.color, backgroundImage: i.styleImage }" /> <div
<div> :style="{backgroundColor: i.color, backgroundImage: i.styleImage }"
<h5>{{ i.name }}</h5> class="preview-image ffz-badge tw-mg-r-1 tw-flex-shrink-0"
<section class="tw-mg-t-05" v-if="i.versions && i.versions.length > 1"> />
<span v-for="v in i.versions" data-tooltip-type="html" class="ffz-badge ffz-tooltip" :title="v.name" :style="{backgroundColor: i.color, backgroundImage: v.styleImage}" /> <div>
</section> <h5>{{ i.name }}</h5>
<button <section
class="tw-mg-t-05 tw-button tw-button--hollow tw-tooltip-wrapper" v-if="i.versions && i.versions.length > 1"
@click="reset(i.id)" class="tw-mg-t-05"
> >
<span class="tw-button__text">Reset</span> <span
<span class="tw-tooltip tw-tooltip--down tw-tooltip--align-right"> v-for="v in i.versions"
{{ t('setting.reset', 'Reset to Default') }} :key="v.name"
</span> :title="v.name"
</button> :style="{backgroundColor: i.color, backgroundImage: v.styleImage}"
</div> data-tooltip-type="html"
</label> class="ffz-badge ffz-tooltip"
</li> />
</ul> </section>
</section> <button
</div> class="tw-mg-t-05 tw-button tw-button--hollow tw-tooltip-wrapper"
@click="reset(i.id)"
>
<span class="tw-button__text">Reset</span>
<span class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.reset', 'Reset to Default') }}
</span>
</button>
</div>
</label>
</li>
</ul>
</section>
</div>
</template> </template>
<script> <script>

View file

@ -1,12 +1,11 @@
<template lang="html"> <template lang="html">
<div class="ffz--changelog tw-border-t tw-pd-t-1"> <div class="ffz--changelog tw-border-t tw-pd-t-1">
<div class="tw-align-center"> <div class="tw-align-center">
<h2>{{ t('home.changelog', 'Changelog') }}</h2> <h2>{{ t('home.changelog', 'Changelog') }}</h2>
</div>
<div ref="changes" />
</div> </div>
<div ref="changes" />
</div>
</template> </template>
@ -17,6 +16,10 @@ import {SERVER} from 'utilities/constants';
export default { export default {
props: ['item', 'context'], props: ['item', 'context'],
mounted() {
this.fetch(`${SERVER}/script/changelog.html`, this.$refs.changes);
},
methods: { methods: {
fetch(url, container) { fetch(url, container) {
const done = data => { const done = data => {
@ -38,13 +41,8 @@ export default {
fetch(url) fetch(url)
.then(resp => resp.ok ? resp.text() : null) .then(resp => resp.ok ? resp.text() : null)
.then(done) .then(done)
.catch(err => done(null)); .catch(() => done(null));
} }
},
mounted() {
this.fetch(`${SERVER}/script/changelog.html`, this.$refs.changes);
} }
} }
</script> </script>

View file

@ -1,35 +1,32 @@
<template lang="html"> <template lang="html">
<div class="ffz--home tw-border-t tw-pd-t-1"> <div class="ffz--home tw-border-t tw-pd-t-1">
<h2>Feedback</h2> <h2>Feedback</h2>
<div class="tw-mg-y-1 tw-c-background-accent tw-c-text-overlay tw-pd-1"> <div class="tw-mg-y-1 tw-c-background-accent tw-c-text-overlay tw-pd-1">
<h3 class="ffz-i-attention"> <h3 class="ffz-i-attention">
Please keep in mind that FrankerFaceZ v4 is under heavy development. Please keep in mind that FrankerFaceZ v4 is under heavy development.
</h3> </h3>
</div> </div>
<p> <p>
Okay, still here? Great! You can provide feedback and bug reports by Okay, still here? Great! You can provide feedback and bug reports by
<a href="https://github.com/FrankerFaceZ/FrankerFaceZ/issues" target="_blank" rel="noopener"> <a href="https://github.com/FrankerFaceZ/FrankerFaceZ/issues" target="_blank" rel="noopener">
opening an issue at our GitHub repository</a>. opening an issue at our GitHub repository</a>.
You can also <a href="https://twitter.com/FrankerFaceZ" target="_blank" rel="noopener"> You can also <a href="https://twitter.com/FrankerFaceZ" target="_blank" rel="noopener">
tweet at us</a>. tweet at us</a>.
</p> </p>
<p> <p>
When creating a GitHub issue, please check that someone else hasn't When creating a GitHub issue, please check that someone else hasn't
already created one for what you'd like to discuss or report. already created one for what you'd like to discuss or report.
</p> </p>
</div>
</div>
</template> </template>
<script> <script>
export default { export default {
props: ['item', 'context'], props: ['item', 'context'],
} }
</script> </script>

View file

@ -1,55 +1,55 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--filter-editor"> <div class="ffz--widget ffz--filter-editor">
<div ref="list" class="ffz--rule-list"> <div ref="list" class="ffz--rule-list">
<section v-for="(rule, idx) in rules"> <section v-for="(rule, idx) in rules">
<div <div
class="ffz--rule tw-elevation-1 tw-c-background tw-border tw-mg-b-05 tw-pd-y-05 tw-pd-r-1 tw-flex tw-flex-nowrap tw-align-items-start" class="ffz--rule tw-elevation-1 tw-c-background tw-border tw-mg-b-05 tw-pd-y-05 tw-pd-r-1 tw-flex tw-flex-nowrap tw-align-items-start"
tabindex="0" tabindex="0"
> >
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-handle tw-pd-x-05 tw-pd-y-1"> <div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-handle tw-pd-x-05 tw-pd-y-1">
<span class="ffz-i-ellipsis-vert" /> <span class="ffz-i-ellipsis-vert" />
</div>
<div class="tw-flex-shrink-0 tw-pd-y-05">
Channel
</div>
<div class="tw-mg-x-1 tw-flex tw-flex-grow-1">
<div class="tw-flex-shrink-0 tw-mg-r-1">
<select class="tw-select">
<option>is one of</option>
<option>is not one of</option>
</select>
</div> </div>
<div class="tw-flex-grow-1">
<input <div class="tw-flex-shrink-0 tw-pd-y-05">
type="text" Channel
class="tw-input" </div>
value="SirStendec"
/> <div class="tw-mg-x-1 tw-flex tw-flex-grow-1">
<div class="tw-flex-shrink-0 tw-mg-r-1">
<select class="tw-select">
<option>is one of</option>
<option>is not one of</option>
</select>
</div>
<div class="tw-flex-grow-1">
<input
type="text"
class="tw-input"
value="SirStendec"
>
</div>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center">
<button class="tw-button tw-button--text" @click="del(idx)">
<span class="tw-button__text ffz-i-trash">
{{ t('setting.filters.delete', 'Delete') }}
</span>
</button>
</div> </div>
</div> </div>
</section>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center"> <button
<button class="tw-button tw-button--text" @click="del(idx)"> class="tw-button tw-button--hollow tw-mg-y-1 tw-full-width"
<span class="tw-button__text ffz-i-trash"> @click="newRule"
{{ t('setting.filters.delete', 'Delete') }} >
</span> <span class="tw-button__text ffz-i-plus">
</button> {{ t('', 'Add New Rule') }}
</div> </span>
</div> </button>
</section>
</div> </div>
<button
class="tw-button tw-button--hollow tw-mg-y-1 tw-full-width"
@click="newRule"
>
<span class="tw-button__text ffz-i-plus">
{{ t('', 'Add New Rule') }}
</span>
</button>
</div>
</template> </template>
<script> <script>

View file

@ -1,72 +1,72 @@
<template lang="html"> <template lang="html">
<div class="ffz--home tw-flex tw-flex-nowrap"> <div class="ffz--home tw-flex tw-flex-nowrap">
<div class="tw-flex-grow-1"> <div class="tw-flex-grow-1">
<div class="tw-align-center"> <div class="tw-align-center">
<h1 class="ffz-i-zreknarf ffz-i-pd-1">FrankerFaceZ</h1> <h1 class="ffz-i-zreknarf ffz-i-pd-1">FrankerFaceZ</h1>
<span class="tw-c-text-alt"> <span class="tw-c-text-alt">
{{ t('home.tag-line', 'The Twitch Enhancement Suite') }} {{ t('home.tag-line', 'The Twitch Enhancement Suite') }}
</span> </span>
</div>
<section class="tw-pd-t-1 tw-border-t tw-mg-t-1">
<h2>Welcome to the v4.0 Beta</h2>
<p>
This is the initial, beta release of FrankerFaceZ v4.0 with support
for the Twitch website rewrite.
As you'll notice, this release is <strong>not</strong> complete.
There are missing features. There are bugs. If you are a moderator,
you will want to just keep opening a Legacy Chat Popout for now.
</p>
<p>
FrankerFaceZ v4.0 is still under heavy development and there will
be significant changes and improvements in the coming weeks. For
now, here are some of the bigger issues:
</p>
<ul class="tw-mg-b-2">
<li>Settings from the old version are not being imported.</li>
<li>Settings cannot be searched.</li>
<li>Emoji aren't displayed.</li>
<li>The emote menu isn't finished.</li>
<li>Tab-completion and advanced input isn't available.</li>
</ul>
<p>And the biggest features still under development:</p>
<ul class="tw-mg-b-2">
<li>Emoji Rendering</li>
<li>Emotes Menu</li>
<li>Chat Filtering (Highlighted Words, etc.)</li>
<li>Room Status Indicators</li>
<li>Custom Mod Cards</li>
<li>Custom Mod Actions</li>
<li>Recent Highlights</li>
<li>More Channel Metadata</li>
<li>Disable Hosting</li>
<li>Portrait Mode</li>
<li>Importing and exporting settings</li>
<li>User Aliases</li>
<li>Rich Content in Chat (aka Clip Embeds)</li>
</ul>
<p>
For a possibly more up-to-date list of what I'm working on,
please consult <a href="https://trello.com/b/LGcYPFwi/frankerfacez-v4" target="_blank">this Trello board</a>.
</p>
</section>
</div>
<div class="tw-mg-l-1 tw-flex-shrink-0 tweet-column">
<a class="twitter-timeline" data-width="300" data-theme="dark" href="https://twitter.com/FrankerFaceZ?ref_src=twsrc%5Etfw">
Tweets by FrankerFaceZ
</a>
</div> </div>
<section class="tw-pd-t-1 tw-border-t tw-mg-t-1">
<h2>Welcome to the v4.0 Beta</h2>
<p>
This is the initial, beta release of FrankerFaceZ v4.0 with support
for the Twitch website rewrite.
As you'll notice, this release is <strong>not</strong> complete.
There are missing features. There are bugs. If you are a moderator,
you will want to just keep opening a Legacy Chat Popout for now.
</p>
<p>
FrankerFaceZ v4.0 is still under heavy development and there will
be significant changes and improvements in the coming weeks. For
now, here are some of the bigger issues:
</p>
<ul class="tw-mg-b-2">
<li>Settings from the old version are not being imported.</li>
<li>Settings cannot be searched.</li>
<li>Emoji aren't displayed.</li>
<li>The emote menu isn't finished.</li>
<li>Tab-completion and advanced input isn't available.</li>
</ul>
<p>And the biggest features still under development:</p>
<ul class="tw-mg-b-2">
<li>Emoji Rendering</li>
<li>Emotes Menu</li>
<li>Chat Filtering (Highlighted Words, etc.)</li>
<li>Room Status Indicators</li>
<li>Custom Mod Cards</li>
<li>Custom Mod Actions</li>
<li>Recent Highlights</li>
<li>More Channel Metadata</li>
<li>Disable Hosting</li>
<li>Portrait Mode</li>
<li>Importing and exporting settings</li>
<li>User Aliases</li>
<li>Rich Content in Chat (aka Clip Embeds)</li>
</ul>
<p>
For a possibly more up-to-date list of what I'm working on,
please consult <a href="https://trello.com/b/LGcYPFwi/frankerfacez-v4" target="_blank">this Trello board</a>.
</p>
</section>
</div> </div>
<div class="tw-mg-l-1 tw-flex-shrink-0 tweet-column">
<a class="twitter-timeline" data-width="300" data-theme="dark" href="https://twitter.com/FrankerFaceZ?ref_src=twsrc%5Etfw">
Tweets by FrankerFaceZ
</a>
</div>
</div>
</template> </template>

View file

@ -1,78 +1,81 @@
<template lang="html"> <template lang="html">
<div class="ffz-main-menu tw-elevation-3 tw-c-background-alt tw-c-text tw-border tw-flex tw-flex-nowrap tw-flex-column" :class="{ maximized: maximized || exclusive, exclusive }"> <div
<header class="tw-c-background tw-full-width tw-align-items-center tw-flex tw-flex-nowrap" @dblclick="resize"> :class="{ maximized: maximized || exclusive, exclusive }"
<h3 class="ffz-i-zreknarf ffz-i-pd-1">FrankerFaceZ</h3> class="ffz-main-menu tw-elevation-3 tw-c-background-alt tw-c-text tw-border tw-flex tw-flex-nowrap tw-flex-column"
<div class="tw-flex-grow-1 tw-pd-x-2"> >
<!--div class="tw-search-input"> <header class="tw-c-background tw-full-width tw-align-items-center tw-flex tw-flex-nowrap" @dblclick="resize">
<label for="ffz-main-menu.search" class="hide-accessible">{{ t('main-menu.search', 'Search Settings') }}</label> <h3 class="ffz-i-zreknarf ffz-i-pd-1">FrankerFaceZ</h3>
<div class="relative"> <div class="tw-flex-grow-1 tw-pd-x-2">
<div class="tw-input__icon-group"> <!--div class="tw-search-input">
<div class="tw-input__icon"> <label for="ffz-main-menu.search" class="hide-accessible">{{ t('main-menu.search', 'Search Settings') }}</label>
<figure class="ffz-i-search" /> <div class="relative">
<div class="tw-input__icon-group">
<div class="tw-input__icon">
<figure class="ffz-i-search" />
</div>
</div> </div>
<input type="search" class="tw-input tw-input--icon-left" :placeholder="t('main-menu.search', 'Search Settings')" autocapitalize="off" autocorrect="off" autocomplete="off" id="ffz-main-menu.search">
</div> </div>
<input type="search" class="tw-input tw-input--icon-left" :placeholder="t('main-menu.search', 'Search Settings')" autocapitalize="off" autocorrect="off" autocomplete="off" id="ffz-main-menu.search"> </div-->
</div>
</div-->
</div>
<button class="tw-button-icon tw-mg-x-05" @click="resize" v-if="!exclusive">
<span class="tw-button-icon__icon">
<figure :class="{'ffz-i-window-maximize': !maximized, 'ffz-i-window-restore': maximized}" />
</span>
</button>
<button class="tw-button-icon tw-mg-x-05" @click="close" v-if="!exclusive">
<span class="tw-button-icon__icon">
<figure class="ffz-i-window-close" />
</span>
</button>
</header>
<section class="tw-border-t tw-full-height tw-full-width tw-flex tw-flex-nowrap tw-overflow-hidden">
<nav class="ffz-vertical-nav tw-c-background-alt-2 tw-border-r tw-full-height tw-flex tw-flex-column tw-flex-shrink-0 tw-flex-nowrap">
<header class="tw-border-b tw-pd-1">
<profile-selector
:context="context"
@navigate="navigate"
/>
</header>
<div class="tw-full-width tw-full-height tw-overflow-hidden tw-flex tw-flex-nowrap tw-relative">
<div class="ffz-vertical-nav__items tw-full-width tw-flex-grow-1 scrollable-area" data-simplebar>
<div class="simplebar-scroll-content">
<div class="simplebar-content">
<menu-tree
:currentItem="currentItem"
:modal="nav"
@change-item="changeItem"
@navigate="navigate"
/>
</div>
</div>
</div>
</div> </div>
<footer class="tw-c-text-alt tw-border-t tw-pd-1"> <button v-if="!exclusive" class="tw-button-icon tw-mg-x-05" @click="resize">
<div> <span class="tw-button-icon__icon">
{{ t('main-menu.version', 'Version %{version}', {version: version.toString()}) }} <figure :class="{'ffz-i-window-maximize': !maximized, 'ffz-i-window-restore': maximized}" />
</div> </span>
<div class="tw-c-text-alt-2"> </button>
{{version.build}} <button v-if="!exclusive" class="tw-button-icon tw-mg-x-05" @click="close">
</div> <span class="tw-button-icon__icon">
</footer> <figure class="ffz-i-window-close" />
</nav> </span>
<main class="tw-flex-grow-1 scrollable-area" data-simplebar> </button>
<div class="simplebar-scroll-content"> </header>
<div class="simplebar-content"> <section class="tw-border-t tw-full-height tw-full-width tw-flex tw-flex-nowrap tw-overflow-hidden">
<menu-page <nav class="ffz-vertical-nav tw-c-background-alt-2 tw-border-r tw-full-height tw-flex tw-flex-column tw-flex-shrink-0 tw-flex-nowrap">
ref="page" <header class="tw-border-b tw-pd-1">
<profile-selector
:context="context" :context="context"
:item="currentItem"
@change-item="changeItem"
@navigate="navigate" @navigate="navigate"
v-if="currentItem"
/> />
</header>
<div class="tw-full-width tw-full-height tw-overflow-hidden tw-flex tw-flex-nowrap tw-relative">
<div class="ffz-vertical-nav__items tw-full-width tw-flex-grow-1 scrollable-area" data-simplebar>
<div class="simplebar-scroll-content">
<div class="simplebar-content">
<menu-tree
:current-item="currentItem"
:modal="nav"
@change-item="changeItem"
@navigate="navigate"
/>
</div>
</div>
</div>
</div> </div>
</div> <footer class="tw-c-text-alt tw-border-t tw-pd-1">
</main> <div>
</section> {{ t('main-menu.version', 'Version %{version}', {version: version.toString()}) }}
</div> </div>
<div class="tw-c-text-alt-2">
{{ version.build }}
</div>
</footer>
</nav>
<main class="tw-flex-grow-1 scrollable-area" data-simplebar>
<div class="simplebar-scroll-content">
<div class="simplebar-content">
<menu-page
v-if="currentItem"
ref="page"
:context="context"
:item="currentItem"
@change-item="changeItem"
@navigate="navigate"
/>
</div>
</div>
</main>
</section>
</div>
</template> </template>
<script> <script>
@ -84,6 +87,12 @@ export default {
return this.$vnode.data; return this.$vnode.data;
}, },
watch: {
maximized() {
this.updateDrag();
}
},
created() { created() {
this.context.context._add_user(); this.context.context._add_user();
}, },
@ -92,6 +101,22 @@ export default {
this.context.context._remove_user(); this.context.context._remove_user();
}, },
mounted() {
this.updateDrag();
this._on_resize = this.handleResize.bind(this);
window.addEventListener('resize', this._on_resize);
},
beforeDestroy() {
this.destroyDrag();
if ( this._on_resize ) {
window.removeEventListener('resize', this._on_resize);
this._on_resize = null;
}
},
methods: { methods: {
changeProfile() { changeProfile() {
const new_id = this.$refs.profiles.value, const new_id = this.$refs.profiles.value,
@ -153,28 +178,6 @@ export default {
this.changeItem(item); this.changeItem(item);
} }
},
watch: {
maximized() {
this.updateDrag();
}
},
mounted() {
this.updateDrag();
this._on_resize = this.handleResize.bind(this);
window.addEventListener('resize', this._on_resize);
},
beforeDestroy() {
this.destroyDrag();
if ( this._on_resize ) {
window.removeEventListener('resize', this._on_resize);
this._on_resize = null;
}
} }
} }
</script> </script>

View file

@ -1,21 +1,21 @@
<template lang="html"> <template lang="html">
<div v-bind:class="classes" v-if="item.contents"> <div v-if="item.contents" :class="classes">
<header v-if="! item.no_header"> <header v-if="! item.no_header">
{{ t(item.i18n_key, item.title, item) }} {{ t(item.i18n_key, item.title, item) }}
</header> </header>
<section <section
v-if="item.description" v-if="item.description"
v-html="t(item.desc_i18n_key, item.description, item)" class="tw-pd-b-1"
class="tw-pd-b-1" v-html="t(item.desc_i18n_key, item.description, item)"
/> />
<component <component
v-for="i in item.contents" v-for="i in item.contents"
v-bind:is="i.component" :is="i.component"
:context="context" :context="context"
:item="i" :item="i"
:key="i.full_key" :key="i.full_key"
/> />
</div> </div>
</template> </template>
<script> <script>

View file

@ -1,51 +1,49 @@
<template lang="html"> <template lang="html">
<div class="ffz--menu-page"> <div class="ffz--menu-page">
<header class="tw-mg-b-1"> <header class="tw-mg-b-1">
<template v-for="i in breadcrumbs"> <span v-for="i in breadcrumbs" :key="i.full_key">
<a v-if="i !== item" href="#" @click="$emit('change-item', i, false)">{{ t(i.i18n_key, i.title, i) }}</a> <a v-if="i !== item" href="#" @click="$emit('change-item', i, false)">{{ t(i.i18n_key, i.title, i) }}</a>
<strong v-if="i === item">{{ t(i.i18n_key, i.title, i) }}</strong> <strong v-if="i === item">{{ t(i.i18n_key, i.title, i) }}</strong>
<template v-if="i !== item">&raquo; </template> <template v-if="i !== item">&raquo; </template>
</template> </span>
</header> </header>
<section v-if="! context.currentProfile.live && item.profile_warning !== false" class="tw-border-t tw-pd-t-1 tw-pd-b-2"> <section v-if="! context.currentProfile.live && item.profile_warning !== false" class="tw-border-t tw-pd-t-1 tw-pd-b-2">
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1"> <div class="tw-c-background-accent tw-c-text-overlay tw-pd-1">
<h3 class="ffz-i-attention"> <h3 class="ffz-i-attention">
{{ t('setting.profiles.inactive', "This profile isn't active.") }} {{ t('setting.profiles.inactive', "This profile isn't active.") }}
</h3> </h3>
{{ t( {{ t('setting.profiles.inactive.description',
'setting.profiles.inactive.description', "This profile's rules don't match the current context and it therefore isn't currently active, so you " +
"This profile's rules don't match the current context and it therefore isn't currently active, so you " + "won't see changes you make here reflected on Twitch.")
"won't see changes you make here reflected on Twitch." }}
) }} </div>
</section>
<section
v-if="item.description"
class="tw-border-t tw-pd-y-1"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/>
<template v-if="! item.contents || ! item.contents.length">
<ul class="tw-border-t tw-pd-y-1">
<li v-for="i in item.items" :key="i.full_key" class="tw-pd-x-1">
<a href="#" @click="$emit('change-item', i, false)">
{{ t(i.i18n_key, i.title, i) }}
</a>
</li>
</ul>
</template>
<component
v-for="i in item.contents"
ref="children"
:is="i.component"
:context="context"
:item="i"
:key="i.full_key"
@change-item="changeItem"
@navigate="navigate"
/>
</div> </div>
</section>
<section
v-if="item.description"
class="tw-border-t tw-pd-y-1"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/>
</section>
<template v-if="! item.contents || ! item.contents.length">
<ul class="tw-border-t tw-pd-y-1">
<li class="tw-pd-x-1" v-for="i in item.items">
<a href="#" @click="$emit('change-item', i, false)">
{{ t(i.i18n_key, i.title, i) }}
</a>
</li>
</ul>
</template>
<component
v-for="i in item.contents"
v-bind:is="i.component"
ref="children"
:context="context"
:item="i"
:key="i.full_key"
@change-item="changeItem"
@navigate="navigate"
/>
</div>
</template> </template>
<script> <script>

View file

@ -1,53 +1,52 @@
<template lang="html"> <template lang="html">
<ul <ul
v-if="modal" v-if="modal"
class="ffz--menu-tree" :role="[root ? 'group' : 'tree']"
:role="[root ? 'group' : 'tree']" :tabindex="tabIndex"
:tabindex="tabIndex" class="ffz--menu-tree"
@keyup.up="prevItem" @keyup.up="prevItem"
@keyup.down="nextItem" @keyup.down="nextItem"
@keyup.left="prevLevel" @keyup.left="prevLevel"
@keyup.right="nextLevel" @keyup.right="nextLevel"
@keyup.*="expandAll" @keyup.*="expandAll"
>
<li
v-for="item in modal"
:key="item.full_key"
:class="[currentItem === item ? 'active' : '']"
role="presentation"
> >
<div <li
class="tw-flex__item tw-flex tw-flex-nowrap tw-align-items-center tw-pd-y-05 tw-pd-r-05" v-for="item in modal"
:key="item.full_key"
role="treeitem" :class="[currentItem === item ? 'active' : '']"
:aria-expanded="item.expanded" role="presentation"
:aria-selected="currentItem === item"
@click="clickItem(item)"
> >
<span <div
role="presentation" :aria-expanded="item.expanded"
class="arrow" :aria-selected="currentItem === item"
:class="[ class="tw-flex__item tw-flex tw-flex-nowrap tw-align-items-center tw-pd-y-05 tw-pd-r-05"
item.items ? '' : 'ffz--invisible', role="treeitem"
item.expanded ? 'ffz-i-down-dir' : 'ffz-i-right-dir' @click="clickItem(item)"
]" >
<span
:class="[
item.items ? '' : 'ffz--invisible',
item.expanded ? 'ffz-i-down-dir' : 'ffz-i-right-dir'
]"
role="presentation"
class="arrow"
/>
<span class="tw-flex-grow-1">
{{ t(item.i18n_key, item.title, item) }}
</span>
<span v-if="item.pill" class="pill">
{{ item.pill_i18n_key ? t(item.pill_i18n_key, item.pill, item) : item.pill }}
</span>
</div>
<menu-tree
v-if="item.items && item.expanded"
:root="item"
:current-item="currentItem"
:modal="item.items"
@change-item="i => $emit('change-item', i)"
/> />
<span class="tw-flex-grow-1"> </li>
{{ t(item.i18n_key, item.title, item) }} </ul>
</span>
<span v-if="item.pill" class="pill">
{{ item.pill_i18n_key ? t(item.pill_i18n_key, item.pill, item) : item.pill }}
</span>
</div>
<menu-tree
:root="item"
:currentItem="currentItem"
:modal="item.items"
v-if="item.items && item.expanded"
@change-item="i => $emit('change-item', i)"
/>
</li>
</ul>
</template> </template>
<script> <script>
@ -120,7 +119,7 @@ export default {
this.$emit('change-item', i.parent); this.$emit('change-item', i.parent);
}, },
nextItem(e) { nextItem() {
if ( this.root ) return; if ( this.root ) return;
const i = this.currentItem; const i = this.currentItem;

View file

@ -1,83 +1,81 @@
<template lang="html"> <template lang="html">
<div class="ffz--profile-editor"> <div class="ffz--profile-editor">
<div class="tw-flex tw-align-items-center tw-border-t tw-pd-1"> <div class="tw-flex tw-align-items-center tw-border-t tw-pd-1">
<div class="tw-flex-grow-1"></div> <div class="tw-flex-grow-1" />
<button <button
class="tw-button tw-button--text" class="tw-button tw-button--text"
@click="save" @click="save"
> >
<span class="tw-button__text ffz-i-floppy"> <span class="tw-button__text ffz-i-floppy">
{{ t('settings.profiles.save', 'Save') }} {{ t('settings.profiles.save', 'Save') }}
</span> </span>
</button> </button>
<button <button
class="tw-mg-l-1 tw-button tw-button--text" :disabled="item.profile && context.profiles.length < 2"
:disabled="item.profile && context.profiles.length < 2" class="tw-mg-l-1 tw-button tw-button--text"
@click="del" @click="del"
> >
<span class="tw-button__text ffz-i-trash"> <span class="tw-button__text ffz-i-trash">
{{ t('setting.profiles.delete', 'Delete') }} {{ t('setting.profiles.delete', 'Delete') }}
</span> </span>
</button> </button>
<!--button class="tw-mg-l-1 tw-button tw-button--text"> <!--button class="tw-mg-l-1 tw-button tw-button--text">
<span class="tw-button__text ffz-i-download"> <span class="tw-button__text ffz-i-download">
{{ t('setting.profiles.export', 'Export') }} {{ t('setting.profiles.export', 'Export') }}
</span> </span>
</button--> </button-->
</div>
<div class="ffz--menu-container tw-border-t">
<header>
{{ t('settings.data_management.profiles.edit.general', 'General') }}
</header>
<div class="ffz--widget tw-flex tw-flex-nowrap">
<label for="ffz:editor:name">
{{ t('settings.data_management.profiles.edit.name', 'Name') }}
</label>
<input
class="tw-input"
ref="name"
id="ffz:editor:name"
v-model="name"
/>
</div> </div>
<div class="ffz--widget tw-flex tw-flex-nowrap"> <div class="ffz--menu-container tw-border-t">
<label for="ffz:editor:description"> <header>
{{ t('settings.data_management.profiles.edit.desc', 'Description') }} {{ t('settings.data_management.profiles.edit.general', 'General') }}
</label> </header>
<textarea <div class="ffz--widget tw-flex tw-flex-nowrap">
class="tw-input" <label for="ffz:editor:name">
ref="desc" {{ t('settings.data_management.profiles.edit.name', 'Name') }}
id="ffz:editor:description" </label>
v-model="desc"
<input
id="ffz:editor:name"
ref="name"
v-model="name"
class="tw-input"
>
</div>
<div class="ffz--widget tw-flex tw-flex-nowrap">
<label for="ffz:editor:description">
{{ t('settings.data_management.profiles.edit.desc', 'Description') }}
</label>
<textarea
id="ffz:editor:description"
ref="desc"
v-model="desc"
class="tw-input"
/> />
</div>
</div> </div>
</div>
<div class="ffz--menu-container tw-border-t"> <div class="ffz--menu-container tw-border-t">
<header> <header>
{{ t('settings.data_management.profiles.edit.rules', 'Rules') }} {{ t('settings.data_management.profiles.edit.rules', 'Rules') }}
</header> </header>
<section class="tw-pd-b-1"> <section class="tw-pd-b-1">
{{ t( {{ t('settings.data_management.profiles.edit.rules.description',
'settings.data_management.profiles.edit.rules.description', 'Rules allows you to define a series of conditions under which this profile will be active.')
'Rules allows you to define a series of conditions under which this profile will be active.' }}
) }} </section>
</section>
<filter-editor <filter-editor
:filters="filters" :filters="filters"
:rules="rules" :rules="rules"
:context="test_context" :context="test_context"
@change="unsaved = true" @change="unsaved = true"
/> />
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -100,17 +98,6 @@ export default {
} }
}, },
created() {
this.context.context.on('context_changed', this.updateContext, this);
this.updateContext();
this.revert();
},
beforeDestroy() {
this.context.context.off('context_changed', this.updateContext, this);
},
watch: { watch: {
name() { name() {
if ( this.name !== this.old_name ) if ( this.name !== this.old_name )
@ -123,6 +110,16 @@ export default {
} }
}, },
created() {
this.context.context.on('context_changed', this.updateContext, this);
this.updateContext();
this.revert();
},
beforeDestroy() {
this.context.context.off('context_changed', this.updateContext, this);
},
methods: { methods: {
revert() { revert() {
const profile = this.item.profile; const profile = this.item.profile;
@ -131,7 +128,7 @@ export default {
profile.i18n_key ? profile.i18n_key ?
this.t(profile.i18n_key, profile.title, profile) : this.t(profile.i18n_key, profile.title, profile) :
profile.title : profile.title :
'Unnamed Profile', 'Unnamed Profile';
this.old_desc = this.desc = profile ? this.old_desc = this.desc = profile ?
profile.desc_i18n_key ? profile.desc_i18n_key ?

View file

@ -1,74 +1,74 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--profile-manager tw-border-t tw-pd-y-1"> <div class="ffz--widget ffz--profile-manager tw-border-t tw-pd-y-1">
<div class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1"> <div class="tw-c-background-accent tw-c-text-overlay tw-pd-1 tw-mg-b-1">
<h3 class="ffz-i-attention"> <h3 class="ffz-i-attention">
This feature is not yet finished. This feature is not yet finished.
</h3> </h3>
Creating and editing profiles is disabled until the rule editor is finished. Creating and editing profiles is disabled until the rule editor is finished.
</div>
<div class="tw-flex tw-align-items-center tw-pd-b-05">
<div class="tw-flex-grow-1">
{{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }}
</div> </div>
<button class="tw-mg-l-1 tw-button tw-button--text" disabled @notclick="edit()"> <div class="tw-flex tw-align-items-center tw-pd-b-05">
<span class="tw-button__text ffz-i-plus"> <div class="tw-flex-grow-1">
{{ t('setting.profiles.new', 'New Profile') }} {{ t('setting.profiles.drag', 'Drag profiles to change their priority.') }}
</span>
</button>
<!--button class="tw-mg-l-1 tw-button tw-button--text">
<span class="tw-button__text ffz-i-upload">
{{ t('setting.profiles.import', 'Import…') }}
</span>
</button-->
</div>
<div ref="list" class="ffz--profile-list">
<section
v-for="p in context.profiles"
:key="p.id"
:data-profile="p.id"
>
<div
class="ffz--profile tw-elevation-1 tw-c-background tw-border tw-pd-y-05 tw-pd-r-1 tw-mg-y-05 tw-flex tw-flex-nowrap"
:class="{live: p.live}"
tabindex="0"
>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-handle tw-pd-x-05 tw-pd-t-1 tw-pd-b-05">
<span class="ffz-i-ellipsis-vert" />
</div>
<div class="tw-flex-grow-1">
<h4>{{ t(p.i18n_key, p.title, p) }}</h4>
<div v-if="p.description" class="description">
{{ t(p.desc_i18n_key, p.description, p) }}
</div>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center">
<button class="tw-button tw-button--text" disabled @notclick="edit(p)">
<span class="tw-button__text ffz-i-cog">
{{ t('setting.profiles.configure', 'Configure') }}
</span>
</button>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-border-l tw-mg-l-1 tw-pd-l-1">
<div v-if="p.live" class="ffz--profile__icon ffz-i-ok tw-tooltip-wrapper">
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.profiles.active', 'This profile is active.') }}
</div>
</div>
<div v-if="! p.live" class="ffz--profile__icon ffz-i-cancel tw-tooltip-wrapper">
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.profiles.inactive', 'This profile is not active.') }}
</div>
</div>
</div>
</div> </div>
</section> <button class="tw-mg-l-1 tw-button tw-button--text" disabled @notclick="edit()">
<span class="tw-button__text ffz-i-plus">
{{ t('setting.profiles.new', 'New Profile') }}
</span>
</button>
<!--button class="tw-mg-l-1 tw-button tw-button--text">
<span class="tw-button__text ffz-i-upload">
{{ t('setting.profiles.import', 'Import…') }}
</span>
</button-->
</div>
<div ref="list" class="ffz--profile-list">
<section
v-for="p in context.profiles"
:key="p.id"
:data-profile="p.id"
>
<div
:class="{live: p.live}"
class="ffz--profile tw-elevation-1 tw-c-background tw-border tw-pd-y-05 tw-pd-r-1 tw-mg-y-05 tw-flex tw-flex-nowrap"
tabindex="0"
>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center handle tw-pd-x-05 tw-pd-t-1 tw-pd-b-05">
<span class="ffz-i-ellipsis-vert" />
</div>
<div class="tw-flex-grow-1">
<h4>{{ t(p.i18n_key, p.title, p) }}</h4>
<div v-if="p.description" class="description">
{{ t(p.desc_i18n_key, p.description, p) }}
</div>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center">
<button class="tw-button tw-button--text" disabled @notclick="edit(p)">
<span class="tw-button__text ffz-i-cog">
{{ t('setting.profiles.configure', 'Configure') }}
</span>
</button>
</div>
<div class="tw-flex tw-flex-shrink-0 tw-align-items-center tw-border-l tw-mg-l-1 tw-pd-l-1">
<div v-if="p.live" class="ffz--profile__icon ffz-i-ok tw-tooltip-wrapper">
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.profiles.active', 'This profile is active.') }}
</div>
</div>
<div v-if="! p.live" class="ffz--profile__icon ffz-i-cancel tw-tooltip-wrapper">
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.profiles.inactive', 'This profile is not active.') }}
</div>
</div>
</div>
</div>
</section>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
@ -78,6 +78,28 @@ import Sortable from 'sortablejs';
export default { export default {
props: ['item', 'context'], props: ['item', 'context'],
mounted() {
this._sortable = Sortable.create(this.$refs.list, {
draggable: 'section',
filter: 'button',
onUpdate: event => {
const id = event.item.dataset.profile,
profile = this.context.profile_keys[id];
if ( profile )
profile.move(event.newIndex);
}
});
},
beforeDestroy() {
if ( this._sortable )
this._sortable.destroy();
this._sortable = null;
},
methods: { methods: {
edit(profile) { edit(profile) {
const item = { const item = {
@ -100,30 +122,7 @@ export default {
item.contents[0].parent = item; item.contents[0].parent = item;
this.$emit('change-item', item); this.$emit('change-item', item);
} }
},
mounted() {
this._sortable = Sortable.create(this.$refs.list, {
draggable: 'section',
filter: 'button',
onUpdate: (event) => {
const id = event.item.dataset.profile,
profile = this.context.profile_keys[id];
if ( profile )
profile.move(event.newIndex);
}
});
},
beforeDestroy() {
if ( this._sortable )
this._sortable.destroy();
this._sortable = null;
} }
} }
</script> </script>

View file

@ -1,79 +1,84 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--profile-selector"> <div class="ffz--widget ffz--profile-selector">
<div
tabindex="0"
class="tw-select"
:class="{active: opened}"
ref="button"
@keyup.up.stop.prevent="focusShow"
@keyup.left.stop.prevent="focusShow"
@keyup.down.stop.prevent="focusShow"
@keyup.right.stop.prevent="focusShow"
@keyup.enter="focusShow"
@keyup.space="focusShow"
@click="togglePopup"
>
{{ t(context.currentProfile.i18n_key, context.currentProfile.title, context.currentProfile) }}
</div>
<div v-if="opened" v-on-clickaway="hide" class="tw-balloon tw-block tw-balloon--lg tw-balloon--down tw-balloon--left">
<div <div
class="ffz--profile-list tw-elevation-2 tw-c-background-alt" ref="button"
@keyup.escape="focusHide" :class="{active: opened}"
@focusin="focus" tabindex="0"
@focusout="blur" class="tw-select"
@keyup.up.stop.prevent="focusShow"
@keyup.left.stop.prevent="focusShow"
@keyup.down.stop.prevent="focusShow"
@keyup.right.stop.prevent="focusShow"
@keyup.enter="focusShow"
@keyup.space="focusShow"
@click="togglePopup"
> >
<div class="scrollable-area tw-border-b" data-simplebar> {{ t(context.currentProfile.i18n_key, context.currentProfile.title, context.currentProfile) }}
<div class="simplebar-scroll-content"> </div>
<div class="simplebar-content" ref="popup"> <div
<div v-on-clickaway="hide"
v-for="(p, idx) in context.profiles" v-if="opened"
tabindex="0" class="tw-balloon tw-block tw-balloon--lg tw-balloon--down tw-balloon--left"
class="ffz--profile-row tw-relative tw-border-b tw-pd-y-05 tw-pd-r-3 tw-pd-l-1" >
:class="{ <div
live: p.live, class="ffz--profile-list tw-elevation-2 tw-c-background-alt"
current: p === context.currentProfile @keyup.escape="focusHide"
}" @focusin="focus"
@keydown.up.stop.prevent="" @focusout="blur"
@keydown.down.stop.prevent="" >
@keydown.page-up.stop.prevent="" <div class="scrollable-area tw-border-b" data-simplebar>
@keydown.page-down.stop.prevent="" <div class="simplebar-scroll-content">
@keyup.up.stop="prevItem" <div ref="popup" class="simplebar-content">
@keyup.down.stop="nextItem"
@keyup.home="firstItem"
@keyup.end="lastItem"
@keyup.page-up.stop="prevPage"
@keyup.page-down.stop="nextPage"
@keyup.enter="changeProfile(p)"
@click="changeProfile(p)"
>
<div <div
v-if="p.live" v-for="p in context.profiles"
class="tw-tooltip-wrapper ffz--profile-row__icon ffz-i-ok tw-absolute" :key="p.id"
:class="{
live: p.live,
current: p === context.currentProfile
}"
tabindex="0"
class="ffz--profile-row tw-relative tw-border-b tw-pd-y-05 tw-pd-r-3 tw-pd-l-1"
@keydown.up.stop.prevent=""
@keydown.down.stop.prevent=""
@keydown.page-up.stop.prevent=""
@keydown.page-down.stop.prevent=""
@keyup.up.stop="prevItem"
@keyup.down.stop="nextItem"
@keyup.home="firstItem"
@keyup.end="lastItem"
@keyup.page-up.stop="prevPage"
@keyup.page-down.stop="nextPage"
@keyup.enter="changeProfile(p)"
@click="changeProfile(p)"
> >
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right"> <div
{{ t('setting.profiles.active', 'This profile is active.') }} v-if="p.live"
class="tw-tooltip-wrapper ffz--profile-row__icon ffz-i-ok tw-absolute"
>
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.profiles.active', 'This profile is active.') }}
</div>
</div> </div>
</div>
<h4>{{ t(p.i18n_key, p.title, p) }}</h4> <h4>{{ t(p.i18n_key, p.title, p) }}</h4>
<div v-if="p.description" class="description"> <div v-if="p.description" class="description">
{{ t(p.desc_i18n_key, p.description, p) }} {{ t(p.desc_i18n_key, p.description, p) }}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="tw-pd-y-05 tw-pd-x-05 tw-align-right">
<div class="tw-pd-y-05 tw-pd-x-05 tw-align-right"> <button class="tw-button tw-button--text" @click="openConfigure">
<button class="tw-button tw-button--text" @click="openConfigure"> <span class="tw-button__text ffz-i-cog">
<span class="tw-button__text ffz-i-cog"> {{ t('setting.profiles.configure', 'Configure') }}
{{ t('setting.profiles.configure', 'Configure') }} </span>
</span> </button>
</button> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<script> <script>

View file

@ -1,43 +1,46 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--checkbox" :class="{inherits: isInherited, default: isDefault}"> <div
<div class="tw-flex tw-align-items-center"> :class="{inherits: isInherited, default: isDefault}"
<input class="ffz--widget ffz--checkbox"
type="checkbox" >
class="tw-checkbox__input" <div class="tw-flex tw-align-items-center">
ref="control" <input
:id="item.full_key" ref="control"
:checked="value" :id="item.full_key"
@change="onChange" :checked="value"
type="checkbox"
class="tw-checkbox__input"
@change="onChange"
> >
<label class="tw-checkbox__label" :for="item.full_key"> <label :for="item.full_key" class="tw-checkbox__label">
{{ t(item.i18n_key, item.title, item) }} {{ t(item.i18n_key, item.title, item) }}
</label> </label>
<button <button
v-if="source && source !== profile" v-if="source && source !== profile"
class="tw-mg-l-05 tw-button tw-button--text" class="tw-mg-l-05 tw-button tw-button--text"
@click="context.currentProfile = source" @click="context.currentProfile = source"
> >
<span class="tw-button__text ffz-i-right-dir"> <span class="tw-button__text ffz-i-right-dir">
{{ sourceDisplay }} {{ sourceDisplay }}
</span> </span>
</button> </button>
<button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear"> <button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear">
<span class="tw-button__text ffz-i-cancel"></span> <span class="tw-button__text ffz-i-cancel" />
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right"> <div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.reset', 'Reset to Default') }} {{ t('setting.reset', 'Reset to Default') }}
</div> </div>
</button> </button>
</div> </div>
<section <section
v-if="item.description" v-if="item.description"
class="tw-c-text-alt-2" class="tw-c-text-alt-2"
style="padding-left:2.2rem" style="padding-left:2.2rem"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)" v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/> />
</div> </div>
</template> </template>
<script> <script>

View file

@ -1,31 +1,31 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--hotkey-input"> <div class="ffz--widget ffz--hotkey-input">
<label <label
:for="item.full_key" :for="item.full_key"
v-html="t(item.i18n_key, item.title, item)" v-html="t(item.i18n_key, item.title, item)"
/> />
<div class="tw-relative"> <div class="tw-relative">
<div class="tw-input__icon-group tw-input__icon-group--right"> <div class="tw-input__icon-group tw-input__icon-group--right">
<div class="tw-input__icon"> <div class="tw-input__icon">
<figure class="ffz-i-keyboard" /> <figure class="ffz-i-keyboard" />
</div>
</div>
<div
ref="display"
:id="item.full_key"
type="text"
class="tw-mg-05 tw-input tw-input--icon-right"
tabindex="0"
@keyup="onKey"
>
&nbsp;
</div> </div>
</div> </div>
<div <section
type="text" v-if="item.description"
class="tw-mg-05 tw-input tw-input--icon-right" v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
ref="display"
:id="item.full_key"
tabindex="0"
@keyup="onKey"
>
&nbsp;
</div>
</div>
<section
v-if="item.description"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/> />
</div> </div>
</template> </template>
<script> <script>

View file

@ -1,27 +1,36 @@
<template lang="html"> <template lang="html">
<div class="tw-input"> <div class="tw-input">
<header> <header>
{{ t(item.i18n_key, item.title, item) }} {{ t(item.i18n_key, item.title, item) }}
</header> </header>
<section <section
v-if="item.description" v-if="item.description"
class="tw-c-text-alt-2" class="tw-c-text-alt-2"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)" v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/> />
<div v-for="(i, idx) in data" class="tw-mg-l-1"> <div v-for="(i, idx) in data" :key="idx" class="tw-mg-l-1">
<input type="radio" :name="item.full_key" :id="item.full_key + idx" :value="i.value" class="tw-radio__input"> <input
<label :for="item.full_key + idx" class="tw-pd-y-05 tw-radio__label">{{ t(i.i18n_key, i.title, i) }}</label> :name="item.full_key"
:id="item.full_key + idx"
:value="i.value"
type="radio"
class="tw-radio__input"
>
<label
:for="item.full_key + idx"
class="tw-pd-y-05 tw-radio__label"
>
{{ t(i.i18n_key, i.title, i) }}
</label>
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
import SettingMixin from '../setting-mixin'; import SettingMixin from '../setting-mixin';
export default { export default {
mixins: [SettingMixin], mixins: [SettingMixin],
props: ['item', 'context'] props: ['item', 'context']
} }
</script> </script>

View file

@ -1,49 +1,55 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--select-box" :class="{inherits: isInherited, default: isDefault}"> <div
<div class="tw-flex tw-align-items-center"> :class="{inherits: isInherited, default: isDefault}"
<label :for="item.full_key"> class="ffz--widget ffz--select-box"
{{ t(item.i18n_key, item.title, item) }} >
</label> <div class="tw-flex tw-align-items-center">
<label :for="item.full_key">
{{ t(item.i18n_key, item.title, item) }}
</label>
<select <select
class="tw-mg-05 tw-select tw-display-inline tw-width-auto" ref="control"
ref="control" :id="item.full_key"
:id="item.full_key" class="tw-mg-05 tw-select tw-display-inline tw-width-auto"
@change="onChange" @change="onChange"
> >
<option v-for="i in data" :selected="i.value === value"> <option
{{ i.i18n_key ? t(i.i18n_key, i.title, i) : i.title }} v-for="i in data"
</option> :key="i.value"
</select> :selected="i.value === value"
>
{{ i.i18n_key ? t(i.i18n_key, i.title, i) : i.title }}
</option>
</select>
<button <button
v-if="source && source !== profile" v-if="source && source !== profile"
class="tw-mg-l-05 tw-button tw-button--text" class="tw-mg-l-05 tw-button tw-button--text"
@click="context.currentProfile = source" @click="context.currentProfile = source"
> >
<span class="tw-button__text ffz-i-right-dir"> <span class="tw-button__text ffz-i-right-dir">
{{ sourceDisplay }} {{ sourceDisplay }}
</span> </span>
</button> </button>
<button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear"> <button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear">
<span class="tw-button__text ffz-i-cancel"></span> <span class="tw-button__text ffz-i-cancel" />
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right"> <div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.reset', 'Reset to Default') }} {{ t('setting.reset', 'Reset to Default') }}
</div> </div>
</button> </button>
</div> </div>
<section <section
v-if="item.description" v-if="item.description"
class="tw-c-text-alt-2" class="tw-c-text-alt-2"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)" v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/> />
</div> </div>
</template> </template>
<script> <script>
import SettingMixin from '../setting-mixin'; import SettingMixin from '../setting-mixin';
export default { export default {
@ -60,5 +66,4 @@ export default {
} }
} }
} }
</script> </script>

View file

@ -1,46 +1,48 @@
<template lang="html"> <template lang="html">
<div class="ffz--widget ffz--text-box" :class="{inherits: isInherited, default: isDefault}"> <div
<div class="tw-flex tw-align-items-center"> :class="{inherits: isInherited, default: isDefault}"
<label :for="item.full_key"> class="ffz--widget ffz--text-box"
{{ t(item.i18n_key, item.title, item) }} >
</label> <div class="tw-flex tw-align-items-center">
<label :for="item.full_key">
{{ t(item.i18n_key, item.title, item) }}
</label>
<input <input
class="tw-mg-05 tw-input tw-display-inline tw-width-auto" ref="control"
ref="control" :id="item.full_key"
:id="item.full_key" :value="value"
@change="onChange" class="tw-mg-05 tw-input tw-display-inline tw-width-auto"
:value="value" @change="onChange"
/> >
<button <button
v-if="source && source !== profile" v-if="source && source !== profile"
class="tw-mg-l-05 tw-button tw-button--text" class="tw-mg-l-05 tw-button tw-button--text"
@click="context.currentProfile = source" @click="context.currentProfile = source"
> >
<span class="tw-button__text ffz-i-right-dir"> <span class="tw-button__text ffz-i-right-dir">
{{ sourceDisplay }} {{ sourceDisplay }}
</span> </span>
</button> </button>
<button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear"> <button v-if="has_value" class="tw-mg-l-05 tw-button tw-button--text tw-tooltip-wrapper" @click="clear">
<span class="tw-button__text ffz-i-cancel"></span> <span class="tw-button__text ffz-i-cancel" />
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right"> <div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
{{ t('setting.reset', 'Reset to Default') }} {{ t('setting.reset', 'Reset to Default') }}
</div> </div>
</button> </button>
</div> </div>
<section <section
v-if="item.description" v-if="item.description"
class="tw-c-text-alt-2" class="tw-c-text-alt-2"
v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)" v-html="t(item.desc_i18n_key || item.i18n_key + '.description', item.description, item)"
/> />
</div> </div>
</template> </template>
<script> <script>
import SettingMixin from '../setting-mixin'; import SettingMixin from '../setting-mixin';
export default { export default {
@ -55,5 +57,4 @@ export default {
} }
} }
} }
</script> </script>

View file

@ -1,154 +1,193 @@
<template lang="html"> <template lang="html">
<div class="ffz-auto-host-options tw-c-background"> <div class="ffz-auto-host-options tw-c-background">
<header class="tw-full-width tw-align-items-center tw-flex tw-flex-nowrap"> <header class="tw-full-width tw-align-items-center tw-flex tw-flex-nowrap">
<h4>{{ t('metadata.host.title', 'Auto Host Management') }}</h4> <h4>{{ t('metadata.host.title', 'Auto Host Management') }}</h4>
</header>
<div class="tab tw-overflow-hidden"
v-show="activeTab === 'auto-host'"
:class="{ active: activeTab === 'auto-host'}">
<section class="tw-border-t tw-full-width tw-full-height">
<main class="tw-flex-grow-1 scrollable-area" data-simplebar="init">
<div class="simplebar-scroll-content">
<draggable v-model="hosts" class="simplebar-content" :options="{
draggable: '.ffz--host-user',
animation: 150,
}" @update="rearrangeHosts">
<div v-for="host in hosts" class="tw-border-t ffz--host-user" :key="host._id" :data-id="host._id">
<div class="tw-interactable">
<div class="tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1">
<figure class="ffz-i-ellipsis-vert handle"></figure>
<div class="ffz-channel-avatar">
<img :src="host.logo" :alt="host.display_name + '(' + host.name + ')'">
</div>
<p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">{{ host.name }}</p>
<div class="tw-flex-grow-1 tw-pd-x-2"></div>
<button class="tw-button-icon tw-mg-x-05 ffz--host-remove-user" @click="removeFromHosts">
<figure class="ffz-i-trash"></figure>
</button>
</div>
</div>
</div>
</draggable>
</div>
</main>
</section>
<header class="tw-border-t tw-full-width tw-align-items-center tw-flex tw-flex-noxwrap tw-pd-1">
<div class="tw-flex-grow-1 tw-pd-x-2"></div>
<button class="tw-button tw-button--hollow tw-mg-x-05" :class="{'tw-button--disabled': addedToHosts}" @click="addToAutoHosts">
<span class="tw-button__text">{{ t('metadata.host.add-channel', 'Add To Auto Host') }}</span>
</button>
</header> </header>
</div> <div
<div class="tab tw-overflow-hidden" v-show="activeTab === 'auto-host'"
v-show="activeTab === 'settings'" :class="{ active: activeTab === 'auto-host'}"
:class="{ active: activeTab === 'settings'}"> class="tab tw-overflow-hidden"
<section class="tw-border-t tw-full-width tw-full-height"> >
<main class="tw-flex-grow-1 scrollable-area" data-simplebar="init"> <section class="tw-border-t tw-full-width tw-full-height">
<div class="simplebar-scroll-content"> <main class="tw-flex-grow-1 scrollable-area" data-simplebar="init">
<div class="simplebar-content"> <div class="simplebar-scroll-content">
<div class="tw-pd-1"> <draggable
<div class="ffz--widget ffz--checkbox"> v-model="hosts"
<div class="tw-flex tw-align-items-center"> :options="{
<input type="checkbox" class="tw-checkbox__input" draggable: '.ffz--host-user',
id="autoHostSettings:enabled" animation: 150,
data-setting="enabled" }"
:checked="autoHostSettings.enabled" class="simplebar-content"
@change="updateCheckbox"> @update="rearrangeHosts"
<label for="autoHostSettings:enabled" class="tw-checkbox__label"> >
{{ t('metadata.host.setting.auto-hosting.title', 'Auto Hosting') }} <div
</label> v-for="host in hosts"
:key="host._id"
:data-id="host._id"
class="tw-border-t ffz--host-user"
>
<div class="tw-interactable">
<div class="tw-align-items-center tw-flex tw-flex-row tw-flex-nowrap tw-mg-x-1">
<figure class="ffz-i-ellipsis-vert handle" />
<div class="ffz-channel-avatar">
<img :src="host.logo" :alt="host.display_name + '(' + host.name + ')'">
</div>
<p class="tw-ellipsis tw-flex-grow-1 tw-mg-l-1 tw-font-size-5">{{ host.name }}</p>
<div class="tw-flex-grow-1 tw-pd-x-2" />
<button class="tw-button-icon tw-mg-x-05 ffz--host-remove-user" @click="removeFromHosts">
<figure class="ffz-i-trash" />
</button>
</div>
</div> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.auto-hosting.description', 'Toggle all forms of auto hosting: teammates, host list, and similar channels.') }}<br>
<a href="https://blog.twitch.tv/grow-your-community-with-auto-hosting-e80c1460f6e1" target="_blank" rel="noopener">{{ t('metadata.host.setting.auto-hosting.link', 'Learn More') }}</a>
</section>
</div> </div>
<div class="ffz--widget ffz--checkbox"> </draggable>
<div class="tw-flex tw-align-items-center"> </div>
<input type="checkbox" class="tw-checkbox__input" </main>
id="autoHostSettings:team_host" </section>
data-setting="team_host" <header class="tw-border-t tw-full-width tw-align-items-center tw-flex tw-flex-noxwrap tw-pd-1">
:checked="autoHostSettings.team_host" <div class="tw-flex-grow-1 tw-pd-x-2" />
@change="updateCheckbox"> <button
<label for="autoHostSettings:team_host" class="tw-checkbox__label"> :class="{'tw-button--disabled': addedToHosts}"
{{ t('metadata.host.setting.team-hosting.title', 'Team Hosting') }} class="tw-button tw-button--hollow tw-mg-x-05"
</label> @click="addToAutoHosts"
>
<span class="tw-button__text">{{ t('metadata.host.add-channel', 'Add To Auto Host') }}</span>
</button>
</header>
</div>
<div
v-show="activeTab === 'settings'"
:class="{ active: activeTab === 'settings'}"
class="tab tw-overflow-hidden"
>
<section class="tw-border-t tw-full-width tw-full-height">
<main class="tw-flex-grow-1 scrollable-area" data-simplebar="init">
<div class="simplebar-scroll-content">
<div class="simplebar-content">
<div class="tw-pd-1">
<div class="ffz--widget ffz--checkbox">
<div class="tw-flex tw-align-items-center">
<input
id="autoHostSettings:enabled"
:checked="autoHostSettings.enabled"
type="checkbox"
class="tw-checkbox__input"
data-setting="enabled"
@change="updateCheckbox"
>
<label for="autoHostSettings:enabled" class="tw-checkbox__label">
{{ t('metadata.host.setting.auto-hosting.title', 'Auto Hosting') }}
</label>
</div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.auto-hosting.description', 'Toggle all forms of auto hosting: teammates, host list, and similar channels.') }}<br>
<a href="https://blog.twitch.tv/grow-your-community-with-auto-hosting-e80c1460f6e1" target="_blank" rel="noopener">{{ t('metadata.host.setting.auto-hosting.link', 'Learn More') }}</a>
</section>
</div> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description"> <div class="ffz--widget ffz--checkbox">
{{ t('metadata.host.setting.team-hosting.description', <div class="tw-flex tw-align-items-center">
'Automatically host random channels from your team when you\'re not live. ' + <input
id="autoHostSettings:team_host"
:checked="autoHostSettings.team_host"
type="checkbox"
class="tw-checkbox__input"
data-setting="team_host"
@change="updateCheckbox"
>
<label for="autoHostSettings:team_host" class="tw-checkbox__label">
{{ t('metadata.host.setting.team-hosting.title', 'Team Hosting') }}
</label>
</div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.team-hosting.description',
"Automatically host random channels from your team when you're not live. " +
'Team channels will be hosted before any channels in your host list.') }} 'Team channels will be hosted before any channels in your host list.') }}
</section> </section>
</div>
<div class="ffz--widget ffz--checkbox">
<div class="tw-flex tw-align-items-center">
<input type="checkbox" class="tw-checkbox__input"
id="autoHostSettings:vodcast_hosting"
data-setting="deprioritize_vodcast"
:checked="!autoHostSettings.deprioritize_vodcast"
@change="updateCheckbox">
<label for="autoHostSettings:vodcast_hosting" class="tw-checkbox__label">
{{ t('metadata.host.setting.vodcast-hosting.title', 'Vodcast Hosting') }}
</label>
</div> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description"> <div class="ffz--widget ffz--checkbox">
{{ t('metadata.host.setting.vodcast-hosting.description', 'Include Vodcasts in auto host.') }} <div class="tw-flex tw-align-items-center">
<a href="https://blog.twitch.tv/vodcast-brings-the-twitch-community-experience-to-uploads-54098498715" target="_blank" rel="noopener">{{ t('metadata.host.setting.vodcast-hosting.link', 'Learn about Vodcasts') }}</a> <input
</section> id="autoHostSettings:vodcast_hosting"
</div> :checked="!autoHostSettings.deprioritize_vodcast"
<div class="ffz--widget ffz--checkbox"> type="checkbox"
<div class="tw-flex tw-align-items-center"> class="tw-checkbox__input"
<input type="checkbox" class="tw-checkbox__input" data-setting="deprioritize_vodcast"
id="autoHostSettings:recommended_host" @change="updateCheckbox"
data-setting="recommended_host" >
:checked="autoHostSettings.recommended_host" <label for="autoHostSettings:vodcast_hosting" class="tw-checkbox__label">
@change="updateCheckbox"> {{ t('metadata.host.setting.vodcast-hosting.title', 'Vodcast Hosting') }}
<label for="autoHostSettings:recommended_host" class="tw-checkbox__label"> </label>
{{ t('metadata.host.setting.recommended-hosting.title', 'Auto Host Channels Similar To Yours') }} </div>
</label> <section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.vodcast-hosting.description', 'Include Vodcasts in auto host.') }}
<a href="https://blog.twitch.tv/vodcast-brings-the-twitch-community-experience-to-uploads-54098498715" target="_blank" rel="noopener">{{ t('metadata.host.setting.vodcast-hosting.link', 'Learn about Vodcasts') }}</a>
</section>
</div> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description"> <div class="ffz--widget ffz--checkbox">
{{ t('metadata.host.setting.recommended-hosting.description', 'Streamers on your primary team &amp; host list will always be hosted first') }} <div class="tw-flex tw-align-items-center">
</section> <input
</div> id="autoHostSettings:recommended_host"
<div class="ffz--widget ffz--checkbox"> :checked="autoHostSettings.recommended_host"
<div class="tw-flex tw-align-items-center"> type="checkbox"
<input type="checkbox" class="tw-checkbox__input" class="tw-checkbox__input"
id="autoHostSettings:strategy" data-setting="recommended_host"
data-setting="strategy" @change="updateCheckbox"
:checked="autoHostSettings.strategy === 'random'" >
@change="updateCheckbox"> <label for="autoHostSettings:recommended_host" class="tw-checkbox__label">
<label for="autoHostSettings:strategy" class="tw-checkbox__label"> {{ t('metadata.host.setting.recommended-hosting.title', 'Auto Host Channels Similar To Yours') }}
{{ t('metadata.host.setting.strategy.title', 'Randomize Host Order') }} </label>
</label> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.recommended-hosting.description', 'Streamers on your primary team &amp; host list will always be hosted first') }}
</section>
</div>
<div class="ffz--widget ffz--checkbox">
<div class="tw-flex tw-align-items-center">
<input
id="autoHostSettings:strategy"
:checked="autoHostSettings.strategy === 'random'"
type="checkbox"
class="tw-checkbox__input"
data-setting="strategy"
@change="updateCheckbox"
>
<label for="autoHostSettings:strategy" class="tw-checkbox__label">
{{ t('metadata.host.setting.strategy.title', 'Randomize Host Order') }}
</label>
</div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.strategy.description',
'If enabled, auto-hosts will be picked at random. ' +
"Otherwise they're picked in order.") }}
</section>
</div> </div>
<section class="tw-c-text-alt-2 ffz-checkbox-description">
{{ t('metadata.host.setting.strategy.description',
'If enabled, auto-hosts will be picked at random. ' +
'Otherwise they\'re picked in order.') }}
</section>
</div> </div>
</div> </div>
</div> </div>
</div> </main>
</main> </section>
</section>
</div>
<footer>
<div class="host-options__tabs-container tw-border-t">
<div id="host-options__auto-host" class="host-options__tab tw-pd-x-1"
@click="setActiveTab('auto-host')"
:class="{active: activeTab === 'auto-host'}">
<span>{{ t('metadata.host.tab.auto-host', 'Auto Host') }}</span>
</div>
<div id="host-options__settings" class="host-options__tab tw-pd-x-1"
@click="setActiveTab('settings')"
:class="{active: activeTab === 'settings'}">
<span>{{ t('metadata.host.tab.settings', 'Settings') }}</span>
</div>
</div> </div>
</footer> <footer>
</div> <div class="host-options__tabs-container tw-border-t">
<div
id="host-options__auto-host"
:class="{active: activeTab === 'auto-host'}"
class="host-options__tab tw-pd-x-1"
@click="setActiveTab('auto-host')"
>
<span>{{ t('metadata.host.tab.auto-host', 'Auto Host') }}</span>
</div>
<div
id="host-options__settings"
:class="{active: activeTab === 'settings'}"
class="host-options__tab tw-pd-x-1"
@click="setActiveTab('settings')"
>
<span>{{ t('metadata.host.tab.settings', 'Settings') }}</span>
</div>
</div>
</footer>
</div>
</template> </template>
@ -160,7 +199,7 @@ export default {
draggable draggable
}, },
data() { data() {
return this.$vnode.data; return this.$vnode.data;
}, },

View file

@ -1,58 +1,59 @@
<template lang="html"> <template lang="html">
<div <div
v-if="item.tabs" v-if="item.tabs"
class="ffz--tab-container" class="ffz--tab-container"
@keyup.alt.page-up.stop="focusPrevTab" @keyup.alt.page-up.stop="focusPrevTab"
@keyup.alt.page-down.stop="focusNextTab" @keyup.alt.page-down.stop="focusNextTab"
>
<header
class="tw-flex"
tabindex="0"
role="tablist"
@keyup.home="firstTab"
@keyup.end="lastTab"
@keyup.page-up="prevTab"
@keyup.up="prevTab"
@keyup.left="prevTab"
@keyup.page-down="nextTab"
@keyup.right="nextTab"
@keyup.down="nextTab"
> >
<div <header
v-for="(i, idx) in item.tabs" class="tw-flex"
role="tab" tabindex="0"
:id="'tab-for-' + i.full_key" role="tablist"
:aria-selected="selected === idx" @keyup.home="firstTab"
:aria-controls="'tab-panel-' + i.full_key" @keyup.end="lastTab"
class="tab tw-pd-y-05 tw-pd-x-1" @keyup.page-up="prevTab"
:class="[selected === idx ? 'active' : '']" @keyup.up="prevTab"
@click="selected = idx" @keyup.left="prevTab"
@keyup.page-down="nextTab"
@keyup.right="nextTab"
@keyup.down="nextTab"
> >
{{ t(i.i18n_key, i.title, i) }} <div
</div> v-for="(i, idx) in item.tabs"
</header> :key="i.full_key"
<section :id="'tab-for-' + i.full_key"
class="tw-border" :aria-selected="selected === idx"
:id="'tab-panel-' + tab.full_key" :aria-controls="'tab-panel-' + i.full_key"
:aria-labelledby="'tab-for-' + tab.full_key" :class="[selected === idx ? 'active' : '']"
role="tabpanel" role="tab"
aria-hidden="false" class="tab tw-pd-y-05 tw-pd-x-1"
aria-expanded="true" @click="selected = idx"
> >
<section v-if="tab.description" class="tw-pd-b-1"> {{ t(i.i18n_key, i.title, i) }}
{{ t(tab.desc_i18n_key, tab.description, tab) }} </div>
</section> </header>
<component <section
v-for="i in tab.contents" :id="'tab-panel-' + tab.full_key"
v-bind:is="i.component" :aria-labelledby="'tab-for-' + tab.full_key"
:currentProfile="currentProfile" class="tw-border"
:profiles="profiles" role="tabpanel"
:context="context" aria-hidden="false"
:item="i" aria-expanded="true"
:key="i.full_key" >
<section v-if="tab.description" class="tw-pd-b-1">
{{ t(tab.desc_i18n_key, tab.description, tab) }}
</section>
<component
v-for="i in tab.contents"
:is="i.component"
:current-profile="currentProfile"
:profiles="profiles"
:context="context"
:item="i"
:key="i.full_key"
/> />
</section> </section>
</div> </div>
</template> </template>
<script> <script>