kopia lustrzana https://github.com/wagtail/wagtail
Reformat codebase with Prettier (#7912)
- Automated reformatting - Manually change code where Prettier reformatting causes issues - Revert "Disable Prettier formatting in CI for now"pull/7907/head
rodzic
11ccf30f00
commit
af942a27e4
|
@ -12,9 +12,9 @@ jobs:
|
|||
key: pipenv-v1-{{ checksum "setup.py" }}
|
||||
# Only install if .venv wasn’t cached.
|
||||
- run: |
|
||||
if [[ ! -e ".venv" ]]; then
|
||||
pipenv install -e .[testing]
|
||||
fi
|
||||
if [[ ! -e ".venv" ]]; then
|
||||
pipenv install -e .[testing]
|
||||
fi
|
||||
- save_cache:
|
||||
key: pipenv-v1-{{ checksum "setup.py" }}
|
||||
paths:
|
||||
|
@ -39,9 +39,9 @@ jobs:
|
|||
key: frontend-v1-{{ checksum "package-lock.json" }}
|
||||
# Only install if node_modules wasn’t cached.
|
||||
- run: |
|
||||
if [[ ! -e "node_modules" ]]; then
|
||||
npm install --no-save --no-optional --no-audit --no-fund --progress=false
|
||||
fi
|
||||
if [[ ! -e "node_modules" ]]; then
|
||||
npm install --no-save --no-optional --no-audit --no-fund --progress=false
|
||||
fi
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
|
@ -54,8 +54,7 @@ jobs:
|
|||
- wagtail
|
||||
- run: npm run lint:js
|
||||
- run: npm run lint:css
|
||||
# TODO Remove || true and enforce a successful exit code.
|
||||
- run: npm run lint:format || true
|
||||
- run: npm run lint:format
|
||||
- run: npm run test:unit:coverage -- --runInBand
|
||||
- run: bash <(curl -s https://codecov.io/bash) -F frontend
|
||||
|
||||
|
@ -73,9 +72,9 @@ jobs:
|
|||
key: pipenv-v1-{{ checksum "setup.py" }}
|
||||
# Only install if .venv wasn’t cached.
|
||||
- run: |
|
||||
if [[ ! -e ".venv" ]]; then
|
||||
pipenv install -e .[testing]
|
||||
fi
|
||||
if [[ ! -e ".venv" ]]; then
|
||||
pipenv install -e .[testing]
|
||||
fi
|
||||
- save_cache:
|
||||
key: pipenv-v1-{{ checksum "setup.py" }}
|
||||
paths:
|
||||
|
@ -84,9 +83,9 @@ jobs:
|
|||
key: ui_tests-npm_integration-v1-{{ checksum "client/tests/integration/package-lock.json" }}
|
||||
# Only install if node_modules wasn’t cached.
|
||||
- run: |
|
||||
if [[ ! -e "client/tests/integration/node_modules" ]]; then
|
||||
npm --prefix ./client/tests/integration ci
|
||||
fi
|
||||
if [[ ! -e "client/tests/integration/node_modules" ]]; then
|
||||
npm --prefix ./client/tests/integration ci
|
||||
fi
|
||||
- save_cache:
|
||||
key: ui_tests-npm_integration-v1-{{ checksum "client/tests/integration/package-lock.json" }}
|
||||
paths:
|
||||
|
@ -123,22 +122,22 @@ jobs:
|
|||
- run: python scripts/nightly/upload.py
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test:
|
||||
jobs:
|
||||
- backend
|
||||
- frontend
|
||||
- ui_tests:
|
||||
requires:
|
||||
- frontend
|
||||
version: 2
|
||||
test:
|
||||
jobs:
|
||||
- backend
|
||||
- frontend
|
||||
- ui_tests:
|
||||
requires:
|
||||
- frontend
|
||||
|
||||
nightly:
|
||||
jobs:
|
||||
- nightly-build
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
nightly:
|
||||
jobs:
|
||||
- nightly-build
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: '0 0 * * *'
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
|
|
254
.eslintrc.js
254
.eslintrc.js
|
@ -1,83 +1,81 @@
|
|||
// Rules which have been enforced in configuration upgrades and flag issues in existing code.
|
||||
// We need to consider whether to disable those rules permanently, or fix the issues.
|
||||
const legacyCode = {
|
||||
"class-methods-use-this": "off",
|
||||
"constructor-super": "off",
|
||||
"default-param-last": "off",
|
||||
"import/extensions": "off",
|
||||
"import/first": "off",
|
||||
"import/newline-after-import": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-useless-path-segments": "off",
|
||||
"import/order": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"jsx-a11y/alt-text": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/interactive-supports-focus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"jsx-a11y/role-supports-aria-props": "off",
|
||||
"lines-between-class-members": "off",
|
||||
"max-classes-per-file": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-continue": "off",
|
||||
"no-else-return": "off",
|
||||
"no-extra-boolean-cast": "off",
|
||||
"no-import-assign": "off",
|
||||
"no-lonely-if": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-this-before-super": "off",
|
||||
"operator-assignment": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"prefer-object-spread": "off",
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"react-hooks/rules-of-hooks": "off",
|
||||
"react/button-has-type": "off",
|
||||
"react/destructuring-assignment": "off",
|
||||
"react/forbid-prop-types": "off",
|
||||
"react/function-component-definition": "off",
|
||||
"react/jsx-curly-brace-presence": "off",
|
||||
"react/jsx-filename-extension": "off",
|
||||
"react/jsx-no-useless-fragment": "off",
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"react/no-danger": "off",
|
||||
"react/no-deprecated": "off",
|
||||
"react/require-default-props": "off",
|
||||
}
|
||||
'class-methods-use-this': 'off',
|
||||
'constructor-super': 'off',
|
||||
'default-param-last': 'off',
|
||||
'import/extensions': 'off',
|
||||
'import/first': 'off',
|
||||
'import/newline-after-import': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'import/no-useless-path-segments': 'off',
|
||||
'import/order': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'jsx-a11y/alt-text': 'off',
|
||||
'jsx-a11y/anchor-is-valid': 'off',
|
||||
'jsx-a11y/click-events-have-key-events': 'off',
|
||||
'jsx-a11y/interactive-supports-focus': 'off',
|
||||
'jsx-a11y/no-noninteractive-element-interactions': 'off',
|
||||
'jsx-a11y/role-supports-aria-props': 'off',
|
||||
'lines-between-class-members': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'no-await-in-loop': 'off',
|
||||
'no-continue': 'off',
|
||||
'no-else-return': 'off',
|
||||
'no-extra-boolean-cast': 'off',
|
||||
'no-import-assign': 'off',
|
||||
'no-lonely-if': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-this-before-super': 'off',
|
||||
'operator-assignment': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
'prefer-object-spread': 'off',
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
'react-hooks/rules-of-hooks': 'off',
|
||||
'react/button-has-type': 'off',
|
||||
'react/destructuring-assignment': 'off',
|
||||
'react/forbid-prop-types': 'off',
|
||||
'react/function-component-definition': 'off',
|
||||
'react/jsx-curly-brace-presence': 'off',
|
||||
'react/jsx-filename-extension': 'off',
|
||||
'react/jsx-no-useless-fragment': 'off',
|
||||
'react/jsx-props-no-spreading': 'off',
|
||||
'react/no-danger': 'off',
|
||||
'react/no-deprecated': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"@wagtail/eslint-config-wagtail",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
extends: [
|
||||
'@wagtail/eslint-config-wagtail',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"env": {
|
||||
"jest": true,
|
||||
"browser": true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
env: {
|
||||
jest: true,
|
||||
browser: true,
|
||||
},
|
||||
"rules": {
|
||||
"no-underscore-dangle": ["error", { "allow": ["__REDUX_DEVTOOLS_EXTENSION__"] }],
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": ["error"],
|
||||
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
'react/jsx-filename-extension': [
|
||||
"error",
|
||||
{ extensions: ['.js', '.tsx'] },
|
||||
rules: {
|
||||
'no-underscore-dangle': [
|
||||
'error',
|
||||
{ allow: ['__REDUX_DEVTOOLS_EXTENSION__'] },
|
||||
],
|
||||
// note you must disable the base rule as it can report incorrect errors
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': ['error'],
|
||||
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'react/jsx-filename-extension': ['error', { extensions: ['.js', '.tsx'] }],
|
||||
'import/extensions': [
|
||||
"error",
|
||||
'error',
|
||||
'always',
|
||||
{
|
||||
ignorePackages: true,
|
||||
|
@ -91,66 +89,66 @@ module.exports = {
|
|||
],
|
||||
...legacyCode,
|
||||
},
|
||||
"overrides": [
|
||||
overrides: [
|
||||
{
|
||||
// Rules we don’t want to enforce for test and tooling code.
|
||||
"files": ["*.test.ts", "*.test.tsx", "*.test.js", "webpack.config.js"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["docs/_static/**"],
|
||||
"globals": { "$": "readonly" }
|
||||
},
|
||||
{
|
||||
"files": ["wagtail/**/**"],
|
||||
"globals": {
|
||||
"$": "readonly",
|
||||
"addMessage": "readonly",
|
||||
"buildExpandingFormset": "readonly",
|
||||
"cancelSpinner": "readonly",
|
||||
"escapeHtml": "readonly",
|
||||
"insertRichTextDeleteControl": "readonly",
|
||||
"jQuery": "readonly",
|
||||
"jsonData": "readonly",
|
||||
"ModalWorkflow": "readonly",
|
||||
"DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
|
||||
"EMBED_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
|
||||
"IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
|
||||
"QUERY_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable",
|
||||
"SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS": "writable"
|
||||
files: ['*.test.ts', '*.test.tsx', '*.test.js', 'webpack.config.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"camelcase": [
|
||||
"error",
|
||||
},
|
||||
{
|
||||
files: ['docs/_static/**'],
|
||||
globals: { $: 'readonly' },
|
||||
},
|
||||
{
|
||||
files: ['wagtail/**/**'],
|
||||
globals: {
|
||||
$: 'readonly',
|
||||
addMessage: 'readonly',
|
||||
buildExpandingFormset: 'readonly',
|
||||
cancelSpinner: 'readonly',
|
||||
escapeHtml: 'readonly',
|
||||
insertRichTextDeleteControl: 'readonly',
|
||||
jQuery: 'readonly',
|
||||
jsonData: 'readonly',
|
||||
ModalWorkflow: 'readonly',
|
||||
DOCUMENT_CHOOSER_MODAL_ONLOAD_HANDLERS: 'writable',
|
||||
EMBED_CHOOSER_MODAL_ONLOAD_HANDLERS: 'writable',
|
||||
IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS: 'writable',
|
||||
QUERY_CHOOSER_MODAL_ONLOAD_HANDLERS: 'writable',
|
||||
SNIPPET_CHOOSER_MODAL_ONLOAD_HANDLERS: 'writable',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'camelcase': [
|
||||
'error',
|
||||
{
|
||||
"allow": [
|
||||
"__unused_webpack_module",
|
||||
"__webpack_modules__",
|
||||
"__webpack_require__"
|
||||
allow: [
|
||||
'__unused_webpack_module',
|
||||
'__webpack_modules__',
|
||||
'__webpack_require__',
|
||||
],
|
||||
"properties": "never"
|
||||
}
|
||||
properties: 'never',
|
||||
},
|
||||
],
|
||||
"consistent-return": "off",
|
||||
"func-names": "off",
|
||||
"id-length": "off",
|
||||
"indent": "off",
|
||||
"key-spacing": "off",
|
||||
"new-cap": "off",
|
||||
"newline-per-chained-call": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"object-shorthand": "off",
|
||||
"prefer-arrow-callback": "off",
|
||||
"quote-props": "off",
|
||||
"space-before-function-paren": "off",
|
||||
"vars-on-top": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
'consistent-return': 'off',
|
||||
'func-names': 'off',
|
||||
'id-length': 'off',
|
||||
'indent': 'off',
|
||||
'key-spacing': 'off',
|
||||
'new-cap': 'off',
|
||||
'newline-per-chained-call': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'object-shorthand': 'off',
|
||||
'prefer-arrow-callback': 'off',
|
||||
'quote-props': 'off',
|
||||
'space-before-function-paren': 'off',
|
||||
'vars-on-top': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: "CodeQL"
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
|
@ -13,16 +13,16 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
language: [ 'javascript', 'python' ]
|
||||
language: ['javascript', 'python']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
|
|
@ -3,11 +3,10 @@ name: Wagtail CI
|
|||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
- 'docs/**'
|
||||
|
||||
# Our test suite should cover:
|
||||
# - all supported databases against current Python and Django
|
||||
|
@ -36,8 +35,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.9"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.9'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
@ -62,22 +61,22 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.7"
|
||||
django: "Django>=3.2,<3.3"
|
||||
- python: '3.7'
|
||||
django: 'Django>=3.2,<3.3'
|
||||
experimental: false
|
||||
- python: "3.10"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.10'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
notz: notz
|
||||
experimental: false
|
||||
- python: "3.10"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.10'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
experimental: false
|
||||
emailuser: emailuser
|
||||
- python: "3.10"
|
||||
django: "git+https://github.com/django/django.git@stable/4.0.x#egg=Django"
|
||||
- python: '3.10'
|
||||
django: 'git+https://github.com/django/django.git@stable/4.0.x#egg=Django'
|
||||
experimental: true
|
||||
- python: "3.10"
|
||||
django: "git+https://github.com/django/django.git@main#egg=Django"
|
||||
- python: '3.10'
|
||||
django: 'git+https://github.com/django/django.git@main#egg=Django'
|
||||
experimental: true
|
||||
|
||||
services:
|
||||
|
@ -116,11 +115,11 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.8"
|
||||
django: "Django>=3.2,<3.3"
|
||||
- python: '3.8'
|
||||
django: 'Django>=3.2,<3.3'
|
||||
experimental: false
|
||||
- python: "3.9"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.9'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
experimental: false
|
||||
|
||||
services:
|
||||
|
@ -150,7 +149,7 @@ jobs:
|
|||
./runtests.py
|
||||
env:
|
||||
DATABASE_ENGINE: django.db.backends.mysql
|
||||
DATABASE_HOST: "127.0.0.1"
|
||||
DATABASE_HOST: '127.0.0.1'
|
||||
DATABASE_USER: root
|
||||
|
||||
# https://github.com/elastic/elastic-github-actions doesn't work for Elasticsearch 5,
|
||||
|
@ -160,8 +159,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.7"
|
||||
django: "Django>=3.2,<3.3"
|
||||
- python: '3.7'
|
||||
django: 'Django>=3.2,<3.3'
|
||||
steps:
|
||||
- name: Configure sysctl limits
|
||||
run: |
|
||||
|
@ -200,8 +199,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.9"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.9'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
emailuser: emailuser
|
||||
steps:
|
||||
- name: Configure sysctl limits
|
||||
|
@ -244,8 +243,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.7"
|
||||
django: "Django>=3.2,<3.3"
|
||||
- python: '3.7'
|
||||
django: 'Django>=3.2,<3.3'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
@ -293,8 +292,8 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- python: "3.8"
|
||||
django: "Django>=4.0,<4.1"
|
||||
- python: '3.8'
|
||||
django: 'Django>=4.0,<4.1'
|
||||
experimental: false
|
||||
|
||||
services:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
python:
|
||||
version: 3.7
|
||||
pip_install: true
|
||||
version: 3.7
|
||||
pip_install: true
|
||||
requirements_file: null
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module.exports = {
|
||||
"stories": [
|
||||
"../src/**/*.stories.@(js|jsx|ts|tsx)"
|
||||
],
|
||||
"core": {
|
||||
"builder": "webpack5"
|
||||
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
core: {
|
||||
builder: 'webpack5',
|
||||
},
|
||||
webpackFinal: (config) => {
|
||||
config.resolve.fallback.crypto = false;
|
||||
|
@ -12,17 +10,17 @@ module.exports = {
|
|||
{
|
||||
test: /\.(scss|css)$/,
|
||||
use: [
|
||||
"style-loader",
|
||||
"css-loader",
|
||||
'style-loader',
|
||||
'css-loader',
|
||||
{
|
||||
loader: "postcss-loader",
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: ["autoprefixer", "cssnano"],
|
||||
plugins: ['autoprefixer', 'cssnano'],
|
||||
},
|
||||
},
|
||||
},
|
||||
"sass-loader",
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -4,11 +4,11 @@ import '../../wagtail/admin/static_src/wagtailadmin/scss/core.scss';
|
|||
import '../../wagtail/admin/static_src/wagtailadmin/scss/sidebar.scss';
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,90 +1,89 @@
|
|||
.breadcrumb {
|
||||
@include unlist();
|
||||
@include clearfix();
|
||||
padding-top: 1.4em;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.5em;
|
||||
margin-left: 2em;
|
||||
background: $color-teal;
|
||||
@include unlist();
|
||||
@include clearfix();
|
||||
padding-top: 1.4em;
|
||||
font-size: 0.85em;
|
||||
line-height: 1.5em;
|
||||
margin-left: 2em;
|
||||
background: $color-teal;
|
||||
|
||||
li,
|
||||
.breadcrumb-item {
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
padding: 4px;
|
||||
li,
|
||||
.breadcrumb-item {
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
padding: 4px;
|
||||
|
||||
&:hover {
|
||||
background: $color-teal-dark;
|
||||
}
|
||||
|
||||
&.breadcrumb-dropdown {
|
||||
padding: 0;
|
||||
font-size: initial;
|
||||
}
|
||||
|
||||
.t-default .u-btn-current {
|
||||
color: inherit;
|
||||
background: rgba(0, 91, 94, 0.6);
|
||||
font-size: 1.15em;
|
||||
border: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
&:hover {
|
||||
background: $color-teal-dark;
|
||||
}
|
||||
|
||||
li > a,
|
||||
.breadcrumb-link {
|
||||
color: $color-white;
|
||||
display: block;
|
||||
padding: calc(0.5em - 4px) 1em;
|
||||
white-space: nowrap;
|
||||
line-height: 1.6em;
|
||||
|
||||
&:hover {
|
||||
background: $color-teal-dark;
|
||||
color: $color-white;
|
||||
|
||||
.arrow_right_icon {
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
max-width: 11.8em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
&.breadcrumb-dropdown {
|
||||
padding: 0;
|
||||
font-size: initial;
|
||||
}
|
||||
|
||||
.t-default .u-btn-current {
|
||||
color: inherit;
|
||||
background: rgba(0, 91, 94, 0.6);
|
||||
font-size: 1.15em;
|
||||
border: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
li > a,
|
||||
.breadcrumb-link {
|
||||
color: $color-white;
|
||||
display: block;
|
||||
padding: calc(0.5em - 4px) 1em;
|
||||
white-space: nowrap;
|
||||
line-height: 1.6em;
|
||||
|
||||
&:hover {
|
||||
background: $color-teal-dark;
|
||||
color: $color-white;
|
||||
|
||||
.arrow_right_icon {
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
max-width: 11.8em;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.home_icon {
|
||||
@include svg-icon(1em);
|
||||
transform: scale(1.5) translate(0, 0.1em);
|
||||
}
|
||||
|
||||
.arrow_right_icon {
|
||||
@include svg-icon(1em);
|
||||
color: $color-teal-dark;
|
||||
transform: scale(1.75) translate(0.3em, 0.1em);
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-top: 0;
|
||||
background: $color-teal-darker;
|
||||
margin-left: -($desktop-nice-padding);
|
||||
margin-right: -($desktop-nice-padding);
|
||||
|
||||
.home_icon {
|
||||
@include svg-icon(1em);
|
||||
transform: scale(1.5) translate(0, 0.1em);
|
||||
margin-left: 1.25em;
|
||||
}
|
||||
|
||||
.arrow_right_icon {
|
||||
@include svg-icon(1em);
|
||||
color: $color-teal-dark;
|
||||
transform: scale(1.75) translate(0.3em, 0.1em);
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-top: 0;
|
||||
background: $color-teal-darker;
|
||||
margin-left: -($desktop-nice-padding);
|
||||
margin-right: -($desktop-nice-padding);
|
||||
|
||||
.home_icon {
|
||||
margin-left: 1.25em;
|
||||
}
|
||||
|
||||
.arrow_right_icon {
|
||||
color: $color-teal;
|
||||
}
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +1,108 @@
|
|||
.bulk-actions-filter-checkbox {
|
||||
.table-headers & {
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.c-dropdown__button {
|
||||
padding-left: 0.3rem;
|
||||
}
|
||||
|
||||
.bulk-actions-choices,
|
||||
.bulk-actions-choices > ul {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bulk-actions-choices li {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.bulk-actions-choices span {
|
||||
text-transform: none;
|
||||
}
|
||||
.table-headers & {
|
||||
> div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.c-dropdown__button {
|
||||
padding-left: 0.3rem;
|
||||
}
|
||||
|
||||
.bulk-actions-choices,
|
||||
.bulk-actions-choices > ul {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bulk-actions-choices li {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.bulk-actions-choices span {
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bulk-actions-choices {
|
||||
&.footer {
|
||||
@include transition(transform 0.1s ease 0.1s);
|
||||
&.footer {
|
||||
@include transition(transform 0.1s ease 0.1s);
|
||||
|
||||
&.hidden {
|
||||
transform: translateY(200px);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bulk-actions-more {
|
||||
.button {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.button:not(:hover) {
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
.c-dropdown__button {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.c-dropdown__menu {
|
||||
bottom: 50px;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.bulk-actions-more.is-open {
|
||||
.c-dropdown__menu.u-toggle {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
&.hidden {
|
||||
transform: translateY(200px);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.footer__container {
|
||||
.button {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bulk-actions-more {
|
||||
.button {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.button:not(:hover) {
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
.c-dropdown__button {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.c-dropdown__menu {
|
||||
bottom: 50px;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.bulk-actions-more.is-open {
|
||||
.c-dropdown__menu.u-toggle {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 1.25em;
|
||||
border-radius: 4px 4px 0 0;
|
||||
margin-left: 30px;
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-right: 1.25em;
|
||||
}
|
||||
|
||||
.bulk-actions-buttons {
|
||||
border-left: 1px solid $color-grey-2;
|
||||
padding-left: 1.5em;
|
||||
|
||||
.bulk-action-btn {
|
||||
max-width: 160px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.num-objects {
|
||||
text-transform: none;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.num-objects-in-listing {
|
||||
color: $color-teal-light;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
font-family: $font-sans;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.button:not(:hover) {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer__container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 1.25em;
|
||||
border-radius: 4px 4px 0 0;
|
||||
margin-left: 30px;
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-right: 1.25em;
|
||||
}
|
||||
|
||||
.bulk-actions-buttons {
|
||||
border-left: 1px solid $color-grey-2;
|
||||
padding-left: 1.5em;
|
||||
|
||||
.bulk-action-btn {
|
||||
max-width: 160px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.num-objects {
|
||||
text-transform: none;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.num-objects-in-listing {
|
||||
color: $color-teal-light;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
font-family: $font-sans;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.button:not(:hover) {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
.button-select {
|
||||
&__option {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
&__option {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
|
||||
background-color: #fff;
|
||||
border-color: $color-teal;
|
||||
color: #262626;
|
||||
background-color: #fff;
|
||||
border-color: $color-teal;
|
||||
color: #262626;
|
||||
|
||||
&--selected {
|
||||
background-color: $color-teal;
|
||||
color: #fff;
|
||||
}
|
||||
&--selected {
|
||||
background-color: $color-teal;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-select .button-select__option {
|
||||
/* override default margin from horizontally-aligned buttons */
|
||||
margin-left: 0;
|
||||
/* override default margin from horizontally-aligned buttons */
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -9,84 +9,88 @@ overridden here? hmm.
|
|||
*/
|
||||
|
||||
.chooser {
|
||||
// We show the 'chosen' state...
|
||||
@include clearfix();
|
||||
// We show the 'chosen' state...
|
||||
@include clearfix();
|
||||
|
||||
input[type=text] {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin-right: 1em;
|
||||
input[type='text'] {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.chosen {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.unchosen,
|
||||
.chosen {
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
color: $color-grey-3;
|
||||
@include svg-icon(2.5em);
|
||||
vertical-align: middle;
|
||||
margin-right: 0.625rem;
|
||||
}
|
||||
|
||||
.chosen {
|
||||
display: block;
|
||||
// TODO: [icon-font] remove when the Wagtail icon font is removed
|
||||
&:before {
|
||||
vertical-align: middle;
|
||||
font-family: $font-wagtail-icons;
|
||||
content: '';
|
||||
// position: relative
|
||||
display: inline-block;
|
||||
// float: left;
|
||||
color: $color-grey-3;
|
||||
line-height: 1em;
|
||||
font-size: 2.5em;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.unchosen,
|
||||
.unchosen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@include clearfix;
|
||||
overflow: hidden;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
margin: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
// ...unless the .page-chooser has the 'blank' class set
|
||||
&.blank {
|
||||
.chosen {
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
color: $color-grey-3;
|
||||
@include svg-icon(2.5em);
|
||||
vertical-align: middle;
|
||||
margin-right: 0.625rem;
|
||||
}
|
||||
|
||||
// TODO: [icon-font] remove when the Wagtail icon font is removed
|
||||
&:before {
|
||||
vertical-align: middle;
|
||||
font-family: $font-wagtail-icons;
|
||||
content: '';
|
||||
// position: relative
|
||||
display: inline-block;
|
||||
// float: left;
|
||||
color: $color-grey-3;
|
||||
line-height: 1em;
|
||||
font-size: 2.5em;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
|
||||
.unchosen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@include clearfix;
|
||||
overflow: hidden;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
margin: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
// ...unless the .page-chooser has the 'blank' class set
|
||||
&.blank {
|
||||
.chosen { display: none; }
|
||||
|
||||
.unchosen { display: block; }
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// standard way of doing a chooser where the chosen object's title is overlaid
|
||||
.page-chooser,
|
||||
.snippet-chooser,
|
||||
.document-chooser {
|
||||
.chosen {
|
||||
.title {
|
||||
color: $color-grey-1;
|
||||
// display: block;
|
||||
padding-left: 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.actions {
|
||||
clear: both;
|
||||
padding-top: 0.6em;
|
||||
}
|
||||
.chosen {
|
||||
.title {
|
||||
color: $color-grey-1;
|
||||
// display: block;
|
||||
padding-left: 1em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.actions {
|
||||
clear: both;
|
||||
padding-top: 0.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [icon-font] remove when the Wagtail icon font is removed
|
||||
|
@ -94,37 +98,37 @@ overridden here? hmm.
|
|||
.snippet-chooser,
|
||||
.document-chooser,
|
||||
.image-chooser {
|
||||
.unchosen,
|
||||
.chosen {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
.unchosen,
|
||||
.chosen {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image-chooser {
|
||||
.chosen {
|
||||
padding-left: $thumbnail-width;
|
||||
.chosen {
|
||||
padding-left: $thumbnail-width;
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
float: left;
|
||||
margin-left: -($thumbnail-width);
|
||||
margin-right: 1em;
|
||||
max-width: $thumbnail-width;
|
||||
|
||||
// Resize standard Wagtail thumbnail size (165x165) to 130 for space-saving purposes.
|
||||
// We could request a 130x130 rendition, but that's just unnecessary and burdens installations
|
||||
// where images are store off-site with higher rendering times.
|
||||
img {
|
||||
max-width: $thumbnail-width;
|
||||
max-height: $thumbnail-width;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
float: left;
|
||||
margin-left: -($thumbnail-width);
|
||||
margin-right: 1em;
|
||||
max-width: $thumbnail-width;
|
||||
|
||||
// Resize standard Wagtail thumbnail size (165x165) to 130 for space-saving purposes.
|
||||
// We could request a 130x130 rendition, but that's just unnecessary and burdens installations
|
||||
// where images are store off-site with higher rendering times.
|
||||
img {
|
||||
max-width: $thumbnail-width;
|
||||
max-height: $thumbnail-width;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,113 +1,113 @@
|
|||
.comments-controls {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 2px;
|
||||
height: 42px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 2px;
|
||||
height: 42px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-right: 30px;
|
||||
height: 43px;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-right: 30px;
|
||||
height: 43px;
|
||||
}
|
||||
}
|
||||
|
||||
.comments-toggle {
|
||||
$root: &;
|
||||
float: none;
|
||||
$root: &;
|
||||
float: none;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
|
||||
// Wagtail adds some top padding to labels on mobile
|
||||
padding: 0;
|
||||
|
||||
&--active,
|
||||
&:hover {
|
||||
#{$root}__label {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#{$root}__icon {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
bottom: 3px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
color: $color-teal-dark;
|
||||
transition: color 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
&__count {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -8px;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background-color: $color-salmon;
|
||||
border: 1px solid $color-teal;
|
||||
color: $color-white;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
line-height: 17px;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-wrapper {
|
||||
width: 28px;
|
||||
height: 52px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// Wagtail adds some top padding to labels on mobile
|
||||
padding: 0;
|
||||
&__label {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
color: $color-white;
|
||||
font-weight: 400;
|
||||
margin-right: 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
&--active,
|
||||
&:hover {
|
||||
#{$root}__label {
|
||||
opacity: 1;
|
||||
}
|
||||
[type='checkbox'] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
#{$root}__icon {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
bottom: 3px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
color: $color-teal-dark;
|
||||
transition: color 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
&__count {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -8px;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background-color: $color-salmon;
|
||||
border: 1px solid $color-teal;
|
||||
color: $color-white;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
line-height: 17px;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-wrapper {
|
||||
width: 28px;
|
||||
height: 52px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
color: $color-white;
|
||||
font-weight: 400;
|
||||
margin-right: 10px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
[type=checkbox] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
[type=checkbox]:checked + &__icon {
|
||||
opacity: 1;
|
||||
}
|
||||
[type='checkbox']:checked + &__icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-notifications-toggle {
|
||||
label {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-top: 5px;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
color: $color-white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
label {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-top: 5px;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
color: $color-white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.switch__toggle {
|
||||
margin-left: 15px;
|
||||
}
|
||||
.switch__toggle {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,66 +1,66 @@
|
|||
.comment-notifications-toggle-button {
|
||||
$root: &;
|
||||
padding: 0 17px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
$root: &;
|
||||
padding: 0 17px;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
|
||||
&--active,
|
||||
&:hover {
|
||||
#{$root}__icon {
|
||||
color: $color-white;
|
||||
}
|
||||
&--active,
|
||||
&:hover {
|
||||
#{$root}__icon {
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
&--icon-toggle {
|
||||
#{$root}__icon {
|
||||
transform: rotate(180deg) translate3d(3px, 10px, 0);
|
||||
}
|
||||
&--icon-toggle {
|
||||
#{$root}__icon {
|
||||
transform: rotate(180deg) translate3d(3px, 10px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
width: 15px;
|
||||
height: 18px;
|
||||
color: $color-teal-dark;
|
||||
transition: color 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
&__icon {
|
||||
width: 15px;
|
||||
height: 18px;
|
||||
color: $color-teal-dark;
|
||||
transition: color 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.comment-notifications-dropdown {
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: -92px;
|
||||
z-index: 51;
|
||||
background-color: $color-text-base;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
min-width: 260px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid $color-text-base;
|
||||
|
||||
&__title {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
&--active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: -92px;
|
||||
z-index: 51;
|
||||
background-color: $color-text-base;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
min-width: 260px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid $color-text-base;
|
||||
|
||||
&__title {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
&--active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: 2;
|
||||
right: 18px;
|
||||
border-style: solid;
|
||||
border-width: 0 8px 8px 8px;
|
||||
border-color: transparent transparent $color-text-base transparent;
|
||||
}
|
||||
top: -8px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: 2;
|
||||
right: 18px;
|
||||
border-style: solid;
|
||||
border-width: 0 8px 8px 8px;
|
||||
border-color: transparent transparent $color-text-base transparent;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,342 +1,343 @@
|
|||
.dropdown {
|
||||
@include clearfix();
|
||||
position: relative;
|
||||
@include clearfix();
|
||||
position: relative;
|
||||
|
||||
input[type=submit],
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
button,
|
||||
.button {
|
||||
padding: 0 5em 0 1em;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
text-align: left;
|
||||
float: left;
|
||||
input[type='submit'],
|
||||
input[type='reset'],
|
||||
input[type='button'],
|
||||
button,
|
||||
.button {
|
||||
padding: 0 5em 0 1em;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
text-align: left;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.action-secondary {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
input[type='submit'],
|
||||
input[type='reset'],
|
||||
input[type='button'],
|
||||
button {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ul {
|
||||
@include unlist();
|
||||
background-color: $color-teal;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 100%;
|
||||
left: -2000px;
|
||||
z-index: 500;
|
||||
opacity: 0;
|
||||
|
||||
li {
|
||||
float: none;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0;
|
||||
overflow: hidden;
|
||||
|
||||
a:focus,
|
||||
button:focus {
|
||||
border: 3px solid $color-focus-outline;
|
||||
}
|
||||
}
|
||||
|
||||
.action-secondary {
|
||||
opacity: 0.8;
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
li {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
|
||||
li a,
|
||||
li button {
|
||||
forced-color-adjust: none;
|
||||
background-color: $color-black;
|
||||
border-color: $color-white;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
li a:focus,
|
||||
li button:focus {
|
||||
background-color: $color-black;
|
||||
forced-color-adjust: none;
|
||||
border: 4px solid #0ff;
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit],
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
a {
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
color: $color-white;
|
||||
padding: 1em;
|
||||
font-weight: normal;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-darker;
|
||||
}
|
||||
|
||||
&.icon {
|
||||
padding-right: 5em;
|
||||
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:before,
|
||||
&:after {
|
||||
right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.shortcut {
|
||||
padding-right: 7em;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
input[type='submit'],
|
||||
input[type='reset'],
|
||||
input[type='button'],
|
||||
.button,
|
||||
button {
|
||||
line-height: inherit;
|
||||
border-radius: 0;
|
||||
font-size: 0.95em;
|
||||
-webkit-font-smoothing: auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
@include unlist();
|
||||
background-color: $color-teal;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 100%;
|
||||
left: -2000px;
|
||||
z-index: 500;
|
||||
opacity: 0;
|
||||
|
||||
li {
|
||||
float: none;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0;
|
||||
overflow: hidden;
|
||||
|
||||
a:focus,
|
||||
button:focus {
|
||||
border: 3px solid $color-focus-outline;
|
||||
}
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
li {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
|
||||
li a,
|
||||
li button {
|
||||
forced-color-adjust: none;
|
||||
background-color: $color-black;
|
||||
border-color: $color-white;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
li a:focus,
|
||||
li button:focus {
|
||||
background-color: $color-black;
|
||||
forced-color-adjust: none;
|
||||
border: 4px solid #0ff;
|
||||
color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
color: $color-white;
|
||||
padding: 1em;
|
||||
font-weight: normal;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-darker;
|
||||
}
|
||||
|
||||
&.icon {
|
||||
padding-right: 5em;
|
||||
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:before,
|
||||
&:after {
|
||||
right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.shortcut {
|
||||
padding-right: 7em;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
input[type=submit],
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
.button,
|
||||
button {
|
||||
border-radius: 0;
|
||||
font-size: 0.95em;
|
||||
-webkit-font-smoothing: auto;
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 1.3em;
|
||||
}
|
||||
|
||||
.kbd {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
font-weight: 600;
|
||||
font-size: 0.8em;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 1.3em;
|
||||
}
|
||||
|
||||
&.open ul {
|
||||
box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
opacity: 1;
|
||||
left: 0;
|
||||
display: block;
|
||||
.kbd {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
font-weight: 600;
|
||||
font-size: 0.8em;
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&.open ul {
|
||||
box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
opacity: 1;
|
||||
left: 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.match-width ul {
|
||||
width: 100%;
|
||||
min-width: 110px;
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&.dropup ul {
|
||||
box-shadow: 0 -3px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
|
||||
li {
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
color: $color-white;
|
||||
text-transform: uppercase;
|
||||
background-color: $color-teal;
|
||||
line-height: 2.8em;
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.2);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 0 0.5em;
|
||||
text-align: center;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.match-width ul {
|
||||
width: 100%;
|
||||
min-width: 110px;
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:before {
|
||||
width: 1em;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
&.dropup ul {
|
||||
box-shadow: 0 -3px 3px 0 rgba(0, 0, 0, 0.2);
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
&:hover {
|
||||
background-color: $color-teal-darker;
|
||||
}
|
||||
|
||||
li {
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
svg.icon {
|
||||
// TODO: remove svg qualifier once the icon font styles are gone
|
||||
@include svg-icon(1.3em);
|
||||
}
|
||||
}
|
||||
|
||||
.bicolor + .dropdown-toggle {
|
||||
background-color: $color-teal-darker;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
}
|
||||
|
||||
&.open .dropdown-toggle {
|
||||
background-color: $color-teal-darker;
|
||||
}
|
||||
|
||||
.bicolor:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
|
||||
// Styles for dropdowns which are also buttons e.g page editor
|
||||
&.dropdown-button {
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
button {
|
||||
border-color: ActiveText;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
|
||||
a.button.bicolor.button:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
color: $color-white;
|
||||
text-transform: uppercase;
|
||||
background-color: $color-teal;
|
||||
line-height: 2.8em;
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
border-left: 1px solid rgba(255, 255, 255, 0.2);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
padding: 0 0.5em;
|
||||
text-align: center;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:before {
|
||||
width: 1em;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-darker;
|
||||
}
|
||||
|
||||
svg.icon { // TODO: remove svg qualifier once the icon font styles are gone
|
||||
@include svg-icon(1.3em);
|
||||
}
|
||||
border-radius: 0 3px 3px 0;
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid ActiveText;
|
||||
}
|
||||
}
|
||||
|
||||
.bicolor + .dropdown-toggle {
|
||||
background-color: $color-teal-darker;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
.dropdown-toggle:hover {
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
background-color: transparent;
|
||||
border: 1px solid Highlight;
|
||||
}
|
||||
}
|
||||
|
||||
&.open .dropdown-toggle {
|
||||
background-color: $color-teal-darker;
|
||||
&.open {
|
||||
> input[type='button'],
|
||||
> input[type='submit'],
|
||||
> button,
|
||||
> .button {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown-button--white {
|
||||
ul {
|
||||
background-color: $color-grey-3;
|
||||
}
|
||||
|
||||
.bicolor:hover {
|
||||
background-color: $color-teal-dark;
|
||||
li a,
|
||||
li .button {
|
||||
background-color: $color-white;
|
||||
color: $color-button;
|
||||
border: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-grey-4;
|
||||
}
|
||||
|
||||
&.no {
|
||||
color: $color-button-no;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
color: $color-button-warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Styles for dropdowns which are also buttons e.g page editor
|
||||
&.dropdown-button {
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
button {
|
||||
border-color: ActiveText;
|
||||
}
|
||||
&.dropup.dropdown-button {
|
||||
&.open {
|
||||
> input[type='button'],
|
||||
> input[type='submit'],
|
||||
> button,
|
||||
> .button {
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
|
||||
a.button.bicolor.button:hover {
|
||||
border-color: Highlight;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 3px 3px 0;
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid ActiveText;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle:hover {
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
background-color: transparent;
|
||||
border: 1px solid Highlight;
|
||||
}
|
||||
}
|
||||
|
||||
&.open {
|
||||
> input[type=button],
|
||||
> input[type=submit],
|
||||
> button,
|
||||
> .button {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 3px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown-button--white {
|
||||
ul {
|
||||
background-color: $color-grey-3;
|
||||
}
|
||||
|
||||
li a,
|
||||
li .button {
|
||||
background-color: $color-white;
|
||||
color: $color-button;
|
||||
border: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-grey-4;
|
||||
}
|
||||
|
||||
&.no {
|
||||
color: $color-button-no;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
color: $color-button-warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dropup.dropdown-button {
|
||||
&.open {
|
||||
> input[type=button],
|
||||
> input[type=submit],
|
||||
> button,
|
||||
> .button {
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 0 3px;
|
||||
}
|
||||
}
|
||||
.dropdown-toggle {
|
||||
border-radius: 0 0 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.white {
|
||||
ul {
|
||||
background-color: $color-white;
|
||||
ul {
|
||||
background-color: $color-white;
|
||||
|
||||
li {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
a {
|
||||
color: $color-grey-2;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-grey-3;
|
||||
}
|
||||
}
|
||||
li {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
a {
|
||||
color: $color-grey-2;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-grey-3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.warning {
|
||||
ul {
|
||||
background-color: $color-button-warning;
|
||||
}
|
||||
ul {
|
||||
background-color: $color-button-warning;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
background-color: $color-button-warning;
|
||||
.dropdown-toggle {
|
||||
background-color: $color-button-warning;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-button-warning-hover;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-button-warning-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transitions
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.dropdown ul {
|
||||
@include transition(opacity 0.2s linear);
|
||||
@include transition(opacity 0.2s linear);
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
.button svg.icon { // TODO: leave only class when iconfont styles are removed
|
||||
@include svg-icon();
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.button svg.icon {
|
||||
// TODO: leave only class when iconfont styles are removed
|
||||
@include svg-icon();
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,105 +5,105 @@
|
|||
// .c-dropdown {
|
||||
// }
|
||||
.c-dropdown__button {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.25rem;
|
||||
// Make this the same as the other buttons
|
||||
line-height: 1.85;
|
||||
border: solid 1px transparent;
|
||||
border-radius: 2px;
|
||||
font-size: 0.95em;
|
||||
cursor: pointer;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
user-select: none;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.25rem;
|
||||
// Make this the same as the other buttons
|
||||
line-height: 1.85;
|
||||
border: solid 1px transparent;
|
||||
border-radius: 2px;
|
||||
font-size: 0.95em;
|
||||
cursor: pointer;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.c-dropdown--large .c-dropdown__button {
|
||||
line-height: 2.9em;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
line-height: 2.9em;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
|
||||
.icon-site {
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
.icon-site {
|
||||
padding-right: 0.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.c-dropdown__icon {
|
||||
padding-left: 0.4rem;
|
||||
padding-right: 0.4rem;
|
||||
padding-left: 0.4rem;
|
||||
padding-right: 0.4rem;
|
||||
}
|
||||
|
||||
.c-dropdown__toggle {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.c-dropdown__togle--icon {
|
||||
&:before {
|
||||
display: none; // TODO: remove when iconfont styles are removed
|
||||
}
|
||||
&:before {
|
||||
display: none; // TODO: remove when iconfont styles are removed
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include svg-icon(1em, middle);
|
||||
}
|
||||
.icon {
|
||||
@include svg-icon(1em, middle);
|
||||
}
|
||||
|
||||
.icon-arrow-up {
|
||||
display: none;
|
||||
}
|
||||
.icon-arrow-up {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.is-open .c-dropdown__togle--icon {
|
||||
.icon-arrow-up {
|
||||
display: inline-block;
|
||||
}
|
||||
.icon-arrow-up {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.icon-arrow-down {
|
||||
display: none;
|
||||
}
|
||||
.icon-arrow-down {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.c-dropdown__menu.c-dropdown__menu {
|
||||
margin-top: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
min-width: 8rem;
|
||||
text-transform: none;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
animation: dropdownIn 0.1s ease-out backwards;
|
||||
list-style: none;
|
||||
// Override any right alignment that might've been set by a parent element
|
||||
// (such as the snippets header)
|
||||
text-align: left;
|
||||
margin-top: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
min-width: 8rem;
|
||||
text-transform: none;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
animation: dropdownIn 0.1s ease-out backwards;
|
||||
list-style: none;
|
||||
// Override any right alignment that might've been set by a parent element
|
||||
// (such as the snippets header)
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.c-dropdown__item {
|
||||
margin-bottom: 0.375rem;
|
||||
font-size: 0.8rem;
|
||||
margin-bottom: 0.375rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&:hover {
|
||||
.c-dropdown__indicator {
|
||||
opacity: 0.6;
|
||||
}
|
||||
&:hover {
|
||||
.c-dropdown__indicator {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-dropdown__item:last-child {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.c-dropdown__divider {
|
||||
border-color: #555;
|
||||
border-style: dotted;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-color: #555;
|
||||
border-style: dotted;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
@keyframes dropdownIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,167 +1,167 @@
|
|||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
|
||||
.footer {
|
||||
$border-curvature: 3px;
|
||||
@include transition(bottom 0.5s ease 1s);
|
||||
@include row();
|
||||
$border-curvature: 3px;
|
||||
@include transition(bottom 0.5s ease 1s);
|
||||
@include row();
|
||||
|
||||
ul {
|
||||
@include unlist();
|
||||
ul {
|
||||
@include unlist();
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
|
||||
.dropdown li, // dropdown li
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
border-radius: $border-curvature $border-curvature 0 0;
|
||||
background: $color-grey-1;
|
||||
color: $color-white;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
transition: transform 1s;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
box-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&.footer__container--hidden {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown li, // dropdown li
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&__save-warning {
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: -0.2em 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__emphasise-span-tags span {
|
||||
color: $color-orange;
|
||||
}
|
||||
|
||||
.actions {
|
||||
width: 250px;
|
||||
|
||||
&--primary {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
input[type='submit'],
|
||||
input[type='reset'],
|
||||
input[type='button'],
|
||||
button,
|
||||
.button {
|
||||
padding-right: 2.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview .dropdown {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
float: right;
|
||||
text-align: right;
|
||||
padding: 7px math.div($grid-gutter-width, 2);
|
||||
font-size: 0.85em;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
margin-right: $grid-gutter-width;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
.actions,
|
||||
.preview,
|
||||
&__container,
|
||||
.preview .dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
margin-top: $mobile-nice-padding;
|
||||
|
||||
.meta {
|
||||
p {
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
border-radius: $border-curvature $border-curvature 0 0;
|
||||
background: $color-grey-1;
|
||||
color: $color-white;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
transition: transform 1s;
|
||||
&:not(:first-child) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
box-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&.footer__container--hidden {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
li {
|
||||
margin-right: 1em;
|
||||
}
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__save-warning {
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 1.2em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: calc(#{$desktop-nice-padding} - 0.75em);
|
||||
margin-right: $desktop-nice-padding;
|
||||
width: auto;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
|
||||
p {
|
||||
margin: -0.2em 0 0 0;
|
||||
}
|
||||
> ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__emphasise-span-tags span {
|
||||
color: $color-orange;
|
||||
&__container {
|
||||
padding: 0.75em;
|
||||
margin-right: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: -$border-curvature;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
width: 250px;
|
||||
|
||||
&--primary {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
input[type=submit],
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
button,
|
||||
.button {
|
||||
padding-right: 2.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview .dropdown {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.meta {
|
||||
float: right;
|
||||
text-align: right;
|
||||
padding: 7px math.div($grid-gutter-width, 2);
|
||||
font-size: 0.85em;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
margin-right: $grid-gutter-width;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
.actions,
|
||||
.preview,
|
||||
&__container,
|
||||
.preview .dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
margin-top: $mobile-nice-padding;
|
||||
|
||||
.meta {
|
||||
p {
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
&:not(:first-child) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__save-warning {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: calc(#{$desktop-nice-padding} - 0.75em);
|
||||
margin-right: $desktop-nice-padding;
|
||||
width: auto;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
|
||||
> ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&__container {
|
||||
padding: 0.75em;
|
||||
margin-right: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: -$border-curvature;
|
||||
}
|
||||
}
|
||||
|
||||
&__save-warning {
|
||||
margin-right: 50px;
|
||||
}
|
||||
&__save-warning {
|
||||
margin-right: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use "sass:map";
|
||||
@use "sass:math";
|
||||
@use 'sass:map';
|
||||
@use 'sass:math';
|
||||
// stylelint-disable scss/comment-no-empty
|
||||
// These are the generic stylings for forms of any type.
|
||||
// If you're styling something specific to the page editing interface,
|
||||
|
@ -26,12 +26,12 @@
|
|||
// }
|
||||
// }
|
||||
.plain-checkbox-label {
|
||||
// cancel heavy / floated label styles, for labels that should appear inline against checkboxes
|
||||
// cancel heavy / floated label styles, for labels that should appear inline against checkboxes
|
||||
|
||||
float: none;
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
font-size: inherit;
|
||||
float: none;
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
// TODO: mixin,
|
||||
|
@ -76,7 +76,7 @@
|
|||
|
||||
// Reset the arrow on `<select>`s in IE10+.
|
||||
select::-ms-expand {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
// select boxes
|
||||
|
@ -84,67 +84,67 @@ select::-ms-expand {
|
|||
.choice_field .input,
|
||||
.model_choice_field .input,
|
||||
.typed_choice_field .input {
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
// Add select arrow back on browsers where native ui has been removed
|
||||
select ~ span:after {
|
||||
border-radius: 0 6px 6px 0;
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 1px;
|
||||
bottom: 0;
|
||||
width: 1.5em;
|
||||
font-family: $font-wagtail-icons;
|
||||
content: map.get($icons, 'arrow-down');
|
||||
border: 1px solid $color-input-border;
|
||||
border-width: 0 0 0 1px;
|
||||
text-align: center;
|
||||
line-height: 1.4em;
|
||||
font-size: 3em;
|
||||
pointer-events: none;
|
||||
color: $color-grey-3;
|
||||
margin: 0 1px 1px 0;
|
||||
// Add select arrow back on browsers where native ui has been removed
|
||||
select ~ span:after {
|
||||
border-radius: 0 6px 6px 0;
|
||||
z-index: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 1px;
|
||||
bottom: 0;
|
||||
width: 1.5em;
|
||||
font-family: $font-wagtail-icons;
|
||||
content: map.get($icons, 'arrow-down');
|
||||
border: 1px solid $color-input-border;
|
||||
border-width: 0 0 0 1px;
|
||||
text-align: center;
|
||||
line-height: 1.4em;
|
||||
font-size: 3em;
|
||||
pointer-events: none;
|
||||
color: $color-grey-3;
|
||||
margin: 0 1px 1px 0;
|
||||
|
||||
.ie & {
|
||||
display: none;
|
||||
}
|
||||
.ie & {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Override default select padding so the chevron will overlap with long option text
|
||||
select {
|
||||
padding-right: 5em;
|
||||
}
|
||||
// Override default select padding so the chevron will overlap with long option text
|
||||
select {
|
||||
padding-right: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
// Other text
|
||||
.help,
|
||||
.error-message {
|
||||
border: 1px solid transparent; // ensure visible separation in Windows High Contrast mode
|
||||
font-size: 0.85em;
|
||||
font-weight: normal;
|
||||
margin: 0.5em 0 0;
|
||||
border: 1px solid transparent; // ensure visible separation in Windows High Contrast mode
|
||||
font-size: 0.85em;
|
||||
font-weight: normal;
|
||||
margin: 0.5em 0 0;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
color: $color-text-error;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
color: $color-text-error;
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
forced-color-adjust: none;
|
||||
color: $color-text-error-forced-color;
|
||||
}
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
forced-color-adjust: none;
|
||||
color: $color-text-error-forced-color;
|
||||
}
|
||||
|
||||
&::before {
|
||||
font-family: $font-wagtail-icons;
|
||||
vertical-align: -10%;
|
||||
content: map.get($icons, 'cross');
|
||||
}
|
||||
&::before {
|
||||
font-family: $font-wagtail-icons;
|
||||
vertical-align: -10%;
|
||||
content: map.get($icons, 'cross');
|
||||
}
|
||||
}
|
||||
|
||||
.help {
|
||||
color: $color-grey-2;
|
||||
color: $color-grey-2;
|
||||
}
|
||||
|
||||
fieldset:hover > .help,
|
||||
|
@ -152,26 +152,26 @@ fieldset:hover > .help,
|
|||
.field:focus + .help,
|
||||
.field:hover + .help,
|
||||
li.focused > .help {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.required .field > label:after,
|
||||
label.required:after {
|
||||
content: '*';
|
||||
color: $color-red;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
line-height: 1em;
|
||||
font-size: 13px;
|
||||
content: '*';
|
||||
color: $color-red;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
line-height: 1em;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.error input,
|
||||
.error textarea,
|
||||
.error select,
|
||||
.error .tagit {
|
||||
border-color: $color-red;
|
||||
background-color: $color-input-error-bg;
|
||||
border-color: $color-red;
|
||||
background-color: $color-input-error-bg;
|
||||
}
|
||||
|
||||
// Layouts for particular kinds of of fields
|
||||
|
@ -179,7 +179,7 @@ label.required:after {
|
|||
// permanently show checkbox/radio help as they have no focus state
|
||||
.boolean_field .help,
|
||||
.radio .help {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// This is expected to go on the parent of the input/select/textarea
|
||||
|
@ -189,86 +189,86 @@ label.required:after {
|
|||
.time_field,
|
||||
.date_time_field,
|
||||
.url_field {
|
||||
.input {
|
||||
position: relative;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
font-family: $font-wagtail-icons;
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
line-height: 100%;
|
||||
font-size: 2em;
|
||||
color: $color-grey-3;
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0.3em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
input:not([type='radio']),
|
||||
input:not([type='checkbox']),
|
||||
input:not([type='submit']),
|
||||
input:not([type='button']) {
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
// smaller fields required slight repositioning of icons
|
||||
&.field-small {
|
||||
.input {
|
||||
position: relative;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
font-family: $font-wagtail-icons;
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
line-height: 100%;
|
||||
font-size: 2em;
|
||||
color: $color-grey-3;
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0.3em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
input:not([type=radio]),
|
||||
input:not([type=checkbox]),
|
||||
input:not([type=submit]),
|
||||
input:not([type=button]) {
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
// smaller fields required slight repositioning of icons
|
||||
&.field-small {
|
||||
.input {
|
||||
&:before,
|
||||
&:after {
|
||||
font-size: 1.3rem; // REMs are necessary here because IE doesn't treat generated content correctly
|
||||
top: 0.3em;
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special case for search spinners
|
||||
&.icon-spinner:after {
|
||||
color: $color-teal;
|
||||
opacity: 0.8;
|
||||
text-align: center;
|
||||
&:before,
|
||||
&:after {
|
||||
font-size: 1.3rem; // REMs are necessary here because IE doesn't treat generated content correctly
|
||||
top: 0.3em;
|
||||
}
|
||||
|
||||
&:before {
|
||||
left: 0.5em;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// special case for search spinners
|
||||
&.icon-spinner:after {
|
||||
color: $color-teal;
|
||||
opacity: 0.8;
|
||||
text-align: center;
|
||||
top: 0.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.date_field,
|
||||
.date_time_field {
|
||||
.input:before {
|
||||
content: map.get($icons, 'date');
|
||||
}
|
||||
.input:before {
|
||||
content: map.get($icons, 'date');
|
||||
}
|
||||
}
|
||||
|
||||
.time_field {
|
||||
.input:before {
|
||||
content: map.get($icons, 'time');
|
||||
}
|
||||
.input:before {
|
||||
content: map.get($icons, 'time');
|
||||
}
|
||||
}
|
||||
|
||||
.url_field {
|
||||
.input:before {
|
||||
content: map.get($icons, 'link');
|
||||
}
|
||||
.input:before {
|
||||
content: map.get($icons, 'link');
|
||||
}
|
||||
}
|
||||
|
||||
.daterange_field {
|
||||
input:last-of-type {
|
||||
margin-top: 1.2em; // Mirrors the label 1.2em top padding.
|
||||
}
|
||||
input:last-of-type {
|
||||
margin-top: 1.2em; // Mirrors the label 1.2em top padding.
|
||||
}
|
||||
}
|
||||
|
||||
// This is specifically for list of radios/checkboxes
|
||||
|
@ -276,301 +276,305 @@ label.required:after {
|
|||
.checkbox_select_multiple .input li,
|
||||
.multiple_choice_field .input li,
|
||||
.choice_field .input li {
|
||||
label {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: none;
|
||||
padding-top: 0; // Negates padding added to label for the group of fields as a whole
|
||||
padding-bottom: 0.8em;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: none;
|
||||
padding-top: 0; // Negates padding added to label for the group of fields as a whole
|
||||
padding-bottom: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.fields > li,
|
||||
.field-col {
|
||||
@include clearfix();
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 1.2em;
|
||||
@include clearfix();
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 1.2em;
|
||||
}
|
||||
|
||||
.field-row {
|
||||
@include clearfix();
|
||||
@include clearfix();
|
||||
|
||||
// negative margin the bottom so it doesn't add too much space
|
||||
margin-bottom: -1.2em;
|
||||
// negative margin the bottom so it doesn't add too much space
|
||||
margin-bottom: -1.2em;
|
||||
}
|
||||
|
||||
.input {
|
||||
clear: both;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// field sizing and alignment
|
||||
.field-small {
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
.halloeditor,
|
||||
.tagit {
|
||||
border-radius: 3px;
|
||||
padding: 0.4em 1em;
|
||||
}
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
.halloeditor,
|
||||
.tagit {
|
||||
border-radius: 3px;
|
||||
padding: 0.4em 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.field {
|
||||
&.col1,
|
||||
&.col2,
|
||||
&.col3,
|
||||
&.col4,
|
||||
&.col5,
|
||||
&.col6,
|
||||
&.col7,
|
||||
&.col8,
|
||||
&.col9,
|
||||
&.col10,
|
||||
&.col11,
|
||||
&.col12 { clear: both;}
|
||||
&.col1,
|
||||
&.col2,
|
||||
&.col3,
|
||||
&.col4,
|
||||
&.col5,
|
||||
&.col6,
|
||||
&.col7,
|
||||
&.col8,
|
||||
&.col9,
|
||||
&.col10,
|
||||
&.col11,
|
||||
&.col12 {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
li.inline .field {
|
||||
&.col1,
|
||||
&.col2,
|
||||
&.col3,
|
||||
&.col4,
|
||||
&.col5,
|
||||
&.col6,
|
||||
&.col7,
|
||||
&.col8,
|
||||
&.col9,
|
||||
&.col10,
|
||||
&.col11,
|
||||
&.col12 { clear: none;}
|
||||
&.col1,
|
||||
&.col2,
|
||||
&.col3,
|
||||
&.col4,
|
||||
&.col5,
|
||||
&.col6,
|
||||
&.col7,
|
||||
&.col8,
|
||||
&.col9,
|
||||
&.col10,
|
||||
&.col11,
|
||||
&.col12 {
|
||||
clear: none;
|
||||
}
|
||||
}
|
||||
|
||||
// solve gutter issues of inline fields
|
||||
ul.inline li:first-child,
|
||||
li.inline:first-child {
|
||||
margin-left: math.div(-$grid-gutter-width, 2);
|
||||
margin-left: math.div(-$grid-gutter-width, 2);
|
||||
}
|
||||
|
||||
// search-bars
|
||||
.search-bar {
|
||||
.required .field > label:after {
|
||||
display: none;
|
||||
}
|
||||
.required .field > label:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-filter {
|
||||
height: 2.71em;
|
||||
border-color: transparent;
|
||||
}
|
||||
.button-filter {
|
||||
height: 2.71em;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// file drop zones
|
||||
.drop-zone {
|
||||
border-radius: 5px;
|
||||
border: 2px dashed $color-grey-4;
|
||||
padding: $mobile-nice-padding;
|
||||
background-color: $color-grey-5;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
border: 2px dashed $color-grey-4;
|
||||
padding: $mobile-nice-padding;
|
||||
background-color: $color-grey-5;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
|
||||
.drop-zone-help {
|
||||
border: 0;
|
||||
}
|
||||
.drop-zone-help {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.hovered {
|
||||
border-color: $color-teal;
|
||||
background-color: $color-input-focus;
|
||||
}
|
||||
&.hovered {
|
||||
border-color: $color-teal;
|
||||
background-color: $color-input-focus;
|
||||
}
|
||||
}
|
||||
|
||||
// Transitions
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.help {
|
||||
@include transition(opacity 0.2s ease);
|
||||
@include transition(opacity 0.2s ease);
|
||||
}
|
||||
|
||||
.label-uppercase {
|
||||
.field > label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.field > label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.help {
|
||||
opacity: 1;
|
||||
}
|
||||
.help {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fields {
|
||||
max-width: 800px;
|
||||
}
|
||||
.fields {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.field {
|
||||
@include row();
|
||||
}
|
||||
.field {
|
||||
@include row();
|
||||
}
|
||||
|
||||
.field-content {
|
||||
@include column(10, 0);
|
||||
}
|
||||
.field-content {
|
||||
@include column(10, 0);
|
||||
}
|
||||
|
||||
.field-col {
|
||||
float: left;
|
||||
.field-col {
|
||||
float: left;
|
||||
padding-left: 0;
|
||||
|
||||
// anything less than 4 columns or greater than 6 is impractical
|
||||
&.col4 {
|
||||
label {
|
||||
@include column(2, 0, 4);
|
||||
}
|
||||
|
||||
.field-content {
|
||||
@include column(2, $padding, 4);
|
||||
padding-left: 0;
|
||||
|
||||
// anything less than 4 columns or greater than 6 is impractical
|
||||
&.col4 {
|
||||
label {
|
||||
@include column(2, 0, 4);
|
||||
}
|
||||
|
||||
.field-content {
|
||||
@include column(2, $padding, 4);
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.col6 {
|
||||
label {
|
||||
@include column(2, 0, 6);
|
||||
}
|
||||
|
||||
.field-content {
|
||||
@include column(4, $padding, 6);
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-above {
|
||||
.field > label,
|
||||
.field > .field-content {
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
&.col6 {
|
||||
label {
|
||||
@include column(2, 0, 6);
|
||||
}
|
||||
|
||||
.field-content {
|
||||
@include column(4, $padding, 6);
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-above {
|
||||
.field > label,
|
||||
.field > .field-content {
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-comment-control {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-content--comments-enabled {
|
||||
.field {
|
||||
position: relative;
|
||||
.field {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.field-content {
|
||||
padding-right: 45px;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-right: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-draftail_rich_text_area .field-content {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.field-comment-control {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
|
||||
&--object {
|
||||
right: 20px;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
right: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
.field-content {
|
||||
padding-right: 45px;
|
||||
button {
|
||||
@include transition(opacity 0.2s ease);
|
||||
border: 0;
|
||||
background: none;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-right: 60px;
|
||||
}
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.widget-draftail_rich_text_area .field-content {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.field-comment-control {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
// Hide by default, reveal on hover of parent
|
||||
@include media-breakpoint-up(md) {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(-50%);
|
||||
right: 0;
|
||||
height: 100%;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
&--object {
|
||||
right: 20px;
|
||||
.icon-reversed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
right: 350px;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
// stylelint-disable max-nesting-depth
|
||||
.icon-default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
@include transition(opacity 0.2s ease);
|
||||
border: 0;
|
||||
background: none;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border-radius: 3px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
// Hide by default, reveal on hover of parent
|
||||
@include media-breakpoint-up(md) {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(-50%);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.icon-reversed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
// stylelint-disable max-nesting-depth
|
||||
.icon-default {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-reversed {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
> svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
color: $color-teal;
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
color: ButtonText;
|
||||
border: 1px solid;
|
||||
}
|
||||
}
|
||||
.icon-reversed {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.field-row .field-comment-control {
|
||||
top: 0;
|
||||
}
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
.field:not(.block_field) {
|
||||
&:hover {
|
||||
.field-comment-control button {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
> svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
color: $color-teal;
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
color: ButtonText;
|
||||
border: 1px solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.object {
|
||||
&:hover {
|
||||
.field-comment-control--object button {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
.field-row .field-comment-control {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.object.model_choice_field {
|
||||
.object-help {
|
||||
right: 153px;
|
||||
}
|
||||
.field:not(.block_field) {
|
||||
&:hover {
|
||||
.field-comment-control button {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.object {
|
||||
&:hover {
|
||||
.field-comment-control--object button {
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.object.model_choice_field {
|
||||
.object-help {
|
||||
right: 153px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,101 +1,101 @@
|
|||
.wrapper {
|
||||
@include clearfix();
|
||||
height: 100vh;
|
||||
transition: transform 0.2s ease;
|
||||
@include clearfix();
|
||||
height: 100vh;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%; // this has no effect on desktop, but on mobile it helps aesthetics of menu popout action
|
||||
float: left;
|
||||
position: relative;
|
||||
background-color: $color-grey-4;
|
||||
border-bottom: 1px solid $color-grey-3;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%; // this has no effect on desktop, but on mobile it helps aesthetics of menu popout action
|
||||
float: left;
|
||||
position: relative;
|
||||
background-color: $color-grey-4;
|
||||
border-bottom: 1px solid $color-grey-3;
|
||||
}
|
||||
|
||||
.content {
|
||||
@include row();
|
||||
background: $color-white;
|
||||
border-top: 0 solid $color-grey-5; // this top border provides space for the floating logo to toggle the menu
|
||||
min-height: 100%;
|
||||
position: relative; // yuk. necessary for positions for jquery ui widgets
|
||||
@include row();
|
||||
background: $color-white;
|
||||
border-top: 0 solid $color-grey-5; // this top border provides space for the floating logo to toggle the menu
|
||||
min-height: 100%;
|
||||
position: relative; // yuk. necessary for positions for jquery ui widgets
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.content-wrapper {
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
.content-wrapper {
|
||||
border-bottom-right-radius: 5px;
|
||||
}
|
||||
|
||||
.content {
|
||||
border-top: 0;
|
||||
background-color: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
.content {
|
||||
border-top: 0;
|
||||
background-color: none;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
@include clearfix();
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.col1 {
|
||||
@include column(1);
|
||||
}
|
||||
.col1 {
|
||||
@include column(1);
|
||||
}
|
||||
|
||||
.col2 {
|
||||
@include column(2);
|
||||
}
|
||||
.col2 {
|
||||
@include column(2);
|
||||
}
|
||||
|
||||
.col3 {
|
||||
@include column(3);
|
||||
}
|
||||
.col3 {
|
||||
@include column(3);
|
||||
}
|
||||
|
||||
.col4 {
|
||||
@include column(4);
|
||||
}
|
||||
.col4 {
|
||||
@include column(4);
|
||||
}
|
||||
|
||||
.col5 {
|
||||
@include column(5);
|
||||
}
|
||||
.col5 {
|
||||
@include column(5);
|
||||
}
|
||||
|
||||
.col6 {
|
||||
@include column(6);
|
||||
}
|
||||
.col6 {
|
||||
@include column(6);
|
||||
}
|
||||
|
||||
.col7 {
|
||||
@include column(7);
|
||||
}
|
||||
.col7 {
|
||||
@include column(7);
|
||||
}
|
||||
|
||||
.col8 {
|
||||
@include column(8);
|
||||
}
|
||||
.col8 {
|
||||
@include column(8);
|
||||
}
|
||||
|
||||
.col9 {
|
||||
@include column(9);
|
||||
}
|
||||
.col9 {
|
||||
@include column(9);
|
||||
}
|
||||
|
||||
.col10 {
|
||||
@include column(10);
|
||||
}
|
||||
.col10 {
|
||||
@include column(10);
|
||||
}
|
||||
|
||||
.col11 {
|
||||
@include column(11);
|
||||
}
|
||||
.col11 {
|
||||
@include column(11);
|
||||
}
|
||||
|
||||
.col12 {
|
||||
@include column(12);
|
||||
}
|
||||
.col12 {
|
||||
@include column(12);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include row();
|
||||
}
|
||||
.row {
|
||||
@include row();
|
||||
}
|
||||
|
||||
.row-flush {
|
||||
@include row-flush();
|
||||
}
|
||||
.row-flush {
|
||||
@include row-flush();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,236 +1,238 @@
|
|||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
|
||||
header {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
background-color: $color-header-bg;
|
||||
margin-bottom: 2em;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
background-color: $color-header-bg;
|
||||
margin-bottom: 2em;
|
||||
color: $color-white;
|
||||
|
||||
a {
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin: 0;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 0.2em 0;
|
||||
|
||||
&.icon:before {
|
||||
width: 1em;
|
||||
display: none;
|
||||
margin-right: 0.4em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
float: left;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
|
||||
.hasform &:first-child {
|
||||
padding-bottom: 0.5em;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
// For case where content below header should merge with it
|
||||
&.merged {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.tab-merged {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
|
||||
.right:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
.breadcrumb {
|
||||
padding-left: calc(#{$desktop-nice-padding} - 8px);
|
||||
}
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
.breadcrumb {
|
||||
margin-left: -$desktop-nice-padding;
|
||||
margin-right: -$desktop-nice-padding;
|
||||
padding-left: math.div($desktop-nice-padding, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.header-with-breadcrumb {
|
||||
padding-top: 0;
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 1rem;
|
||||
padding-left: math.div(
|
||||
$desktop-nice-padding,
|
||||
2
|
||||
); // rather than padding-left: revert;
|
||||
}
|
||||
}
|
||||
|
||||
&.tab-merged,
|
||||
&.no-border {
|
||||
border: 0;
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
// To all hamburger menu to be visible
|
||||
padding-left: 1.6em;
|
||||
padding-right: 1.6em;
|
||||
padding-top: 11px;
|
||||
|
||||
.nice-padding {
|
||||
margin-left: -$desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.merged.no-border {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.no-v-padding {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: $color-teal-darker;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
@include visuallyhidden();
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
select {
|
||||
border-width: 0;
|
||||
|
||||
&:focus {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.fields {
|
||||
margin-top: -0.5em;
|
||||
|
||||
li {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.field-content {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.last-updated {
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
.avatar.small {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
margin: 0;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 0.2em 0;
|
||||
|
||||
&.icon:before {
|
||||
width: 1em;
|
||||
display: none;
|
||||
margin-right: 0.4em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
float: left;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
|
||||
.hasform &:first-child {
|
||||
padding-bottom: 0.5em;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
// For case where content below header should merge with it
|
||||
&.merged {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.tab-merged {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
|
||||
.right:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
.breadcrumb {
|
||||
padding-left: calc(#{$desktop-nice-padding} - 8px);
|
||||
}
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
.breadcrumb {
|
||||
margin-left: -$desktop-nice-padding;
|
||||
margin-right: -$desktop-nice-padding;
|
||||
padding-left: math.div($desktop-nice-padding, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.header-with-breadcrumb {
|
||||
padding-top: 0;
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 1rem;
|
||||
padding-left: math.div($desktop-nice-padding, 2); // rather than padding-left: revert;
|
||||
}
|
||||
}
|
||||
|
||||
&.tab-merged,
|
||||
&.no-border {
|
||||
border: 0;
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
// To all hamburger menu to be visible
|
||||
padding-left: 1.6em;
|
||||
padding-right: 1.6em;
|
||||
padding-top: 11px;
|
||||
|
||||
.nice-padding {
|
||||
margin-left: -$desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.merged.no-border {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.no-v-padding {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: $color-teal-darker;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
@include visuallyhidden();
|
||||
}
|
||||
|
||||
input[type=text],
|
||||
select {
|
||||
border-width: 0;
|
||||
|
||||
&:focus {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.fields {
|
||||
margin-top: -0.5em;
|
||||
|
||||
li {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.field-content {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.last-updated {
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
.avatar.small {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
}
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
header .field-content {
|
||||
border: 0.1em solid $system-color-link-text;
|
||||
}
|
||||
header .field-content {
|
||||
border: 0.1em solid $system-color-link-text;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
header {
|
||||
padding-top: 1.5em;
|
||||
padding-bottom: 1.5em;
|
||||
header {
|
||||
padding-top: 1.5em;
|
||||
padding-bottom: 1.5em;
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
margin-right: 0;
|
||||
.left {
|
||||
float: left;
|
||||
margin-right: 0;
|
||||
|
||||
&:first-child {
|
||||
padding-bottom: 0;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.second {
|
||||
clear: none;
|
||||
|
||||
.right,
|
||||
.left {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
h1.icon:before {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.col3 {
|
||||
@include column(3);
|
||||
}
|
||||
|
||||
.col3.actionbutton {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.col6 {
|
||||
@include column(6);
|
||||
}
|
||||
|
||||
.col9 {
|
||||
@include column(9);
|
||||
}
|
||||
&:first-child {
|
||||
padding-bottom: 0;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.second {
|
||||
clear: none;
|
||||
|
||||
.right,
|
||||
.left {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
h1.icon:before {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.col3 {
|
||||
@include column(3);
|
||||
}
|
||||
|
||||
.col3.actionbutton {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.col6 {
|
||||
@include column(6);
|
||||
}
|
||||
|
||||
.col9 {
|
||||
@include column(9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@include media-breakpoint-down(xs) {
|
||||
padding-left: $mobile-nav-indent;
|
||||
}
|
||||
@include media-breakpoint-down(xs) {
|
||||
padding-left: $mobile-nav-indent;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
@include svg-icon();
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
&-icon {
|
||||
@include svg-icon();
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,88 +1,88 @@
|
|||
@use "sass:color";
|
||||
@use "sass:map";
|
||||
@use 'sass:color';
|
||||
@use 'sass:map';
|
||||
// Help text formatters
|
||||
.help-block {
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
clear: both;
|
||||
color: $color-text-base;
|
||||
padding: 1em;
|
||||
margin: 1em 0;
|
||||
clear: both;
|
||||
color: $color-text-base;
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
p {
|
||||
margin-top: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $color-link;
|
||||
}
|
||||
a {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
|
||||
.help-info,
|
||||
.help-warning,
|
||||
.help-critical {
|
||||
border-radius: 3px;
|
||||
padding-left: 3.5em;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
padding-left: 3.5em;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
position: absolute;
|
||||
left: 1em;
|
||||
top: 0.7em;
|
||||
content: map.get($icons, 'help');
|
||||
font-size: 1.4em;
|
||||
}
|
||||
&:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
position: absolute;
|
||||
left: 1em;
|
||||
top: 0.7em;
|
||||
content: map.get($icons, 'help');
|
||||
font-size: 1.4em;
|
||||
}
|
||||
}
|
||||
|
||||
.help-info {
|
||||
background-color: color.adjust($color-blue, $lightness: 30%);
|
||||
background-color: color.adjust($color-blue, $lightness: 30%);
|
||||
|
||||
&:before {
|
||||
color: $color-blue;
|
||||
}
|
||||
&:before {
|
||||
color: $color-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.help-warning {
|
||||
background-color: color.adjust($color-orange, $lightness: 30%);
|
||||
background-color: color.adjust($color-orange, $lightness: 30%);
|
||||
|
||||
&:before {
|
||||
color: $color-orange;
|
||||
content: map.get($icons, 'warning');
|
||||
}
|
||||
&:before {
|
||||
color: $color-orange;
|
||||
content: map.get($icons, 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
.help-critical {
|
||||
background-color: color.adjust($color-red, $lightness: 40%);
|
||||
background-color: color.adjust($color-red, $lightness: 40%);
|
||||
|
||||
&:before {
|
||||
color: $color-red;
|
||||
content: map.get($icons, 'warning');
|
||||
}
|
||||
&:before {
|
||||
color: $color-red;
|
||||
content: map.get($icons, 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
.help-block {
|
||||
forced-color-adjust: none;
|
||||
border: 1px solid $system-color-link-text; // ensure visible separation in Windows High Contrast mode
|
||||
background-color: transparent;
|
||||
color: $color-white;
|
||||
.help-block {
|
||||
forced-color-adjust: none;
|
||||
border: 1px solid $system-color-link-text; // ensure visible separation in Windows High Contrast mode
|
||||
background-color: transparent;
|
||||
color: $color-white;
|
||||
|
||||
&:before {
|
||||
color: currentColor;
|
||||
}
|
||||
&:before {
|
||||
color: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
.help-warning {
|
||||
color: $color-text-warning-forced-color;
|
||||
border-color: $color-text-warning-forced-color;
|
||||
}
|
||||
.help-warning {
|
||||
color: $color-text-warning-forced-color;
|
||||
border-color: $color-text-warning-forced-color;
|
||||
}
|
||||
|
||||
.help-critical {
|
||||
color: $color-text-error-forced-color;
|
||||
border-color: $color-text-error-forced-color;
|
||||
}
|
||||
.help-critical {
|
||||
color: $color-text-error-forced-color;
|
||||
border-color: $color-text-error-forced-color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Displays 'timesince' formatted date with full date on hover
|
||||
.human-readable-date {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: none;
|
||||
content: attr(title);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
visibility: hidden;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
display: none;
|
||||
content: attr(title);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
visibility: hidden;
|
||||
|
||||
&:before {
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
visibility: visible;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
@use "sass:string";
|
||||
@use 'sass:string';
|
||||
|
||||
@font-face {
|
||||
font-family: 'wagtail';
|
||||
src: url('../fonts/wagtail.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'wagtail';
|
||||
src: url('../fonts/wagtail.woff') format('woff');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
// Set SVG icons to use the current text color in the location they appear as
|
||||
// their default fill color. Can be overridden for a specific icon by either
|
||||
// the color or fill properties.
|
||||
.icon {
|
||||
fill: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.icon.teal {
|
||||
color: $color-teal;
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
.icon.white {
|
||||
color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.icon:before,
|
||||
|
@ -27,14 +27,14 @@
|
|||
.hallotoolbar [class^='icon-'],
|
||||
.hallotoolbar [class*=' icon-']:before,
|
||||
.hallotoolbar [class^='icon-']:before {
|
||||
@include icon(); // from _mixins.scss
|
||||
@include icon(); // from _mixins.scss
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.icon:after,
|
||||
.hallotoolbar [class^='icon-']:after,
|
||||
.hallotoolbar [class^='icon-']:after {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
|
@ -42,150 +42,149 @@
|
|||
.hallotoolbar [class*=' icon-']:before,
|
||||
.hallotoolbar [class*=' icon-']:before,
|
||||
.hallotoolbar [class^='icon-']:before {
|
||||
vertical-align: -10%;
|
||||
margin-right: 0;
|
||||
vertical-align: -10%;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Icon factory methods
|
||||
// =============================================================================
|
||||
|
||||
@each $icon, $content in $icons {
|
||||
.icon-#{$icon}:before {
|
||||
content: string.quote(#{$content});
|
||||
}
|
||||
.icon-#{$icon}:before {
|
||||
content: string.quote(#{$content});
|
||||
}
|
||||
}
|
||||
|
||||
@each $icon, $content in $icons-after {
|
||||
.icon-#{$icon}:after {
|
||||
content: string.quote(#{$content});
|
||||
}
|
||||
.icon-#{$icon}:after {
|
||||
content: string.quote(#{$content});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Custom config for various icons
|
||||
// =============================================================================
|
||||
.icon-download {
|
||||
// Credit: Icon made by Freepik from Flaticon.com
|
||||
// Credit: Icon made by Freepik from Flaticon.com
|
||||
}
|
||||
|
||||
.icon-view:before,
|
||||
.icon-no-view:before { // icon-font
|
||||
vertical-align: -3.5px;
|
||||
font-size: 1.1rem;
|
||||
.icon-no-view:before {
|
||||
// icon-font
|
||||
vertical-align: -3.5px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.icon-spinner:after,
|
||||
.icon-spinner:before { // iconfont
|
||||
width: 1em;
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
display: inline-block;
|
||||
.icon-spinner:before {
|
||||
// iconfont
|
||||
width: 1em;
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
svg.icon-spinner { // TODO: leave only class when iconfont styles are removed
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
svg.icon-spinner {
|
||||
// TODO: leave only class when iconfont styles are removed
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
}
|
||||
|
||||
.icon-horizontalrule:before {
|
||||
font-family: $font-sans;
|
||||
font-family: $font-sans;
|
||||
}
|
||||
|
||||
|
||||
.icon-larger:before {
|
||||
font-size: 1.5em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.icon.text-replace { // iconfont
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
.icon.text-replace {
|
||||
// iconfont
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&:before {
|
||||
margin-right: 0;
|
||||
font-size: 1rem;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
line-height: 1.2em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&:before {
|
||||
margin-right: 0;
|
||||
font-size: 1rem;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
line-height: 1.2em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.text-replace {
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.icon {
|
||||
@include svg-icon(1rem, middle);
|
||||
}
|
||||
.icon {
|
||||
@include svg-icon(1rem, middle);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin-wag {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.icon-spinner:after {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// CSS-only circled question mark.
|
||||
// <span class="icon-help-inverse" aria-hidden="true"></span>
|
||||
.icon-help-inverse {
|
||||
$size: 15px;
|
||||
$size: 15px;
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
width: $size;
|
||||
height: $size;
|
||||
line-height: $size;
|
||||
font-size: 1.1em;
|
||||
text-align: center;
|
||||
border-radius: 100%;
|
||||
color: $color-grey-2;
|
||||
border: 1px solid currentColor;
|
||||
}
|
||||
&:before {
|
||||
display: inline-block;
|
||||
width: $size;
|
||||
height: $size;
|
||||
line-height: $size;
|
||||
font-size: 1.1em;
|
||||
text-align: center;
|
||||
border-radius: 100%;
|
||||
color: $color-grey-2;
|
||||
border: 1px solid currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
// stylelint-disable-next-line no-duplicate-selectors
|
||||
.icon {
|
||||
&.initial {
|
||||
@include svg-icon(1em);
|
||||
vertical-align: initial;
|
||||
}
|
||||
&.initial {
|
||||
@include svg-icon(1em);
|
||||
vertical-align: initial;
|
||||
}
|
||||
|
||||
&.default {
|
||||
@include svg-icon(1.5em);
|
||||
}
|
||||
&.default {
|
||||
@include svg-icon(1.5em);
|
||||
}
|
||||
|
||||
&--flipped {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
&--flipped {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
|
||||
.icon.locale-error {
|
||||
vertical-align: text-top;
|
||||
margin-right: 0.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
color: $color-red;
|
||||
vertical-align: text-top;
|
||||
margin-right: 0.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
color: $color-red;
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast mode
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
.icon {
|
||||
fill: $system-color-link-text;
|
||||
}
|
||||
.icon {
|
||||
fill: $system-color-link-text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
// =============================================================================
|
||||
// Indicator light
|
||||
// =============================================================================
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Indicator light
|
||||
// =============================================================================
|
||||
|
@ -11,47 +10,47 @@ $c-indicator-size: 0.625em;
|
|||
$c-indicator-margin: 0.25rem;
|
||||
|
||||
.c-indicator {
|
||||
display: inline-block;
|
||||
border-radius: 50rem;
|
||||
width: $c-indicator-size;
|
||||
height: $c-indicator-size;
|
||||
margin-top: -0.125rem;
|
||||
margin-right: $c-indicator-margin;
|
||||
font-size: 1rem;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: 50rem;
|
||||
width: $c-indicator-size;
|
||||
height: $c-indicator-size;
|
||||
margin-top: -0.125rem;
|
||||
margin-right: $c-indicator-margin;
|
||||
font-size: 1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// States
|
||||
// =============================================================================
|
||||
.is-absent .c-indicator {
|
||||
background: $color-state-absent;
|
||||
background: $color-state-absent;
|
||||
}
|
||||
|
||||
.is-live .c-indicator {
|
||||
background: $color-state-live;
|
||||
background: $color-state-live;
|
||||
}
|
||||
|
||||
.is-draft .c-indicator {
|
||||
background: $color-state-draft;
|
||||
background: $color-state-draft;
|
||||
}
|
||||
|
||||
// This is hipster. But it works.
|
||||
.is-live\+draft .c-indicator {
|
||||
background: $color-state-draft;
|
||||
position: relative;
|
||||
background: $color-state-draft;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: math.div($c-indicator-size, 2);
|
||||
height: $c-indicator-size;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-bottom-left-radius: 50rem;
|
||||
border-top-left-radius: 50rem;
|
||||
background: $color-state-live;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
width: math.div($c-indicator-size, 2);
|
||||
height: $c-indicator-size;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-bottom-left-radius: 50rem;
|
||||
border-top-left-radius: 50rem;
|
||||
background: $color-state-live;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
// makes a link look like regular text
|
||||
.nolink {
|
||||
color: $color-text-base;
|
||||
color: $color-text-base;
|
||||
|
||||
&:hover {
|
||||
color: $color-teal;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
|
||||
a.underlined {
|
||||
border-bottom: 1px solid currentColor;
|
||||
border-bottom: 1px solid currentColor;
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -1,37 +1,37 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
// Loading mask: overlays a certain area with a loading spinner and a faded out cover to prevent interaction
|
||||
.loading-mask {
|
||||
&.loading {
|
||||
position: relative;
|
||||
&.loading {
|
||||
position: relative;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
bottom: -5px;
|
||||
right: -5px;
|
||||
z-index: 1;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 30px;
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -15px 0 0 -15px;
|
||||
font-family: $font-wagtail-icons;
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
content: map.get($icons, 'spinner');
|
||||
z-index: 2;
|
||||
color: $color-teal;
|
||||
}
|
||||
&:before,
|
||||
&:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
bottom: -5px;
|
||||
right: -5px;
|
||||
z-index: 1;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 30px;
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -15px 0 0 -15px;
|
||||
font-family: $font-wagtail-icons;
|
||||
animation: spin-wag 0.5s infinite linear;
|
||||
content: map.get($icons, 'spinner');
|
||||
z-index: 2;
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,129 +1,129 @@
|
|||
@keyframes tail-wag {
|
||||
from {
|
||||
transform: rotate(-3deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(-3deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(7deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(7deg);
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.6em 1.2em;
|
||||
color: #aaa;
|
||||
-webkit-font-smoothing: auto;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.6em 1.2em;
|
||||
color: #aaa;
|
||||
-webkit-font-smoothing: auto;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
color: $color-white;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
text-align: center;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.wagtail-logo-container__mobile {
|
||||
margin-right: 10px;
|
||||
background-color: #555;
|
||||
border-radius: 50%;
|
||||
padding: 5px 7.5px;
|
||||
margin-right: 10px;
|
||||
background-color: #555;
|
||||
border-radius: 50%;
|
||||
padding: 5px 7.5px;
|
||||
|
||||
.wagtail-logo {
|
||||
width: 20px;
|
||||
float: left;
|
||||
border: 0;
|
||||
}
|
||||
.wagtail-logo {
|
||||
width: 20px;
|
||||
float: left;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wagtail-logo-container__desktop {
|
||||
position: relative;
|
||||
width: 100px;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: #555;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
transition: all 0.25s cubic-bezier(0.28, 0.15, 0, 2.1);
|
||||
|
||||
.page404__bg & {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.wagtail-logo-container-inner {
|
||||
width: 52px;
|
||||
height: 100px;
|
||||
background-color: #555;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
transition: all 0.25s cubic-bezier(0.28, 0.15, 0, 2.1);
|
||||
margin: auto;
|
||||
position: relative;
|
||||
|
||||
.page404__bg & {
|
||||
background-color: transparent;
|
||||
width: auto;
|
||||
height: auto;
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
.wagtail-logo {
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: inherit;
|
||||
|
||||
&.wagtail-logo__eye--open {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: inline !important; // doesn't work without `!important`, likely a specificity issue
|
||||
}
|
||||
|
||||
.wagtail-logo-container-inner {
|
||||
width: 52px;
|
||||
height: 100px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
&.wagtail-logo__eye--closed {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.page404__bg & {
|
||||
width: auto;
|
||||
height: auto;
|
||||
position: static;
|
||||
// Wagtail 'serious' animation (nod):
|
||||
&.logo-serious {
|
||||
&:hover {
|
||||
transform: rotate(4deg);
|
||||
}
|
||||
}
|
||||
|
||||
// Wagtail 'playful' animation (tail-wag, triggered by JS in base.html):
|
||||
&.logo-playful {
|
||||
&:hover {
|
||||
transform: rotate(8deg);
|
||||
transition: transform 1.2s ease;
|
||||
|
||||
.wagtail-logo {
|
||||
// stylelint-disable max-nesting-depth
|
||||
&.wagtail-logo__tail {
|
||||
animation: tail-wag 0.09s alternate;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.wagtail-logo {
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transition: inherit;
|
||||
|
||||
&.wagtail-logo__eye--open {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: inline !important; // doesn't work without `!important`, likely a specificity issue
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&.wagtail-logo__eye--closed {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Wagtail 'serious' animation (nod):
|
||||
&.logo-serious {
|
||||
&:hover {
|
||||
transform: rotate(4deg);
|
||||
}
|
||||
}
|
||||
|
||||
// Wagtail 'playful' animation (tail-wag, triggered by JS in base.html):
|
||||
&.logo-playful {
|
||||
&:hover {
|
||||
transform: rotate(8deg);
|
||||
transition: transform 1.2s ease;
|
||||
|
||||
.wagtail-logo {
|
||||
// stylelint-disable max-nesting-depth
|
||||
&.wagtail-logo__tail {
|
||||
animation: tail-wag 0.09s alternate;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
&.wagtail-logo__eye--open {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
&.wagtail-logo__eye--closed {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: inline !important;
|
||||
}
|
||||
}
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: inline !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast mode
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
.wagtail-logo-container__desktop {
|
||||
background-color: $system-color-link-text;
|
||||
}
|
||||
.wagtail-logo-container__desktop {
|
||||
background-color: $system-color-link-text;
|
||||
}
|
||||
}
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -1,17 +1,17 @@
|
|||
.media-placeholder {
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
background-color: #ccc;
|
||||
padding: 5px;
|
||||
width: 600px;
|
||||
height: 400px;
|
||||
background-color: #ccc;
|
||||
padding: 5px;
|
||||
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 350px;
|
||||
max-height: 350px;
|
||||
margin: 20px;
|
||||
}
|
||||
img {
|
||||
max-width: 350px;
|
||||
max-height: 350px;
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
.capabilitymessage {
|
||||
display: block;
|
||||
background-color: $color-red;
|
||||
color: $color-white;
|
||||
padding: 1em 2em;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
display: block;
|
||||
background-color: $color-red;
|
||||
color: $color-white;
|
||||
padding: 1em 2em;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: $color-white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
a {
|
||||
color: $color-white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,85 +2,85 @@
|
|||
// for display on the next page visited. These appear as an animated banner at the top of the page.
|
||||
// For inline help text, see typography.scss
|
||||
.messages {
|
||||
position: relative;
|
||||
background-color: $color-grey-1;
|
||||
|
||||
.buttons {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
> ul {
|
||||
@include unlistimmediate();
|
||||
position: relative;
|
||||
background-color: $color-grey-1;
|
||||
top: -100px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-left: 1em;
|
||||
> ul > li {
|
||||
// @include nice-padding;
|
||||
padding: 1.6em 3em 1.6em 1.6em;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
> ul > li:before {
|
||||
@include font-smoothing;
|
||||
margin-right: 0.5em;
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
vertical-align: text-top;
|
||||
margin-right: 0.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: $color-red-dark;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: $color-orange-dark;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: $color-green-dark;
|
||||
}
|
||||
|
||||
.success .button:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
color: $color-white;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
> ul {
|
||||
@include unlistimmediate();
|
||||
position: relative;
|
||||
top: -100px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
> ul > li {
|
||||
// @include nice-padding;
|
||||
padding: 1.6em 3em 1.6em 1.6em;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
> ul > li:before {
|
||||
@include font-smoothing;
|
||||
margin-right: 0.5em;
|
||||
font-size: 1.5em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
vertical-align: text-top;
|
||||
margin-right: 0.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: $color-red-dark;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: $color-orange-dark;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: $color-green-dark;
|
||||
}
|
||||
|
||||
.success .button:hover {
|
||||
background-color: $color-teal-dark;
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
color: $color-white;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.errorlist {
|
||||
margin: 0.5em 0 0 1em;
|
||||
}
|
||||
.errorlist {
|
||||
margin: 0.5em 0 0 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.messages.new > ul {
|
||||
transition: none;
|
||||
top: -100px;
|
||||
transition: none;
|
||||
top: -100px;
|
||||
}
|
||||
|
||||
.ready .messages > ul,
|
||||
.messages.appear > ul {
|
||||
transition: top 0.5s ease, opacity 0.5s ease, max-height 1.2s ease;
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
transition: top 0.5s ease, opacity 0.5s ease, max-height 1.2s ease;
|
||||
opacity: 1;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.messages > ul > li {
|
||||
padding-left: 1.6em;
|
||||
padding-right: 3em;
|
||||
}
|
||||
.messages > ul > li {
|
||||
padding-left: 1.6em;
|
||||
padding-right: 3em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
.status-msg {
|
||||
&.success {
|
||||
color: $color-green-dark;
|
||||
}
|
||||
&.success {
|
||||
color: $color-green-dark;
|
||||
}
|
||||
|
||||
&.failure {
|
||||
color: $color-red-dark;
|
||||
}
|
||||
&.failure {
|
||||
color: $color-red-dark;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,136 +1,140 @@
|
|||
$zindex-modal-background: 500;
|
||||
|
||||
.fade {
|
||||
@include transition(opacity 0.15s linear);
|
||||
opacity: 0;
|
||||
@include transition(opacity 0.15s linear);
|
||||
opacity: 0;
|
||||
|
||||
&.in {
|
||||
opacity: 1;
|
||||
}
|
||||
&.in {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the scroll on the body
|
||||
.modal-open {
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
|
||||
.content-wrapper {
|
||||
transform: none;
|
||||
}
|
||||
.content-wrapper {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Container that the modal scrolls within
|
||||
.modal {
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
overflow: auto;
|
||||
overflow-y: scroll;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal-background;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
overflow: auto;
|
||||
overflow-y: scroll;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $zindex-modal-background;
|
||||
}
|
||||
|
||||
// Shell div to position the modal with bottom padding
|
||||
.modal-dialog {
|
||||
box-sizing: border-box;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0;
|
||||
z-index: ($zindex-modal-background + 10);
|
||||
height: 90%;
|
||||
width: 85%;
|
||||
box-sizing: border-box;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0;
|
||||
z-index: ($zindex-modal-background + 10);
|
||||
height: 90%;
|
||||
width: 85%;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
margin-right: -0.25em;
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
margin-right: -0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
// Actual modal
|
||||
.modal-content {
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
width: 98.7%;
|
||||
position: relative;
|
||||
background-color: $color-white;
|
||||
margin-top: 2em;
|
||||
padding-bottom: 3em;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
width: 98.7%;
|
||||
position: relative;
|
||||
background-color: $color-white;
|
||||
margin-top: 2em;
|
||||
padding-bottom: 3em;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Modal background
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: ($zindex-modal-background - 10);
|
||||
background-color: $color-black;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: ($zindex-modal-background - 10);
|
||||
background-color: $color-black;
|
||||
|
||||
// Fade for backdrop
|
||||
&.fade { opacity: 0; }
|
||||
// Fade for backdrop
|
||||
&.fade {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.in { opacity: 0.5; }
|
||||
&.in {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.modal .close {
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Where all modal content resides
|
||||
.modal-body {
|
||||
position: relative;
|
||||
padding-bottom: 2em;
|
||||
position: relative;
|
||||
padding-bottom: 2em;
|
||||
|
||||
header {
|
||||
padding-left: 2em;
|
||||
padding-right: 100px;
|
||||
header {
|
||||
padding-left: 2em;
|
||||
padding-right: 100px;
|
||||
|
||||
&.tab-merged {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
&.tab-merged {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
padding-left: 0 !important;
|
||||
margin-left: -36px;
|
||||
}
|
||||
.header-title {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
padding-left: 0 !important;
|
||||
margin-left: -36px;
|
||||
}
|
||||
|
||||
.tab-merged .header-title {
|
||||
margin-left: 0;
|
||||
}
|
||||
.tab-merged .header-title {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.modal-dialog {
|
||||
padding: 0 0 2em $menu-width;
|
||||
}
|
||||
.modal-dialog {
|
||||
padding: 0 0 2em $menu-width;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
header.tab-merged {
|
||||
padding-left: $desktop-nice-padding;
|
||||
}
|
||||
.modal-body {
|
||||
header.tab-merged {
|
||||
padding-left: $desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.modal-dialog {
|
||||
max-width: 100em;
|
||||
padding: 0 0 2em;
|
||||
}
|
||||
.modal-dialog {
|
||||
max-width: 100em;
|
||||
padding: 0 0 2em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
.privacy-indicator {
|
||||
.label-private,
|
||||
.label-private,
|
||||
.label-public {
|
||||
&:before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&.public {
|
||||
.label-private {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.private {
|
||||
.label-public {
|
||||
&:before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&.public {
|
||||
.label-private {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.private {
|
||||
.label-public {
|
||||
display: none;
|
||||
}
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
.progress {
|
||||
border-radius: 1.2em;
|
||||
background-color: $color-teal-dark;
|
||||
border: 1px solid $color-teal;
|
||||
opacity: 0;
|
||||
border-radius: 1.2em;
|
||||
background-color: $color-teal-dark;
|
||||
border: 1px solid $color-teal;
|
||||
opacity: 0;
|
||||
|
||||
&.active {
|
||||
@include transition(opacity 0.3s ease);
|
||||
opacity: 1;
|
||||
}
|
||||
&.active {
|
||||
@include transition(opacity 0.3s ease);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bar {
|
||||
@include transition(width 0.3s ease);
|
||||
border-radius: 1.5em;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
line-height: 1.2em;
|
||||
color: $color-white;
|
||||
font-size: 0.85em;
|
||||
background-color: $color-teal;
|
||||
height: 1.2em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.bar {
|
||||
@include transition(width 0.3s ease);
|
||||
border-radius: 1.5em;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
line-height: 1.2em;
|
||||
color: $color-white;
|
||||
font-size: 0.85em;
|
||||
background-color: $color-teal;
|
||||
height: 1.2em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
.skiplink {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: -1000rem;
|
||||
left: 1rem;
|
||||
z-index: 3000;
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: -1000rem;
|
||||
left: 1rem;
|
||||
z-index: 3000;
|
||||
|
||||
&:focus {
|
||||
top: 1rem;
|
||||
}
|
||||
&:focus {
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
&.button {
|
||||
background: $color-green-darker;
|
||||
border: $color-green-darker;
|
||||
}
|
||||
&.button {
|
||||
background: $color-green-darker;
|
||||
border: $color-green-darker;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,50 @@
|
|||
@use "sass:color";
|
||||
@use 'sass:color';
|
||||
|
||||
.status-tag {
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
padding: 0 0.5em;
|
||||
border: 1px solid color.adjust($color-grey-2, $lightness: 30%);
|
||||
color: color.adjust($color-grey-2, $lightness: 30%);
|
||||
-webkit-font-smoothing: auto;
|
||||
line-height: 19px;
|
||||
font-size: 0.8em;
|
||||
margin: 0 0.5em 0.5em;
|
||||
background: #fff url('#{$images-root}bg-dark-diag.svg');
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
padding: 0 0.5em;
|
||||
border: 1px solid color.adjust($color-grey-2, $lightness: 30%);
|
||||
color: color.adjust($color-grey-2, $lightness: 30%);
|
||||
-webkit-font-smoothing: auto;
|
||||
line-height: 19px;
|
||||
font-size: 0.8em;
|
||||
margin: 0 0.5em 0.5em;
|
||||
background: #fff url('#{$images-root}bg-dark-diag.svg');
|
||||
|
||||
&.primary {
|
||||
color: $color-grey-2;
|
||||
border: 1px solid $color-grey-2;
|
||||
background: #fff;
|
||||
}
|
||||
&.primary {
|
||||
color: $color-grey-2;
|
||||
border: 1px solid $color-grey-2;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&--label {
|
||||
color: $color-grey-2;
|
||||
background: $color-grey-4;
|
||||
border: $color-grey-4;
|
||||
font-weight: 500;
|
||||
}
|
||||
&--label {
|
||||
color: $color-grey-2;
|
||||
background: $color-grey-4;
|
||||
border: $color-grey-4;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.listing .index .status-tag--label {
|
||||
border: 1px solid;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
button.status-tag:hover,
|
||||
a.status-tag:hover,
|
||||
a.status-tag.primary:hover {
|
||||
border-color: $color-teal;
|
||||
color: $color-teal;
|
||||
border-color: $color-teal;
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
button.status-tag:hover {
|
||||
border-color: $color-teal-dark;
|
||||
background-color: $color-teal-darker;
|
||||
color: $color-white;
|
||||
border-color: $color-teal-dark;
|
||||
background-color: $color-teal-darker;
|
||||
color: $color-white;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
|
||||
$switch-width: 40px;
|
||||
$switch-height: 20px;
|
||||
|
@ -12,98 +12,101 @@ $switch-outline-radius: $switch-border-radius + $switch-outline;
|
|||
$switch-color-middle-grey: #777;
|
||||
|
||||
.switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 5px 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin: 5px 0;
|
||||
|
||||
// Disable forms styling that's applied to the <label> tag
|
||||
width: unset;
|
||||
float: unset;
|
||||
// Disable forms styling that's applied to the <label> tag
|
||||
width: unset;
|
||||
float: unset;
|
||||
|
||||
&__toggle {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
&__toggle {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
transition: all 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: block;
|
||||
}
|
||||
|
||||
&::before {
|
||||
height: $switch-height;
|
||||
width: $switch-width;
|
||||
border-radius: $switch-border-radius;
|
||||
background: $switch-color-middle-grey;
|
||||
border: $switch-border solid $switch-color-middle-grey;
|
||||
}
|
||||
|
||||
&::after {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate($switch-border, -50%);
|
||||
height: $switch-height;
|
||||
width: $switch-height;
|
||||
border: $switch-border solid $color-white;
|
||||
border-radius: 50%;
|
||||
background-color: $color-white;
|
||||
}
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
transition: all 100ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: block;
|
||||
}
|
||||
|
||||
[type=checkbox]:checked + &__toggle::before {
|
||||
background: $color-teal;
|
||||
border-color: $color-teal;
|
||||
&::before {
|
||||
height: $switch-height;
|
||||
width: $switch-width;
|
||||
border-radius: $switch-border-radius;
|
||||
background: $switch-color-middle-grey;
|
||||
border: $switch-border solid $switch-color-middle-grey;
|
||||
}
|
||||
|
||||
[type=checkbox]:checked + &__toggle::after {
|
||||
transform: translate(calc(#{$switch-width} + #{$switch-border} - 100%), -50%);
|
||||
&::after {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate($switch-border, -50%);
|
||||
height: $switch-height;
|
||||
width: $switch-height;
|
||||
border: $switch-border solid $color-white;
|
||||
border-radius: 50%;
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
[type='checkbox']:checked + &__toggle::before {
|
||||
background: $color-teal;
|
||||
border-color: $color-teal;
|
||||
}
|
||||
|
||||
[type='checkbox']:checked + &__toggle::after {
|
||||
transform: translate(
|
||||
calc(#{$switch-width} + #{$switch-border} - 100%),
|
||||
-50%
|
||||
);
|
||||
}
|
||||
|
||||
[type='checkbox']:disabled + &__toggle {
|
||||
cursor: not-allowed;
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
[type='checkbox']:disabled + &__toggle::after {
|
||||
opacity: 0.5;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[type='checkbox']:focus + &__toggle {
|
||||
outline: $color-focus-outline solid $switch-outline;
|
||||
-moz-outline-radius: $switch-outline-radius;
|
||||
}
|
||||
|
||||
[type='checkbox'] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Colour changes for when displaying on teal background
|
||||
&--teal-background {
|
||||
$background: #03b0b1;
|
||||
|
||||
.switch__toggle {
|
||||
&::before {
|
||||
background-color: #b9b9b9;
|
||||
border: $switch-border solid #b9b9b9;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
[type=checkbox]:disabled + &__toggle {
|
||||
cursor: not-allowed;
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
[type=checkbox]:disabled + &__toggle::after {
|
||||
opacity: 0.5;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[type=checkbox]:focus + &__toggle {
|
||||
outline: $color-focus-outline solid $switch-outline;
|
||||
-moz-outline-radius: $switch-outline-radius;
|
||||
}
|
||||
|
||||
[type=checkbox] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Colour changes for when displaying on teal background
|
||||
&--teal-background {
|
||||
$background: #03b0b1;
|
||||
|
||||
.switch__toggle {
|
||||
&::before {
|
||||
background-color: #b9b9b9;
|
||||
border: $switch-border solid #b9b9b9;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: $color-white;
|
||||
}
|
||||
}
|
||||
|
||||
[type=checkbox]:checked + .switch__toggle::before {
|
||||
// Override the white-background styling
|
||||
background-color: $background;
|
||||
border-color: $background;
|
||||
opacity: 1;
|
||||
}
|
||||
[type='checkbox']:checked + .switch__toggle::before {
|
||||
// Override the white-background styling
|
||||
background-color: $background;
|
||||
border-color: $background;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,161 +1,161 @@
|
|||
.tab-nav {
|
||||
@include row();
|
||||
@include row();
|
||||
padding: 0;
|
||||
background: $color-grey-4;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
width: 33%;
|
||||
float: left;
|
||||
padding: 0;
|
||||
background: $color-grey-4;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
width: 33%;
|
||||
float: left;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: $desktop-nice-padding;
|
||||
margin-left: 0;
|
||||
}
|
||||
&:first-of-type {
|
||||
padding-left: $desktop-nice-padding;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
@include transition(border-color 0.2s ease);
|
||||
background-color: $color-teal-darker;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 0.6em 0.7em 0.8em;
|
||||
color: $color-white;
|
||||
border-top: 0.3em solid $color-teal-darker;
|
||||
max-height: 1.44em;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
color: $color-white;
|
||||
border-top-color: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@include transition(border-color 0.2s ease);
|
||||
background-color: $color-teal-darker;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 0.6em 0.7em 0.8em;
|
||||
color: $color-white;
|
||||
border-top: 0.3em solid $color-teal-darker;
|
||||
max-height: 1.44em;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
color: $color-white;
|
||||
border-top-color: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
a.errors {
|
||||
&:after {
|
||||
border-radius: 50px;
|
||||
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
right: -0.5em;
|
||||
top: -0.5em;
|
||||
z-index: 5;
|
||||
min-width: 0.9em;
|
||||
color: $color-white;
|
||||
background: $color-red;
|
||||
content: attr(data-count);
|
||||
padding: 0 0.3em;
|
||||
line-height: 1.4em;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
a.errors {
|
||||
&:after {
|
||||
border-radius: 50px;
|
||||
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
right: -0.5em;
|
||||
top: -0.5em;
|
||||
z-index: 5;
|
||||
min-width: 0.9em;
|
||||
color: $color-white;
|
||||
background: $color-red;
|
||||
content: attr(data-count);
|
||||
padding: 0 0.3em;
|
||||
line-height: 1.4em;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
li.active a {
|
||||
box-shadow: none;
|
||||
color: $color-grey-1;
|
||||
background-color: $color-white;
|
||||
border-top: 0.3em solid $color-grey-1;
|
||||
}
|
||||
|
||||
li.active a {
|
||||
box-shadow: none;
|
||||
color: $color-grey-1;
|
||||
background-color: $color-white;
|
||||
border-top: 0.3em solid $color-grey-1;
|
||||
}
|
||||
// For cases where tab-nav should merge with header
|
||||
&.merged {
|
||||
margin-top: 0;
|
||||
background-color: $color-header-bg;
|
||||
}
|
||||
|
||||
// For cases where tab-nav should merge with header
|
||||
&.merged {
|
||||
margin-top: 0;
|
||||
background-color: $color-header-bg;
|
||||
}
|
||||
li.right {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
li.right {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 2px;
|
||||
}
|
||||
li.wide {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
li.wide {
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.right {
|
||||
max-height: 1.44em;
|
||||
overflow: visible;
|
||||
}
|
||||
.right {
|
||||
max-height: 1.44em;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
> section {
|
||||
display: none;
|
||||
padding-top: 1em;
|
||||
> section {
|
||||
display: none;
|
||||
padding-top: 1em;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.page-locked & {
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
.page-locked & {
|
||||
cursor: not-allowed;
|
||||
user-select: none;
|
||||
|
||||
> * {
|
||||
pointer-events: none;
|
||||
}
|
||||
> * {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.tab-nav {
|
||||
// For cases where tab-nav should merge with header
|
||||
&.merged {
|
||||
background-color: $color-header-bg;
|
||||
}
|
||||
|
||||
li {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
}
|
||||
|
||||
li.settings a {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
.tab-nav {
|
||||
// For cases where tab-nav should merge with header
|
||||
&.merged {
|
||||
background-color: $color-header-bg;
|
||||
}
|
||||
|
||||
.modal-content .tab-nav li {
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: $desktop-nice-padding;
|
||||
}
|
||||
li {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
}
|
||||
|
||||
li.settings a {
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-content .tab-nav li {
|
||||
padding: 0;
|
||||
min-width: 0;
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: $desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
// To allow tabs on the edit page to be editable
|
||||
.tab-nav li:first-of-type {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
// To allow tabs on the edit page to be editable
|
||||
.tab-nav li:first-of-type {
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
|
||||
.tab-nav li {
|
||||
width: auto;
|
||||
}
|
||||
.tab-nav li {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Media for Windows High Contrast
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
.tab-nav li.active a {
|
||||
border-bottom: 0.3em solid $system-color-link-text;
|
||||
}
|
||||
.tab-nav li.active a {
|
||||
border-bottom: 0.3em solid $system-color-link-text;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
// free tagging tags from taggit
|
||||
.tag {
|
||||
border-radius: 2px;
|
||||
background-color: $color-teal;
|
||||
padding: 0.2em 0.5em;
|
||||
border-radius: 2px;
|
||||
background-color: $color-teal;
|
||||
padding: 0.2em 0.5em;
|
||||
color: $color-white;
|
||||
line-height: 2em;
|
||||
white-space: nowrap;
|
||||
|
||||
&:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
display: inline-block;
|
||||
color: $color-white;
|
||||
line-height: 2em;
|
||||
white-space: nowrap;
|
||||
content: map.get($icons, 'tag');
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
&:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
display: inline-block;
|
||||
color: $color-white;
|
||||
content: map.get($icons, 'tag');
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.taglist & {
|
||||
margin-right: 0.8em;
|
||||
}
|
||||
.taglist & {
|
||||
margin-right: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
a.tag:hover {
|
||||
background-color: $color-teal-darker;
|
||||
color: $color-white;
|
||||
background-color: $color-teal-darker;
|
||||
color: $color-white;
|
||||
}
|
||||
|
||||
.taglist {
|
||||
font-size: 0.9em;
|
||||
line-height: 2.4em;
|
||||
font-size: 0.9em;
|
||||
line-height: 2.4em;
|
||||
}
|
||||
|
||||
.tagfilter {
|
||||
legend {
|
||||
@include visuallyvisible;
|
||||
legend {
|
||||
@include visuallyvisible;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
@include column(2);
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
@include media-breakpoint-up(sm) {
|
||||
@include column(2);
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
}
|
||||
|
||||
.button.bicolor.icon-cross {
|
||||
padding-left: 2em;
|
||||
a {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&:before {
|
||||
background-color: transparent;
|
||||
}
|
||||
.button.bicolor.icon-cross {
|
||||
padding-left: 2em;
|
||||
|
||||
&:before {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,108 +1,108 @@
|
|||
// From Bootstrap v3.0.0
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: 1030;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
opacity: 0;
|
||||
visibility: visible;
|
||||
position: absolute;
|
||||
z-index: 1030;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
opacity: 0;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tooltip.in {
|
||||
opacity: 0.9;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.tooltip.top {
|
||||
padding: 5px 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.right {
|
||||
padding: 0 5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.tooltip.bottom {
|
||||
padding: 5px 0;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.left {
|
||||
padding: 0 5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
max-width: 200px;
|
||||
padding: 3px 8px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #000;
|
||||
border-radius: 4px;
|
||||
max-width: 200px;
|
||||
padding: 3px 8px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background-color: #000;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.top-left .tooltip-arrow {
|
||||
bottom: 0;
|
||||
left: 5px;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
bottom: 0;
|
||||
left: 5px;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.top-right .tooltip-arrow {
|
||||
right: 5px;
|
||||
bottom: 0;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
right: 5px;
|
||||
bottom: 0;
|
||||
border-top-color: #000;
|
||||
border-width: 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.right .tooltip-arrow {
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-top: -5px;
|
||||
border-right-color: #000;
|
||||
border-width: 5px 5px 5px 0;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-top: -5px;
|
||||
border-right-color: #000;
|
||||
border-width: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.left .tooltip-arrow {
|
||||
top: 50%;
|
||||
right: 0;
|
||||
margin-top: -5px;
|
||||
border-left-color: #000;
|
||||
border-width: 5px 0 5px 5px;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
margin-top: -5px;
|
||||
border-left-color: #000;
|
||||
border-width: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
.tooltip.bottom .tooltip-arrow {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
|
||||
.tooltip.bottom-left .tooltip-arrow {
|
||||
top: 0;
|
||||
left: 5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
top: 0;
|
||||
left: 5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
|
||||
.tooltip.bottom-right .tooltip-arrow {
|
||||
top: 0;
|
||||
right: 5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
border-bottom-color: #000;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
.workflow-tasks {
|
||||
$task-width: 117px;
|
||||
$task-height: 56px;
|
||||
$task-width: 117px;
|
||||
$task-height: 56px;
|
||||
|
||||
list-style-type: none;
|
||||
list-style-type: none;
|
||||
|
||||
&__task {
|
||||
display: inline-block;
|
||||
background-color: #f8ffff;
|
||||
border: 2px solid #7ebebe;
|
||||
border-radius: 5px;
|
||||
color: #007d7e;
|
||||
box-sizing: border-box;
|
||||
width: $task-width;
|
||||
height: $task-height;
|
||||
margin: 7px;
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
}
|
||||
&__task {
|
||||
display: inline-block;
|
||||
background-color: #f8ffff;
|
||||
border: 2px solid #7ebebe;
|
||||
border-radius: 5px;
|
||||
color: #007d7e;
|
||||
box-sizing: border-box;
|
||||
width: $task-width;
|
||||
height: $task-height;
|
||||
margin: 7px;
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
}
|
||||
|
||||
&__step {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 3px;
|
||||
}
|
||||
&__step {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
&__name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__extra-tasks {
|
||||
display: inline-block;
|
||||
height: $task-height;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&__extra-tasks {
|
||||
display: inline-block;
|
||||
height: $task-height;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@include media-breakpoint-up(sm) {
|
||||
.browsermessage {
|
||||
margin: 0 0 0 -150px;
|
||||
}
|
||||
.browsermessage {
|
||||
margin: 0 0 0 -150px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ OTHER PREFIXES
|
|||
|
||||
==============================================================================*/
|
||||
|
||||
|
||||
/* SETTINGS
|
||||
These are variables, maps, and fonts.
|
||||
* No CSS should be produced by these files
|
||||
|
@ -49,7 +48,6 @@ These are variables, maps, and fonts.
|
|||
|
||||
@import 'settings';
|
||||
|
||||
|
||||
/* TOOLS
|
||||
These are functions and mixins.
|
||||
* No CSS should be produced by these files.
|
||||
|
@ -57,7 +55,6 @@ These are functions and mixins.
|
|||
|
||||
@import 'tools';
|
||||
|
||||
|
||||
/* GENERIC
|
||||
This is for resets and other rules that affect large collections of bare elements.
|
||||
* Changes to them should be very rare.
|
||||
|
@ -65,7 +62,6 @@ This is for resets and other rules that affect large collections of bare element
|
|||
|
||||
// @import 'generic/generic';
|
||||
|
||||
|
||||
/* ELEMENTS
|
||||
These are base styles for bare HTML elements.
|
||||
* Changes to them should be very rare.
|
||||
|
@ -76,7 +72,6 @@ These are base styles for bare HTML elements.
|
|||
@import 'elements/forms';
|
||||
@import 'elements/root';
|
||||
|
||||
|
||||
/* OBJECTS
|
||||
These are classes related to layout, known as 'objects' in ITCSS or OOCSS.
|
||||
* This is for grids, wrappers, and other non-consmetic layout utilities.
|
||||
|
@ -86,7 +81,6 @@ These are classes related to layout, known as 'objects' in ITCSS or OOCSS.
|
|||
@import 'objects/objects';
|
||||
@import 'objects/avatar';
|
||||
|
||||
|
||||
/* COMPONENTS
|
||||
These are classes for components.
|
||||
* These classes (unless legacy) are prefixed with `.c-`.
|
||||
|
@ -147,7 +141,6 @@ These are classes that provide overrides.
|
|||
@import 'overrides/vendor.datetimepicker';
|
||||
@import 'overrides/vendor.tagit';
|
||||
|
||||
|
||||
// UTILITIES: classes that do one simple thing.
|
||||
@import 'overrides/utilities.hidden';
|
||||
@import 'overrides/utilities.text';
|
||||
|
@ -159,7 +152,6 @@ These are classes that provide overrides.
|
|||
@import 'overrides/utilities.text.legacy';
|
||||
@import 'overrides/utilities.legacy';
|
||||
|
||||
|
||||
// PAGES: page-specific overrides
|
||||
@import 'overrides/pages.homepage';
|
||||
@import 'overrides/pages.page-explorer';
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
html {
|
||||
background: $color-grey-4;
|
||||
height: 100%;
|
||||
background: $color-grey-4;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
transition: visibility 0s linear 0s, opacity 0.2s ease-out;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
transition: visibility 0s linear 0s, opacity 0.2s ease-out;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid $color-grey-4;
|
||||
border-width: 1px 0 0;
|
||||
margin: 1.5em 0;
|
||||
border: 1px solid $color-grey-4;
|
||||
border-width: 1px 0 0;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
// general image style
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
|
|
@ -1,242 +1,239 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
// These are the generic stylings for forms of any type.
|
||||
// If you're styling something specific to the page editing interface,
|
||||
// it probably ought to go in layouts/page-editor.scss
|
||||
form {
|
||||
ul,
|
||||
ul,
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 0;
|
||||
padding: 0 0 2em;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0 0 2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
@include visuallyhidden();
|
||||
@include visuallyhidden();
|
||||
}
|
||||
|
||||
label,
|
||||
.label {
|
||||
text-transform: none;
|
||||
font-weight: bold;
|
||||
color: $color-grey-1;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
margin: 0;
|
||||
line-height: 1.3em;
|
||||
text-transform: none;
|
||||
font-weight: bold;
|
||||
color: $color-grey-1;
|
||||
font-size: 1.1em;
|
||||
display: block;
|
||||
padding: 0 0 0.8em;
|
||||
margin: 0;
|
||||
line-height: 1.3em;
|
||||
|
||||
.checkbox &,
|
||||
.radio & {
|
||||
display: inline;
|
||||
.checkbox &,
|
||||
.radio & {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
&.no-float {
|
||||
float: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
@include column(2);
|
||||
padding-top: 1.2em;
|
||||
padding-left: 0;
|
||||
|
||||
.radio_select &,
|
||||
.multiple_choice_field &,
|
||||
.model_multiple_choice_field &,
|
||||
.checkbox_select_multiple &,
|
||||
.boolean_field &,
|
||||
.model_choice_field &,
|
||||
.image_field & {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.no-float {
|
||||
float: none;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
@include column(2);
|
||||
padding-top: 1.2em;
|
||||
padding-left: 0;
|
||||
|
||||
.radio_select &,
|
||||
.multiple_choice_field &,
|
||||
.model_multiple_choice_field &,
|
||||
.checkbox_select_multiple &,
|
||||
.boolean_field &,
|
||||
.model_choice_field &,
|
||||
.image_field & {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
// Horrid specificity war
|
||||
.model_choice_field.select & {
|
||||
padding-top: 1.2em;
|
||||
}
|
||||
// Horrid specificity war
|
||||
.model_choice_field.select & {
|
||||
padding-top: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input:not([type=submit]),
|
||||
input:not([type='submit']),
|
||||
textarea,
|
||||
select,
|
||||
.halloeditor,
|
||||
.tagit {
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
font-family: $font-sans;
|
||||
border: 1px solid $color-input-border;
|
||||
padding: 0.9em 1.2em;
|
||||
background-color: $color-fieldset-hover;
|
||||
color: $color-text-input;
|
||||
font-size: 1.2em;
|
||||
font-weight: 300;
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
font-family: $font-sans;
|
||||
border: 1px solid $color-input-border;
|
||||
padding: 0.9em 1.2em;
|
||||
background-color: $color-fieldset-hover;
|
||||
color: $color-text-input;
|
||||
font-size: 1.2em;
|
||||
font-weight: 300;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-white;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $color-input-focus;
|
||||
border-color: $color-input-focus-border;
|
||||
}
|
||||
&:focus {
|
||||
background-color: $color-input-focus;
|
||||
border-color: $color-input-focus-border;
|
||||
}
|
||||
|
||||
&:disabled,
|
||||
&[disabled],
|
||||
&:disabled:hover,
|
||||
&[disabled]:hover {
|
||||
background-color: $color-grey-4;
|
||||
cursor: not-allowed;
|
||||
color: $color-grey-2;
|
||||
}
|
||||
&:disabled,
|
||||
&[disabled],
|
||||
&:disabled:hover,
|
||||
&[disabled]:hover {
|
||||
background-color: $color-grey-4;
|
||||
cursor: not-allowed;
|
||||
color: $color-grey-2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: $media-forced-colours) {
|
||||
.tagit,
|
||||
.field-content .tagit .tagit-choice,
|
||||
.tagit .tagit-new .ui-widget-content {
|
||||
box-shadow: inset 1000px 0 0 0 $color-black;
|
||||
color: $color-white;
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
.tagit,
|
||||
.field-content .tagit .tagit-choice,
|
||||
.tagit .tagit-new .ui-widget-content {
|
||||
box-shadow: inset 1000px 0 0 0 $color-black;
|
||||
color: $color-white;
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
|
||||
.tagit span.tagit-label:before,
|
||||
.tagit span.tagit-label {
|
||||
color: $color-black;
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
.tagit span.tagit-label:before,
|
||||
.tagit span.tagit-label {
|
||||
color: $color-black;
|
||||
forced-color-adjust: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the arrow on `<select>`s in IE10+.
|
||||
select::-ms-expand {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.file_field {
|
||||
.input {
|
||||
label {
|
||||
float: none;
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.input {
|
||||
label {
|
||||
float: none;
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// radio and check boxes
|
||||
input[type=radio],
|
||||
input[type=checkbox] {
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
input[type='radio'],
|
||||
input[type='checkbox'] {
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=radio] {
|
||||
height: 12px;
|
||||
width: auto;
|
||||
position: relative;
|
||||
margin-right: 27px;
|
||||
input[type='radio'] {
|
||||
height: 12px;
|
||||
width: auto;
|
||||
position: relative;
|
||||
margin-right: 27px;
|
||||
}
|
||||
|
||||
input[type=radio]:before {
|
||||
border-radius: 100%;
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -2px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
content: map.get($icons, 'radio-full');
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1.1em;
|
||||
padding: 4px;
|
||||
background-color: $color-white;
|
||||
color: $color-grey-4;
|
||||
border: 1px solid $color-grey-4;
|
||||
input[type='radio']:before {
|
||||
border-radius: 100%;
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -2px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
content: map.get($icons, 'radio-full');
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1.1em;
|
||||
padding: 4px;
|
||||
background-color: $color-white;
|
||||
color: $color-grey-4;
|
||||
border: 1px solid $color-grey-4;
|
||||
}
|
||||
|
||||
input[type=radio]:checked:before {
|
||||
content: map.get($icons, 'radio-full');
|
||||
color: $color-teal;
|
||||
input[type='radio']:checked:before {
|
||||
content: map.get($icons, 'radio-full');
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
height: 12px;
|
||||
width: 22px;
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
input[type='checkbox'] {
|
||||
height: 12px;
|
||||
width: 22px;
|
||||
position: relative;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
input[type=checkbox]:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
content: '';
|
||||
line-height: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: $color-white;
|
||||
border: 1px solid $color-grey-4;
|
||||
color: $color-teal;
|
||||
input[type='checkbox']:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
content: '';
|
||||
line-height: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: $color-white;
|
||||
border: 1px solid $color-grey-4;
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked:before {
|
||||
content: map.get($icons, 'tick');
|
||||
input[type='checkbox']:checked:before {
|
||||
content: map.get($icons, 'tick');
|
||||
}
|
||||
|
||||
input[type=checkbox][disabled]:before {
|
||||
cursor: not-allowed;
|
||||
input[type='checkbox'][disabled]:before {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
// Special styles to counteract Firefox's completely unwarranted assumptions about button styles
|
||||
input[type=submit],
|
||||
input[type=reset],
|
||||
input[type=button],
|
||||
input[type='submit'],
|
||||
input[type='reset'],
|
||||
input[type='button'],
|
||||
button {
|
||||
padding: 0 1em;
|
||||
padding: 0 1em;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
&.button-small {
|
||||
height: 2em;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
&.button-small {
|
||||
height: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transitions
|
||||
|
@ -244,5 +241,5 @@ fieldset,
|
|||
input,
|
||||
textarea,
|
||||
select {
|
||||
@include transition(background-color 0.2s ease);
|
||||
@include transition(background-color 0.2s ease);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
:root {
|
||||
@include define-color('color-primary', #007d7e);
|
||||
@include define-color('color-primary-darker', css-darken(css-adjust-hue(get-color('color-primary'), 1), 4%));
|
||||
@include define-color('color-primary-dark', css-darken(css-adjust-hue(get-color('color-primary'), 1), 7%));
|
||||
@include define-color('color-primary-lighter', css-lighten(css-desaturate(css-adjust-hue(get-color('color-primary'), 1), 46%), 48%));
|
||||
@include define-color('color-primary-light', css-lighten(css-desaturate(css-adjust-hue(get-color('color-primary'), 1), 44%), 58%));
|
||||
@include define-color('color-primary', #007d7e);
|
||||
@include define-color(
|
||||
'color-primary-darker',
|
||||
css-darken(css-adjust-hue(get-color('color-primary'), 1), 4%)
|
||||
);
|
||||
@include define-color(
|
||||
'color-primary-dark',
|
||||
css-darken(css-adjust-hue(get-color('color-primary'), 1), 7%)
|
||||
);
|
||||
@include define-color(
|
||||
'color-primary-lighter',
|
||||
css-lighten(
|
||||
css-desaturate(css-adjust-hue(get-color('color-primary'), 1), 46%),
|
||||
48%
|
||||
)
|
||||
);
|
||||
@include define-color(
|
||||
'color-primary-light',
|
||||
css-lighten(
|
||||
css-desaturate(css-adjust-hue(get-color('color-primary'), 1), 44%),
|
||||
58%
|
||||
)
|
||||
);
|
||||
|
||||
@include define-color('color-input-focus', css-lighten(css-desaturate(get-color('color-primary'), 40%), 72%));
|
||||
@include define-color('color-input-focus-border', css-lighten(css-saturate(get-color('color-primary'), 12%), 10%));
|
||||
@include define-color(
|
||||
'color-input-focus',
|
||||
css-lighten(css-desaturate(get-color('color-primary'), 40%), 72%)
|
||||
);
|
||||
@include define-color(
|
||||
'color-input-focus-border',
|
||||
css-lighten(css-saturate(get-color('color-primary'), 12%), 10%)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
body {
|
||||
-webkit-font-smoothing: antialiased; // Do not remove!
|
||||
font-family: $font-sans;
|
||||
font-size: 85%;
|
||||
line-height: 1.5em;
|
||||
color: $color-text-base;
|
||||
-webkit-font-smoothing: antialiased; // Do not remove!
|
||||
font-family: $font-sans;
|
||||
font-size: 85%;
|
||||
line-height: 1.5em;
|
||||
color: $color-text-base;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
@ -12,75 +12,75 @@ h3,
|
|||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
line-height: 1.3em;
|
||||
font-size: 1.5em;
|
||||
text-transform: uppercase;
|
||||
color: $color-grey-1;
|
||||
font-weight: 700;
|
||||
line-height: 1.3em;
|
||||
font-size: 1.5em;
|
||||
text-transform: uppercase;
|
||||
color: $color-grey-1;
|
||||
font-weight: 700;
|
||||
|
||||
span {
|
||||
text-transform: none;
|
||||
font-weight: 300;
|
||||
}
|
||||
span {
|
||||
text-transform: none;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3em;
|
||||
font-family: $font-sans;
|
||||
font-weight: 600;
|
||||
color: $color-grey-2;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3em;
|
||||
font-family: $font-sans;
|
||||
font-weight: 600;
|
||||
color: $color-grey-2;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
// @include transition(color 0.2s ease, background-color 0.2s ease);
|
||||
color: $color-link;
|
||||
text-decoration: none;
|
||||
// @include transition(color 0.2s ease, background-color 0.2s ease);
|
||||
color: $color-link;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $color-link-hover;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-link-hover;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
box-shadow: inset 0 0 4px 0 rgba(0, 0, 0, 0.2);
|
||||
background-color: $color-fieldset-hover;
|
||||
padding: 2px 5px;
|
||||
box-shadow: inset 0 0 4px 0 rgba(0, 0, 0, 0.2);
|
||||
background-color: $color-fieldset-hover;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
kbd {
|
||||
border-radius: 3px;
|
||||
font-family: $font-sans;
|
||||
border: 1px solid $color-grey-2;
|
||||
border-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 0.3em 0.5em;
|
||||
border-radius: 3px;
|
||||
font-family: $font-sans;
|
||||
border: 1px solid $color-grey-2;
|
||||
border-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl,
|
||||
dt,
|
||||
dd {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-top: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: $color-grey-2;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9em;
|
||||
color: $color-grey-2;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
// user avatars
|
||||
.avatar {
|
||||
border-radius: 100%;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border-radius: 100%;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.small {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 0 0.5em;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.small {
|
||||
vertical-align: middle;
|
||||
margin: 0 0.5em;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
&.large {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
&.square {
|
||||
border-radius: 0;
|
||||
|
||||
&:before {
|
||||
border-radius: 0;
|
||||
}
|
||||
&.large {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
&.square {
|
||||
border-radius: 0;
|
||||
|
||||
&:before {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
.o-pill {
|
||||
display: inline-block;
|
||||
padding: 0.2em 0.5em;
|
||||
border-radius: 0.25em;
|
||||
vertical-align: middle;
|
||||
line-height: 1.5;
|
||||
display: inline-block;
|
||||
padding: 0.2em 0.5em;
|
||||
border-radius: 0.25em;
|
||||
vertical-align: middle;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
// For dropdowns
|
||||
.o-icon {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
margin-top: -0.25rem;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.homepage h1 {
|
||||
text-transform: none;
|
||||
text-transform: none;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.page-explorer h2 {
|
||||
text-transform: none;
|
||||
text-transform: none;
|
||||
}
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
// Arrows
|
||||
// =============================================================================
|
||||
.u-arrow:before {
|
||||
content: '';
|
||||
border: solid 0.35rem transparent;
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: '';
|
||||
border: solid 0.35rem transparent;
|
||||
display: block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.u-arrow--tl:before {
|
||||
bottom: 100%;
|
||||
left: 1rem;
|
||||
bottom: 100%;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.dropup .u-arrow--tl:before {
|
||||
top: 100%;
|
||||
transform: rotateZ(180deg);
|
||||
top: 100%;
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
@ -26,85 +26,84 @@
|
|||
|
||||
// }
|
||||
.t-default .u-btn-current {
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
color: $color-teal;
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
color: $color-teal;
|
||||
}
|
||||
|
||||
.t-default .u-btn-current:hover {
|
||||
background: $color-teal;
|
||||
color: #fff;
|
||||
border-color: $color-teal;
|
||||
background: $color-teal;
|
||||
color: #fff;
|
||||
border-color: $color-teal;
|
||||
}
|
||||
|
||||
.t-default .u-btn-current:active {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-color: #333;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
.t-inverted .u-btn-current {
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
color: #fff;
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.t-inverted .u-btn-current:hover {
|
||||
background-color: $color-teal-darker;
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
background-color: $color-teal-darker;
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.t-inverted .u-btn-current:active {
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-color: rgba(0, 0, 0, 0.35);
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Dark theme
|
||||
// =============================================================================
|
||||
.t-dark .u-link {
|
||||
color: #fff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.t-dark .u-link:hover {
|
||||
color: #aaa;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.t-dark .u-background {
|
||||
background: #333;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.t-dark .u-arrow:before {
|
||||
border-bottom-color: #333;
|
||||
border-bottom-color: #333;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Light theme
|
||||
// =============================================================================
|
||||
.t-light .u-link {
|
||||
color: #333;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.t-light .u-link:hover {
|
||||
color: #aaa;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.t-light .u-background {
|
||||
background: #fff;
|
||||
border-color: #ccc;
|
||||
background: #fff;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.t-light .u-arrow:before {
|
||||
border-bottom-color: #fff;
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// States
|
||||
// =============================================================================
|
||||
.u-toggle {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-open .u-toggle {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// without individual components having to explicitly define focus styles.
|
||||
// Using !important because we want to enforce only one style is used across the UI.
|
||||
.focus-outline-on *:focus {
|
||||
outline: $focus-outline-width solid $color-focus-outline !important;
|
||||
outline: $focus-outline-width solid $color-focus-outline !important;
|
||||
}
|
||||
|
||||
.focus-outline-off *:focus {
|
||||
outline: none !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
|
|
@ -1,40 +1,40 @@
|
|||
// stylelint-disable declaration-no-important
|
||||
.u-hidden {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.u-hidden\@sm {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: none !important;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-hidden\@xs {
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: none !important;
|
||||
}
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-inline\@sm {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: inline !important;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: inline !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-inline\@xs {
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: inline !important;
|
||||
}
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: inline !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-block\@sm {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block !important;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.u-block\@xs {
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: block !important;
|
||||
}
|
||||
@include media-breakpoint-down(xs) {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +1,54 @@
|
|||
.clearfix {
|
||||
@include clearfix();
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
.nice-padding {
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
.divider-before {
|
||||
border-left: 1px solid $color-grey-4;
|
||||
}
|
||||
|
||||
.divider-after {
|
||||
border-right: 1px solid $color-grey-4;
|
||||
}
|
||||
.divider-before {
|
||||
border-left: 1px solid $color-grey-4;
|
||||
}
|
||||
|
||||
.divider-after {
|
||||
border-right: 1px solid $color-grey-4;
|
||||
}
|
||||
}
|
||||
|
||||
body.reordering {
|
||||
overflow: visible;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
// Show a transparency grid in background
|
||||
.show-transparency {
|
||||
background: url('#{$images-root}transparency.svg');
|
||||
background: url('#{$images-root}transparency.svg');
|
||||
}
|
||||
|
||||
// make a block-level element inline
|
||||
.inline {
|
||||
display: inline;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.unlist {
|
||||
@include unlist();
|
||||
@include unlist();
|
||||
}
|
||||
|
||||
// utility class to allow things to be scrollable if their contents can't wrap more nicely
|
||||
.overflow {
|
||||
overflow: auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.unbold {
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
.u-text-transform-uppercase {
|
||||
text-transform: uppercase;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.u-text-weight-normal {
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.u-para {
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.visuallyvisible {
|
||||
@include visuallyvisible;
|
||||
@include visuallyvisible;
|
||||
}
|
||||
|
||||
.visuallyhidden {
|
||||
@include visuallyhidden;
|
||||
@include visuallyhidden;
|
||||
}
|
||||
|
|
|
@ -1,328 +1,335 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
|
||||
.xdsoft_datetimepicker {
|
||||
box-shadow: 0 5px 10px -5px rgba(0, 0, 0, 0.4);
|
||||
background: $color-white;
|
||||
border: 1px solid $color-input-focus-border;
|
||||
padding: 8px;
|
||||
padding-left: 0;
|
||||
padding-top: 2px;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
box-shadow: 0 5px 10px -5px rgba(0, 0, 0, 0.4);
|
||||
background: $color-white;
|
||||
border: 1px solid $color-input-focus-border;
|
||||
padding: 8px;
|
||||
padding-left: 0;
|
||||
padding-top: 2px;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 75px;
|
||||
height: 210px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.xdsoft_datepicker,
|
||||
.xdsoft_timepicker {
|
||||
display: none;
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_datepicker {
|
||||
float: left;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.xdsoft_datepicker.active + .xdsoft_timepicker {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.xdsoft_mounthpicker {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.xdsoft_next,
|
||||
.xdsoft_prev,
|
||||
.xdsoft_today_button {
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: 2em;
|
||||
color: $color-teal;
|
||||
text-transform: none;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
font-size: 1.5em;
|
||||
font-family: $font-wagtail-icons;
|
||||
width: 1em;
|
||||
line-height: 1.3em;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
iframe {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 75px;
|
||||
height: 210px;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
&:hover {
|
||||
color: $color-teal-darker;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_datepicker,
|
||||
.xdsoft_timepicker {
|
||||
display: none;
|
||||
.xdsoft_prev {
|
||||
float: left;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
&:before {
|
||||
content: map.get($icons, 'arrow-left');
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_datepicker {
|
||||
float: left;
|
||||
margin-left: 8px;
|
||||
.xdsoft_today_button {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
|
||||
&:before {
|
||||
content: map.get($icons, 'home');
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_datepicker.active + .xdsoft_timepicker {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 3px;
|
||||
.xdsoft_next {
|
||||
float: right;
|
||||
|
||||
&:before {
|
||||
content: map.get($icons, 'arrow-right');
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_mounthpicker {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.xdsoft_timepicker {
|
||||
min-width: 70px;
|
||||
float: left;
|
||||
text-align: center;
|
||||
margin-left: 8px;
|
||||
margin-top: 0;
|
||||
|
||||
.xdsoft_next,
|
||||
.xdsoft_prev,
|
||||
.xdsoft_today_button {
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
width: 2em;
|
||||
color: $color-teal;
|
||||
text-transform: none;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
font-size: 1.5em;
|
||||
font-family: $font-wagtail-icons;
|
||||
width: 1em;
|
||||
line-height: 1.3em;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $color-teal-darker;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_prev {
|
||||
float: left;
|
||||
|
||||
&:before {
|
||||
content: map.get($icons, 'arrow-left');
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_today_button {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
|
||||
&:before {
|
||||
content: map.get($icons, 'home');
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_next {
|
||||
float: right;
|
||||
float: none;
|
||||
height: 1.5em;
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
|
||||
&:before {
|
||||
content: map.get($icons, 'arrow-right');
|
||||
}
|
||||
&:before {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_timepicker {
|
||||
min-width: 70px;
|
||||
float: left;
|
||||
text-align: center;
|
||||
margin-left: 8px;
|
||||
margin-top: 0;
|
||||
|
||||
.xdsoft_prev,
|
||||
.xdsoft_next {
|
||||
float: none;
|
||||
height: 1.5em;
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
|
||||
&:before {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_prev:before {
|
||||
content: map.get($icons, 'arrow-up');
|
||||
}
|
||||
|
||||
.xdsoft_next:before {
|
||||
content: map.get($icons, 'arrow-down');
|
||||
}
|
||||
|
||||
.xdsoft_time_box {
|
||||
position: relative;
|
||||
border: 1px solid #ccc;
|
||||
height: 170px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
> div > div {
|
||||
background: #f5f5f5;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #666;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
border-collapse: collapse;
|
||||
cursor: pointer;
|
||||
border-bottom-width: 0;
|
||||
height: 2.3em;
|
||||
line-height: 2.3em;
|
||||
padding-left: 0.6em;
|
||||
padding-right: 0.6em;
|
||||
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.xdsoft_prev:before {
|
||||
content: map.get($icons, 'arrow-up');
|
||||
}
|
||||
|
||||
.xdsoft_label {
|
||||
display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
background-color: $color-white;
|
||||
float: left;
|
||||
width: 182px;
|
||||
.xdsoft_next:before {
|
||||
content: map.get($icons, 'arrow-down');
|
||||
}
|
||||
|
||||
.xdsoft_time_box {
|
||||
position: relative;
|
||||
border: 1px solid #ccc;
|
||||
height: 170px;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
> div > div {
|
||||
background: #f5f5f5;
|
||||
border-top: 1px solid #ddd;
|
||||
color: #666;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
border-collapse: collapse;
|
||||
cursor: pointer;
|
||||
border-bottom-width: 0;
|
||||
height: 2.3em;
|
||||
line-height: 2.3em;
|
||||
padding-left: 0.6em;
|
||||
padding-right: 0.6em;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:first-child {
|
||||
border-top-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .xdsoft_select {
|
||||
border: 1px solid #ccc;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 30px;
|
||||
z-index: 101;
|
||||
display: none;
|
||||
background: $color-white;
|
||||
max-height: 160px;
|
||||
overflow-y: hidden;
|
||||
|
||||
&.xdsoft_monthselect {right: -7px;}
|
||||
|
||||
&.xdsoft_yearselect {right: 2px;}
|
||||
|
||||
> div > .xdsoft_option:hover {
|
||||
color: $color-white;
|
||||
background: #ff8000;
|
||||
}
|
||||
|
||||
> div > .xdsoft_option {
|
||||
padding: 2px 15px 2px 5px;
|
||||
}
|
||||
|
||||
> div > .xdsoft_option.xdsoft_current {
|
||||
background: #3af;
|
||||
color: $color-white;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
.xdsoft_label {
|
||||
display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
background-color: $color-white;
|
||||
float: left;
|
||||
width: 182px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.xdsoft_month {
|
||||
width: 90px;
|
||||
text-align: right;
|
||||
}
|
||||
> .xdsoft_select {
|
||||
border: 1px solid #ccc;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 30px;
|
||||
z-index: 101;
|
||||
display: none;
|
||||
background: $color-white;
|
||||
max-height: 160px;
|
||||
overflow-y: hidden;
|
||||
|
||||
.xdsoft_year {
|
||||
width: 56px;
|
||||
}
|
||||
&.xdsoft_monthselect {
|
||||
right: -7px;
|
||||
}
|
||||
|
||||
.xdsoft_calendar {
|
||||
clear: both;
|
||||
&.xdsoft_yearselect {
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
> div > .xdsoft_option:hover {
|
||||
color: $color-white;
|
||||
background: #ff8000;
|
||||
}
|
||||
|
||||
td > div {
|
||||
padding-right: 5px;
|
||||
}
|
||||
> div > .xdsoft_option {
|
||||
padding: 2px 15px 2px 5px;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
width: 14.285%;
|
||||
border: 1px solid #ddd;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
padding: 5px 7px;
|
||||
border-collapse: collapse;
|
||||
cursor: pointer;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f1f1f1;
|
||||
font-weight: 700;
|
||||
font-size: 0.85em;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_default,
|
||||
.xdsoft_calendar td.xdsoft_current,
|
||||
.xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current {
|
||||
background: $color-salmon;
|
||||
> div > .xdsoft_option.xdsoft_current {
|
||||
background: #3af;
|
||||
color: $color-white;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_month {
|
||||
width: 90px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.xdsoft_year {
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
.xdsoft_calendar {
|
||||
clear: both;
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_other_month,
|
||||
.xdsoft_calendar td.xdsoft_disabled,
|
||||
.xdsoft_time_box > div > div.xdsoft_disabled {
|
||||
opacity: 0.5;
|
||||
background: $color-grey-3;
|
||||
td > div {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
|
||||
opacity: 0.2;
|
||||
td,
|
||||
th {
|
||||
width: 14.285%;
|
||||
border: 1px solid #ddd;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
padding: 5px 7px;
|
||||
border-collapse: collapse;
|
||||
cursor: pointer;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td:hover,
|
||||
.xdsoft_timepicker .xdsoft_time_box > div > div:hover {
|
||||
color: $color-white;
|
||||
background: $color-teal;
|
||||
td {
|
||||
background-color: $color-white;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_today {
|
||||
font-weight: 700;
|
||||
th {
|
||||
background: #f1f1f1;
|
||||
font-weight: 700;
|
||||
font-size: 0.85em;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_default,
|
||||
.xdsoft_calendar td.xdsoft_current,
|
||||
.xdsoft_timepicker .xdsoft_time_box > div > div.xdsoft_current {
|
||||
background: $color-salmon;
|
||||
color: $color-white;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_other_month,
|
||||
.xdsoft_calendar td.xdsoft_disabled,
|
||||
.xdsoft_time_box > div > div.xdsoft_disabled {
|
||||
opacity: 0.5;
|
||||
background: $color-grey-3;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td:hover,
|
||||
.xdsoft_timepicker .xdsoft_time_box > div > div:hover {
|
||||
color: $color-white;
|
||||
background: $color-teal;
|
||||
}
|
||||
|
||||
.xdsoft_calendar td.xdsoft_today {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.xdsoft_noselect {
|
||||
user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.xdsoft_noselect::selection { background: transparent; }
|
||||
.xdsoft_noselect::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.xdsoft_noselect::-moz-selection { background: transparent; }
|
||||
.xdsoft_noselect::-moz-selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.xdsoft_datetimepicker.xdsoft_inline {
|
||||
display: inline-block;
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
display: inline-block;
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.xdsoft_scroller_box {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xdsoft_scrollbar {
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
cursor: pointer;
|
||||
|
||||
> .xdsoft_scroller {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
background: #ccc !important;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
> .xdsoft_scroller {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
background: #ccc !important;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +1,45 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
// taggit tagging
|
||||
.tagit {
|
||||
padding: 0.6em 1.2em;
|
||||
padding: 0.6em 1.2em;
|
||||
|
||||
.tagit-choice {
|
||||
border: 0;
|
||||
}
|
||||
.tagit-choice {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Additional specificity (.admin_tag_widget ) required to override tagit stylesheets,
|
||||
// which get added after the core CSS, and otherwise trump our styles.
|
||||
.admin_tag_widget ul.tagit input[type='text'] {
|
||||
padding: 0.2em 0.5em;
|
||||
padding: 0.2em 0.5em;
|
||||
}
|
||||
|
||||
// Additional specificity (.admin_tag_widget ) required to override tagit stylesheets,
|
||||
// which get added after the core CSS, and otherwise trump our styles.
|
||||
.admin_tag_widget ul.tagit li.tagit-choice-editable {
|
||||
padding: 0 23px 0 0;
|
||||
padding: 0 23px 0 0;
|
||||
}
|
||||
|
||||
.ui-front { // provided by jqueryui but not high enough an index
|
||||
z-index: 1000;
|
||||
.ui-front {
|
||||
// provided by jqueryui but not high enough an index
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.tagit-close {
|
||||
.ui-icon-close {
|
||||
margin-left: 1em;
|
||||
text-indent: 0;
|
||||
background: none;
|
||||
}
|
||||
.ui-icon-close {
|
||||
margin-left: 1em;
|
||||
text-indent: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.ui-icon-close:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
display: block;
|
||||
color: $color-grey-3;
|
||||
content: map.get($icons, 'cross');
|
||||
}
|
||||
.ui-icon-close:before {
|
||||
font-family: $font-wagtail-icons;
|
||||
display: block;
|
||||
color: $color-grey-3;
|
||||
content: map.get($icons, 'cross');
|
||||
}
|
||||
|
||||
.ui-icon-close:hover:before {
|
||||
color: $color-red;
|
||||
}
|
||||
.ui-icon-close:hover:before {
|
||||
color: $color-red;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +1,91 @@
|
|||
@use "sass:map";
|
||||
@use 'sass:map';
|
||||
$icons: (
|
||||
'arrow-down-big': '\e030',
|
||||
'arrow-down': '\e01a',
|
||||
'arrow-left': '\e022',
|
||||
'arrow-right': '\e017',
|
||||
'arrow-up-big': '\e02f',
|
||||
'arrow-up': '\e010',
|
||||
'arrows-up-down': '\e016',
|
||||
'bin': '\e038',
|
||||
'bold': '\e026',
|
||||
'chain-broken': '\e047',
|
||||
'code': '\e001',
|
||||
'cog': '\e020',
|
||||
'cogs': '\e00c',
|
||||
'collapse-down': '\e03f',
|
||||
'collapse-up': '\e03e',
|
||||
'cross': '\e012',
|
||||
'date': '\e045',
|
||||
'doc-empty-inverse': '\e00d',
|
||||
'doc-empty': '\e00e',
|
||||
'doc-full-inverse': '\e01b',
|
||||
'doc-full': '\e018',
|
||||
'download': '\e044',
|
||||
'duplicate': '\e902',
|
||||
'edit': '\e00f',
|
||||
'folder-inverse': '\e014',
|
||||
'folder-open-1': '\e013',
|
||||
'folder-open-inverse': '\e01f',
|
||||
'folder': '\e01c',
|
||||
'form': '\e00b',
|
||||
'grip': '\e03b',
|
||||
'group': '\e031',
|
||||
'help': '\e041',
|
||||
// help-inverse directly renders the corresponding character.
|
||||
'help-inverse': '?',
|
||||
'home': '\e035',
|
||||
// horizontalrule is not rendered as an icon font – it uses a unicode dash character rendered with a fallback font.
|
||||
'horizontalrule': '\2014',
|
||||
'image': '\e019',
|
||||
'italic': '\e027',
|
||||
'link': '\e02c',
|
||||
'list-ol': '\e029',
|
||||
'list-ul': '\e028',
|
||||
'locked': '\e009',
|
||||
'logout': '\e049',
|
||||
'mail': '\e015',
|
||||
'media': '\e032',
|
||||
'no-view': '\e006',
|
||||
'openquote': '\e000',
|
||||
'order-down': '\e036',
|
||||
'order-up': '\e037',
|
||||
'order': '\e034',
|
||||
'password': '\e033',
|
||||
'pick': '\e03d',
|
||||
'pilcrow': '\e002',
|
||||
'placeholder': '\e003',
|
||||
'plus-inverse': '\e024',
|
||||
'plus': '\e01d',
|
||||
'radio-empty': '\e02e',
|
||||
'radio-full': '\e02d',
|
||||
'redirect': '\e03c',
|
||||
'repeat': '\e02b',
|
||||
'search': '\e011',
|
||||
'site': '\e007',
|
||||
'snippet': '\e025',
|
||||
'spinner': '\e03a',
|
||||
'strikethrough': '\e04a',
|
||||
'subscript': '\e04c',
|
||||
'success': '\e043',
|
||||
'superscript': '\e04b',
|
||||
'table': '\e048',
|
||||
'tag': '\e01e',
|
||||
'tick-inverse': '\e023',
|
||||
'tick': '\e021',
|
||||
'time': '\e008',
|
||||
'title': '\e046',
|
||||
'undo': '\e02a',
|
||||
'unlocked': '\e00a',
|
||||
'user': '\e004',
|
||||
'view': '\e005',
|
||||
'wagtail-inverse': '\e040',
|
||||
'wagtail': '\e039',
|
||||
'warning': '\e042',
|
||||
'arrow-down-big': '\e030',
|
||||
'arrow-down': '\e01a',
|
||||
'arrow-left': '\e022',
|
||||
'arrow-right': '\e017',
|
||||
'arrow-up-big': '\e02f',
|
||||
'arrow-up': '\e010',
|
||||
'arrows-up-down': '\e016',
|
||||
'bin': '\e038',
|
||||
'bold': '\e026',
|
||||
'chain-broken': '\e047',
|
||||
'code': '\e001',
|
||||
'cog': '\e020',
|
||||
'cogs': '\e00c',
|
||||
'collapse-down': '\e03f',
|
||||
'collapse-up': '\e03e',
|
||||
'cross': '\e012',
|
||||
'date': '\e045',
|
||||
'doc-empty-inverse': '\e00d',
|
||||
'doc-empty': '\e00e',
|
||||
'doc-full-inverse': '\e01b',
|
||||
'doc-full': '\e018',
|
||||
'download': '\e044',
|
||||
'duplicate': '\e902',
|
||||
'edit': '\e00f',
|
||||
'folder-inverse': '\e014',
|
||||
'folder-open-1': '\e013',
|
||||
'folder-open-inverse': '\e01f',
|
||||
'folder': '\e01c',
|
||||
'form': '\e00b',
|
||||
'grip': '\e03b',
|
||||
'group': '\e031',
|
||||
'help': '\e041',
|
||||
// help-inverse directly renders the corresponding character.
|
||||
'help-inverse': '?',
|
||||
'home': '\e035',
|
||||
// horizontalrule is not rendered as an icon font – it uses a unicode dash character rendered with a fallback font.
|
||||
'horizontalrule': '\2014',
|
||||
'image': '\e019',
|
||||
'italic': '\e027',
|
||||
'link': '\e02c',
|
||||
'list-ol': '\e029',
|
||||
'list-ul': '\e028',
|
||||
'locked': '\e009',
|
||||
'logout': '\e049',
|
||||
'mail': '\e015',
|
||||
'media': '\e032',
|
||||
'no-view': '\e006',
|
||||
'openquote': '\e000',
|
||||
'order-down': '\e036',
|
||||
'order-up': '\e037',
|
||||
'order': '\e034',
|
||||
'password': '\e033',
|
||||
'pick': '\e03d',
|
||||
'pilcrow': '\e002',
|
||||
'placeholder': '\e003',
|
||||
'plus-inverse': '\e024',
|
||||
'plus': '\e01d',
|
||||
'radio-empty': '\e02e',
|
||||
'radio-full': '\e02d',
|
||||
'redirect': '\e03c',
|
||||
'repeat': '\e02b',
|
||||
'search': '\e011',
|
||||
'site': '\e007',
|
||||
'snippet': '\e025',
|
||||
'spinner': '\e03a',
|
||||
'strikethrough': '\e04a',
|
||||
'subscript': '\e04c',
|
||||
'success': '\e043',
|
||||
'superscript': '\e04b',
|
||||
'table': '\e048',
|
||||
'tag': '\e01e',
|
||||
'tick-inverse': '\e023',
|
||||
'tick': '\e021',
|
||||
'time': '\e008',
|
||||
'title': '\e046',
|
||||
'undo': '\e02a',
|
||||
'unlocked': '\e00a',
|
||||
'user': '\e004',
|
||||
'view': '\e005',
|
||||
'wagtail-inverse': '\e040',
|
||||
'wagtail': '\e039',
|
||||
'warning': '\e042',
|
||||
);
|
||||
|
||||
$icons-after: (
|
||||
'arrow-down-after': map.get($icons, 'arrow-down'),
|
||||
'arrow-right-after': map.get($icons, 'arrow-right'),
|
||||
'arrow-up-after': map.get($icons, 'arrow-up'),
|
||||
'arrow-down-after': map.get($icons, 'arrow-down'),
|
||||
'arrow-right-after': map.get($icons, 'arrow-right'),
|
||||
'arrow-up-after': map.get($icons, 'arrow-up'),
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@use "sass:color";
|
||||
@use 'sass:color';
|
||||
// paths
|
||||
|
||||
// We can't use absolute paths here, because those are dependent on Django's
|
||||
|
@ -19,11 +19,15 @@ $desktop-nice-padding: 50px;
|
|||
|
||||
// screen breakpoints
|
||||
$breakpoints: (
|
||||
xs: 0,
|
||||
sm: 50em, // 800px
|
||||
md: 56.25em, // 900px
|
||||
lg: 75em, // 1200px
|
||||
xl: 100em, // 1440px
|
||||
xs: 0,
|
||||
sm: 50em,
|
||||
// 800px
|
||||
md: 56.25em,
|
||||
// 900px
|
||||
lg: 75em,
|
||||
// 1200px
|
||||
xl: 100em,
|
||||
// 1440px
|
||||
);
|
||||
|
||||
// colours
|
||||
|
@ -62,7 +66,10 @@ $color-fieldset-hover: $color-grey-5;
|
|||
$color-input-border: $color-grey-4;
|
||||
$color-input-focus: var(--color-input-focus);
|
||||
$color-input-focus-border: var(--color-input-focus-border);
|
||||
$color-input-error-bg: color.adjust(color.adjust($color-red, $saturation: 28%), $lightness: 45%);
|
||||
$color-input-error-bg: color.adjust(
|
||||
color.adjust($color-red, $saturation: 28%),
|
||||
$lightness: 45%
|
||||
);
|
||||
|
||||
$color-button: $color-teal;
|
||||
$color-button-hover: $color-teal-darker;
|
||||
|
@ -71,7 +78,10 @@ $color-button-yes-hover: color.adjust($color-button-yes, $lightness: -8%);
|
|||
$color-button-no: $color-red-dark;
|
||||
$color-button-no-hover: color.adjust($color-button-no, $lightness: -20%);
|
||||
$color-button-warning: $color-orange-dark;
|
||||
$color-button-warning-hover: color.adjust($color-button-warning, $lightness: -20%);
|
||||
$color-button-warning-hover: color.adjust(
|
||||
$color-button-warning,
|
||||
$lightness: -20%
|
||||
);
|
||||
|
||||
$color-link: $color-teal-darker;
|
||||
$color-link-hover: $color-teal-dark;
|
||||
|
@ -95,7 +105,8 @@ $system-color-link-text: LinkText;
|
|||
$system-color-button-text: ButtonText;
|
||||
|
||||
// Fonts
|
||||
$font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
$font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
|
||||
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||
// Legacy icon font, to be removed in the near future.
|
||||
$font-wagtail-icons: wagtail;
|
||||
|
||||
|
@ -145,5 +156,13 @@ $nav-search-focus-bg: $nav-item-hover-bg;
|
|||
|
||||
// Form Errors
|
||||
$color-text-error: color.change($color-red, $saturation: 69%, $lightness: 52%);
|
||||
$color-text-error-forced-color: color.change($color-red, $saturation: 100%, $lightness: 50%);
|
||||
$color-text-warning-forced-color: color.change($color-orange, $saturation: 100%, $lightness: 70%);
|
||||
$color-text-error-forced-color: color.change(
|
||||
$color-red,
|
||||
$saturation: 100%,
|
||||
$lightness: 50%
|
||||
);
|
||||
$color-text-warning-forced-color: color.change(
|
||||
$color-orange,
|
||||
$saturation: 100%,
|
||||
$lightness: 70%
|
||||
);
|
||||
|
|
|
@ -10,7 +10,6 @@ see core.scss for details.
|
|||
|
||||
==============================================================================*/
|
||||
|
||||
|
||||
/* SETTINGS
|
||||
These are variables, maps, and fonts.
|
||||
* No CSS should be produced by these files
|
||||
|
@ -18,7 +17,6 @@ These are variables, maps, and fonts.
|
|||
|
||||
@import 'settings';
|
||||
|
||||
|
||||
/* TOOLS
|
||||
These are functions and mixins.
|
||||
* No CSS should be produced by these files.
|
||||
|
@ -26,7 +24,6 @@ These are functions and mixins.
|
|||
|
||||
@import 'tools';
|
||||
|
||||
|
||||
/* GENERIC
|
||||
This is for resets and other rules that affect large collections of bare elements.
|
||||
* Changes to them should be very rare.
|
||||
|
@ -34,7 +31,6 @@ This is for resets and other rules that affect large collections of bare element
|
|||
|
||||
@import 'generic/generic';
|
||||
|
||||
|
||||
/* ELEMENTS
|
||||
These are base styles for bare HTML elements.
|
||||
* Changes to them should be very rare.
|
||||
|
@ -45,7 +41,6 @@ These are base styles for bare HTML elements.
|
|||
@import 'elements/forms';
|
||||
@import 'elements/root';
|
||||
|
||||
|
||||
/* OBJECTS
|
||||
These are classes related to layout, known as 'objects' in ITCSS or OOCSS.
|
||||
* This is for grids, wrappers, and other non-consmetic layout utilities.
|
||||
|
@ -55,7 +50,6 @@ These are classes related to layout, known as 'objects' in ITCSS or OOCSS.
|
|||
@import 'objects/objects';
|
||||
@import 'objects/avatar';
|
||||
|
||||
|
||||
/* COMPONENTS
|
||||
These are classes for components.
|
||||
* These classes (unless legacy) are prefixed with `.c-`.
|
||||
|
@ -69,6 +63,5 @@ These are classes for components.
|
|||
@import '../src/components/PageExplorer/PageExplorer';
|
||||
@import '../src/components/Sidebar/Sidebar';
|
||||
|
||||
|
||||
// Legacy
|
||||
@import 'components/icons';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@use "sass:list";
|
||||
@use "sass:map";
|
||||
@use 'sass:list';
|
||||
@use 'sass:map';
|
||||
// Based upon the fine work and thoughts from Bootstrap v4.
|
||||
// Copyright 2011-2018 The Bootstrap Authors
|
||||
// Copyright 2011-2018 Twitter, Inc.
|
||||
|
@ -9,23 +9,27 @@
|
|||
// >> breakpoint-next(sm)
|
||||
// md
|
||||
@function breakpoint-next($name) {
|
||||
$breakpoint-names: map.keys($breakpoints);
|
||||
$n: list.index($breakpoint-names, $name);
|
||||
@return if($n < list.length($breakpoint-names), list.nth($breakpoint-names, $n + 1), null);
|
||||
$breakpoint-names: map.keys($breakpoints);
|
||||
$n: list.index($breakpoint-names, $name);
|
||||
@return if(
|
||||
$n < list.length($breakpoint-names),
|
||||
list.nth($breakpoint-names, $n + 1),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
// Minimum breakpoint width. Null for the smallest (first) breakpoint.
|
||||
// >> breakpoint-min(sm)
|
||||
// 50em
|
||||
@function breakpoint-min($name) {
|
||||
$min: map.get($breakpoints, $name);
|
||||
@return if($min != 0, $min, null);
|
||||
$min: map.get($breakpoints, $name);
|
||||
@return if($min != 0, $min, null);
|
||||
}
|
||||
|
||||
// Maximum breakpoint width. Null for the largest (last) breakpoint.
|
||||
// >> breakpoint-max(sm)
|
||||
// 56.1875em
|
||||
@function breakpoint-max($name) {
|
||||
$next: breakpoint-next($name);
|
||||
@return if($next, breakpoint-min($next) - 0.0625em, null);
|
||||
$next: breakpoint-next($name);
|
||||
@return if($next, breakpoint-min($next) - 0.0625em, null);
|
||||
}
|
||||
|
|
|
@ -3,29 +3,28 @@
|
|||
// Copyright 2011-2018 Twitter, Inc.
|
||||
// Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
|
||||
|
||||
// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
|
||||
// Makes the @content apply to the given breakpoint and wider.
|
||||
@mixin media-breakpoint-up($name) {
|
||||
$min: breakpoint-min($name);
|
||||
@if $min {
|
||||
@media screen and (min-width: $min) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
$min: breakpoint-min($name);
|
||||
@if $min {
|
||||
@media screen and (min-width: $min) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
|
||||
// Makes the @content apply to the given breakpoint and narrower.
|
||||
@mixin media-breakpoint-down($name) {
|
||||
$max: breakpoint-max($name);
|
||||
@if $max {
|
||||
@media screen and (max-width: $max) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
$max: breakpoint-max($name);
|
||||
@if $max {
|
||||
@media screen and (max-width: $max) {
|
||||
@content;
|
||||
}
|
||||
} @else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,111 +6,109 @@
|
|||
|
||||
// Turns on font-smoothing when used. Use sparingly.
|
||||
@mixin font-smoothing {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
@mixin clearfix() {
|
||||
&:before,
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
&:before,
|
||||
&:after {
|
||||
content: ' ';
|
||||
display: table;
|
||||
}
|
||||
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin unlist() {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// remove list styles, but only for the immediate element -
|
||||
// allow nested lists inside it to keep the default style
|
||||
|
||||
@mixin unlistimmediate() {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
|
||||
> li {
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
|
||||
> li {
|
||||
list-style-type: none;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin transition($transition...) {
|
||||
body.ready & {
|
||||
transition: $transition;
|
||||
}
|
||||
body.ready & {
|
||||
transition: $transition;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin visuallyhidden {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
|
||||
@mixin visuallyvisible {
|
||||
clip: auto;
|
||||
height: auto;
|
||||
width: auto;
|
||||
margin: initial;
|
||||
overflow: visible;
|
||||
position: initial;
|
||||
clip: auto;
|
||||
height: auto;
|
||||
width: auto;
|
||||
margin: initial;
|
||||
overflow: visible;
|
||||
position: initial;
|
||||
}
|
||||
|
||||
@mixin svg-icon ($size: 1.5em, $position: text-top) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
vertical-align: $position;
|
||||
@mixin svg-icon($size: 1.5em, $position: text-top) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
vertical-align: $position;
|
||||
}
|
||||
|
||||
@mixin icon () {
|
||||
@include font-smoothing;
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
speak: none;
|
||||
text-decoration: none;
|
||||
width: 1.3em;
|
||||
line-height: 1em;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.2em;
|
||||
@mixin icon() {
|
||||
@include font-smoothing;
|
||||
font-family: $font-wagtail-icons;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
speak: none;
|
||||
text-decoration: none;
|
||||
width: 1.3em;
|
||||
line-height: 1em;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
// Applies given rules on hover, except for touch screens.
|
||||
// Relies on feature detection to add a no-touch class on the html element.
|
||||
@mixin hover {
|
||||
.no-touch &:hover {
|
||||
@content;
|
||||
}
|
||||
.no-touch &:hover {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// Where included, show the focus outline within focusable items instead of around them.
|
||||
// This is useful when focusable items are tightly packed and there is no space in-between.
|
||||
@mixin show-focus-outline-inside {
|
||||
outline-offset: -1 * $focus-outline-width;
|
||||
outline-offset: -1 * $focus-outline-width;
|
||||
}
|
||||
|
|
|
@ -1,75 +1,75 @@
|
|||
@use "sass:math";
|
||||
@use 'sass:math';
|
||||
|
||||
// Utility variable - you should never need to modify this
|
||||
$padding: math.div($grid-gutter-width, 2);
|
||||
|
||||
// Our row container
|
||||
@mixin row($padding: 0) {
|
||||
@include clearfix();
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: $padding;
|
||||
padding-left: $padding;
|
||||
@include clearfix();
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-right: $padding;
|
||||
padding-left: $padding;
|
||||
}
|
||||
|
||||
@mixin row-flush() {
|
||||
margin-left: -$padding;
|
||||
margin-right: -$padding;
|
||||
margin-left: -$padding;
|
||||
margin-right: -$padding;
|
||||
}
|
||||
|
||||
// Our column container
|
||||
@mixin column($x, $padding: $padding, $grid-columns: $grid-columns) {
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
float: left;
|
||||
width: 100% * math.div($x, $grid-columns);
|
||||
padding-right: $padding;
|
||||
padding-left: $padding;
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
float: left;
|
||||
width: 100% * math.div($x, $grid-columns);
|
||||
padding-right: $padding;
|
||||
padding-left: $padding;
|
||||
}
|
||||
|
||||
@mixin table-column($x, $padding: $padding, $grid-columns: $grid-columns) {
|
||||
box-sizing: border-box;
|
||||
width: 100% * math.div($x, $grid-columns);
|
||||
box-sizing: border-box;
|
||||
width: 100% * math.div($x, $grid-columns);
|
||||
}
|
||||
|
||||
// Push adds left padding
|
||||
@mixin push($offset: 1, $grid-columns: $grid-columns) {
|
||||
margin-left: 100% * math.div($offset, $grid-columns);
|
||||
margin-left: 100% * math.div($offset, $grid-columns);
|
||||
}
|
||||
|
||||
@mixin push-padding($offset: 1, $grid-columns: $grid-columns) {
|
||||
padding-left: 100% * math.div($offset, $grid-columns);
|
||||
padding-left: 100% * math.div($offset, $grid-columns);
|
||||
}
|
||||
|
||||
// Pull adds right padding
|
||||
@mixin pull($offset: 1, $grid-columns: $grid-columns) {
|
||||
margin-right: 100% * math.div($offset, $grid-columns);
|
||||
margin-right: 100% * math.div($offset, $grid-columns);
|
||||
}
|
||||
|
||||
@mixin pull-padding($offset: 1, $grid-columns: $grid-columns) {
|
||||
padding-right: 100% * math.div($offset, $grid-columns);
|
||||
padding-right: 100% * math.div($offset, $grid-columns);
|
||||
}
|
||||
|
||||
// only used in places where padding not applied to same elements as row or row-flush
|
||||
// most of the time this class should be applied directly to the html elements
|
||||
@mixin nice-padding {
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
padding-left: $mobile-nice-padding;
|
||||
padding-right: $mobile-nice-padding;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding-left: $desktop-nice-padding;
|
||||
padding-right: $desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin nice-margin {
|
||||
margin-left: $mobile-nice-padding;
|
||||
margin-right: $mobile-nice-padding;
|
||||
margin-left: $mobile-nice-padding;
|
||||
margin-right: $mobile-nice-padding;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: $desktop-nice-padding;
|
||||
margin-right: $desktop-nice-padding;
|
||||
}
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: $desktop-nice-padding;
|
||||
margin-right: $desktop-nice-padding;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,67 +1,73 @@
|
|||
@use "sass:math";
|
||||
@use "sass:color";
|
||||
@use "sass:list";
|
||||
@use "sass:meta";
|
||||
@use 'sass:math';
|
||||
@use 'sass:color';
|
||||
@use 'sass:list';
|
||||
@use 'sass:meta';
|
||||
|
||||
// $color is either a color or an hsl tuple
|
||||
@mixin define-color($name, $color) {
|
||||
$h: null;
|
||||
$s: null;
|
||||
$l: null;
|
||||
$h: null;
|
||||
$s: null;
|
||||
$l: null;
|
||||
|
||||
@if meta.type-of($color) == color {
|
||||
$h: math.div(color.hue($color), 1deg); // Cast to unitless
|
||||
$s: color.saturation($color);
|
||||
$l: color.lightness($color);
|
||||
} @else {
|
||||
$h: list.nth($color, 1);
|
||||
$s: list.nth($color, 2);
|
||||
$l: list.nth($color, 3);
|
||||
}
|
||||
@if meta.type-of($color) == color {
|
||||
$h: math.div(color.hue($color), 1deg); // Cast to unitless
|
||||
$s: color.saturation($color);
|
||||
$l: color.lightness($color);
|
||||
} @else {
|
||||
$h: list.nth($color, 1);
|
||||
$s: list.nth($color, 2);
|
||||
$l: list.nth($color, 3);
|
||||
}
|
||||
|
||||
--#{$name}-hue: #{$h};
|
||||
--#{$name}-saturation: #{$s};
|
||||
--#{$name}-lightness: #{$l};
|
||||
--#{$name}: hsl(#{ var(--#{$name}-hue), var(--#{$name}-saturation), var(--#{$name}-lightness) });
|
||||
--#{$name}-hue: #{$h};
|
||||
--#{$name}-saturation: #{$s};
|
||||
--#{$name}-lightness: #{$l};
|
||||
// Prettier causes a linting issue when reformatting this.
|
||||
/* prettier-ignore */
|
||||
--#{$name}: hsl(#{ var(--#{$name}-hue), var(--#{$name}-saturation), var(--#{$name}-lightness) });
|
||||
}
|
||||
|
||||
@function get-color($name) {
|
||||
@return (var(--#{$name}-hue), var(--#{$name}-saturation), var(--#{$name}-lightness));
|
||||
@return (
|
||||
var(--#{$name}-hue),
|
||||
var(--#{$name}-saturation),
|
||||
var(--#{$name}-lightness)
|
||||
);
|
||||
}
|
||||
|
||||
@function css-darken($hsl-tuple, $darken-by) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, calc(#{$l} - #{$darken-by + 0%}));
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, calc(#{$l} - #{$darken-by + 0%}));
|
||||
}
|
||||
@function css-lighten($hsl-tuple, $lighten-by) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, calc(#{$l} + #{$lighten-by + 0%}));
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, calc(#{$l} + #{$lighten-by + 0%}));
|
||||
}
|
||||
@function css-saturate($hsl-tuple, $saturate-by) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, calc(#{$s} + #{$saturate-by + 0%}), $l);
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, calc(#{$s} + #{$saturate-by + 0%}), $l);
|
||||
}
|
||||
@function css-desaturate($hsl-tuple, $desaturate-by) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, calc(#{$s} - #{$desaturate-by + 0%}), $l);
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, calc(#{$s} - #{$desaturate-by + 0%}), $l);
|
||||
}
|
||||
@function css-adjust-hue($hsl-tuple, $adjust-by) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return (calc(#{$h} + #{$adjust-by}), $s, $l);
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return (calc(#{$h} + #{$adjust-by}), $s, $l);
|
||||
}
|
||||
@function css-transparentize($hsl-tuple, $alpha) {
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, $l, $alpha);
|
||||
$h: list.nth($hsl-tuple, 1);
|
||||
$s: list.nth($hsl-tuple, 2);
|
||||
$l: list.nth($hsl-tuple, 3);
|
||||
@return ($h, $s, $l, $alpha);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ const stubResult = {
|
|||
verbose_name: 'Test',
|
||||
},
|
||||
},
|
||||
items: [
|
||||
{ meta: { type: 'test' } },
|
||||
{ meta: { type: 'foo' } },
|
||||
],
|
||||
items: [{ meta: { type: 'test' } }, { meta: { type: 'foo' } }],
|
||||
};
|
||||
|
||||
client.get = jest.fn(() => Promise.resolve(stubResult));
|
||||
|
@ -20,27 +17,29 @@ describe('admin API', () => {
|
|||
describe('getPageChildren', () => {
|
||||
it('works', () => {
|
||||
getPageChildren(3);
|
||||
expect(client.get).toBeCalledWith(`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent`);
|
||||
expect(client.get).toBeCalledWith(
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent`,
|
||||
);
|
||||
});
|
||||
|
||||
it('#fields', () => {
|
||||
getPageChildren(3, { fields: ['title', 'latest_revision_created_at'] });
|
||||
expect(client.get).toBeCalledWith(
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent,title%2Clatest_revision_created_at`
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent,title%2Clatest_revision_created_at`,
|
||||
);
|
||||
});
|
||||
|
||||
it('#onlyWithChildren', () => {
|
||||
getPageChildren(3, { onlyWithChildren: true });
|
||||
expect(client.get).toBeCalledWith(
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent&has_children=1`
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent&has_children=1`,
|
||||
);
|
||||
});
|
||||
|
||||
it('#offset', () => {
|
||||
getPageChildren(3, { offset: 5 });
|
||||
expect(client.get).toBeCalledWith(
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent&offset=5`
|
||||
`${ADMIN_API.PAGES}?child_of=3&for_explorer=1&fields=parent&offset=5`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,9 +8,9 @@ export interface WagtailPageAPI {
|
|||
status: {
|
||||
status: string;
|
||||
live: boolean;
|
||||
|
||||
|
||||
has_unpublished_changes: boolean;
|
||||
}
|
||||
};
|
||||
children: any;
|
||||
parent: {
|
||||
id: number;
|
||||
|
@ -18,13 +18,12 @@ export interface WagtailPageAPI {
|
|||
locale?: string;
|
||||
translations?: any;
|
||||
};
|
||||
|
||||
|
||||
admin_display_title?: string;
|
||||
}
|
||||
|
||||
interface WagtailPageListAPI {
|
||||
meta: {
|
||||
|
||||
total_count: number;
|
||||
};
|
||||
items: WagtailPageAPI[];
|
||||
|
@ -42,12 +41,17 @@ interface GetPageChildrenOptions {
|
|||
offset?: number;
|
||||
}
|
||||
|
||||
type GetPageChildren = (id: number, options: GetPageChildrenOptions) => Promise<WagtailPageListAPI>;
|
||||
type GetPageChildren = (
|
||||
id: number,
|
||||
options: GetPageChildrenOptions,
|
||||
) => Promise<WagtailPageListAPI>;
|
||||
export const getPageChildren: GetPageChildren = (id, options = {}) => {
|
||||
let url = `${ADMIN_API.PAGES}?child_of=${id}&for_explorer=1`;
|
||||
|
||||
if (options.fields) {
|
||||
url += `&fields=parent,${window.encodeURIComponent(options.fields.join(','))}`;
|
||||
url += `&fields=parent,${window.encodeURIComponent(
|
||||
options.fields.join(','),
|
||||
)}`;
|
||||
} else {
|
||||
url += '&fields=parent';
|
||||
}
|
||||
|
@ -70,12 +74,17 @@ interface GetPageTranslationsOptions {
|
|||
onlyWithChildren?: boolean;
|
||||
offset?: number;
|
||||
}
|
||||
type GetPageTranslations = (id: number, options: GetPageTranslationsOptions) => Promise<WagtailPageListAPI>;
|
||||
type GetPageTranslations = (
|
||||
id: number,
|
||||
options: GetPageTranslationsOptions,
|
||||
) => Promise<WagtailPageListAPI>;
|
||||
export const getPageTranslations: GetPageTranslations = (id, options = {}) => {
|
||||
let url = `${ADMIN_API.PAGES}?translation_of=${id}&limit=20`;
|
||||
|
||||
if (options.fields) {
|
||||
url += `&fields=parent,${global.encodeURIComponent(options.fields.join(','))}`;
|
||||
url += `&fields=parent,${global.encodeURIComponent(
|
||||
options.fields.join(','),
|
||||
)}`;
|
||||
} else {
|
||||
url += '&fields=parent';
|
||||
}
|
||||
|
@ -96,14 +105,20 @@ interface GetAllPageTranslationsOptions {
|
|||
onlyWithChildren?: boolean;
|
||||
}
|
||||
|
||||
export const getAllPageTranslations = async (id: number, options: GetAllPageTranslationsOptions) => {
|
||||
export const getAllPageTranslations = async (
|
||||
id: number,
|
||||
options: GetAllPageTranslationsOptions,
|
||||
) => {
|
||||
const items: WagtailPageAPI[] = [];
|
||||
let iterLimit = 100;
|
||||
|
||||
for (;;) {
|
||||
const page = await getPageTranslations(id, { offset: items.length, ...options });
|
||||
const page = await getPageTranslations(id, {
|
||||
offset: items.length,
|
||||
...options,
|
||||
});
|
||||
|
||||
page.items.forEach(item => items.push(item));
|
||||
page.items.forEach((item) => items.push(item));
|
||||
|
||||
if (items.length >= page.meta.total_count || iterLimit-- <= 0) {
|
||||
return items;
|
||||
|
|
|
@ -3,7 +3,7 @@ const Headers = global.Headers;
|
|||
|
||||
const REQUEST_TIMEOUT = 15000;
|
||||
|
||||
const checkStatus = (response) => {
|
||||
const checkStatus = (response) => {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const checkStatus = (response) => {
|
|||
throw error;
|
||||
};
|
||||
|
||||
const parseJSON = response => response.json();
|
||||
const parseJSON = (response) => response.json();
|
||||
|
||||
// Response timeout cancelling the promise (not the request).
|
||||
// See https://github.com/github/fetch/issues/175#issuecomment-216791333.
|
||||
|
@ -23,13 +23,16 @@ const timeout = (ms, promise) => {
|
|||
reject(new Error('Response timeout'));
|
||||
}, ms);
|
||||
|
||||
promise.then((res) => {
|
||||
clearTimeout(timeoutId);
|
||||
resolve(res);
|
||||
}, (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
reject(err);
|
||||
});
|
||||
promise.then(
|
||||
(res) => {
|
||||
clearTimeout(timeoutId);
|
||||
resolve(res);
|
||||
},
|
||||
(err) => {
|
||||
clearTimeout(timeoutId);
|
||||
reject(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
return race;
|
||||
|
@ -46,7 +49,7 @@ const request = (method, url) => {
|
|||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
method: method
|
||||
method: method,
|
||||
};
|
||||
|
||||
return timeout(REQUEST_TIMEOUT, fetch(url, options))
|
||||
|
@ -54,4 +57,4 @@ const request = (method, url) => {
|
|||
.then(parseJSON);
|
||||
};
|
||||
|
||||
export const get = url => request('GET', url);
|
||||
export const get = (url) => request('GET', url);
|
||||
|
|
|
@ -17,7 +17,9 @@ describe('Button', () => {
|
|||
});
|
||||
|
||||
it('#accessibleLabel', () => {
|
||||
expect(shallow(<Button accessibleLabel="I am here in the shadows" />)).toMatchSnapshot();
|
||||
expect(
|
||||
shallow(<Button accessibleLabel="I am here in the shadows" />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('#dialogTrigger', () => {
|
||||
|
@ -25,7 +27,9 @@ describe('Button', () => {
|
|||
});
|
||||
|
||||
it('#target', () => {
|
||||
expect(shallow(<Button target="_blank" rel="noopener noreferrer" />)).toMatchSnapshot();
|
||||
expect(
|
||||
shallow(<Button target="_blank" rel="noopener noreferrer" />),
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('is clickable', () => {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
const handleClick = (
|
||||
|
@ -7,7 +5,7 @@ const handleClick = (
|
|||
onClick: ((e: React.MouseEvent) => void) | undefined,
|
||||
preventDefault: boolean,
|
||||
navigate: (url: string) => Promise<void>,
|
||||
e: React.MouseEvent
|
||||
e: React.MouseEvent,
|
||||
) => {
|
||||
if (preventDefault && href === '#') {
|
||||
e.preventDefault();
|
||||
|
@ -52,9 +50,7 @@ const Button: React.FunctionComponent<ButtonProps> = ({
|
|||
}) => {
|
||||
const hasText = React.Children.count(children) > 0;
|
||||
const accessibleElt = accessibleLabel ? (
|
||||
<span className="visuallyhidden">
|
||||
{accessibleLabel}
|
||||
</span>
|
||||
<span className="visuallyhidden">{accessibleLabel}</span>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { Comment, CommentReply, CommentsState } from '../state/comments';
|
||||
|
||||
|
||||
const remoteReply: CommentReply = {
|
||||
localId: 2,
|
||||
remoteId: 2,
|
||||
|
@ -41,7 +40,10 @@ const remoteComment: Comment = {
|
|||
newReply: '',
|
||||
newText: '',
|
||||
remoteReplyCount: 1,
|
||||
replies: new Map([[remoteReply.localId, remoteReply], [localReply.localId, localReply]]),
|
||||
replies: new Map([
|
||||
[remoteReply.localId, remoteReply],
|
||||
[localReply.localId, localReply],
|
||||
]),
|
||||
};
|
||||
|
||||
const localComment: Comment = {
|
||||
|
@ -68,5 +70,8 @@ export const basicCommentsState: CommentsState = {
|
|||
forceFocus: false,
|
||||
pinnedComment: 1,
|
||||
remoteCommentCount: 1,
|
||||
comments: new Map([[remoteComment.localId, remoteComment], [localComment.localId, localComment]]),
|
||||
comments: new Map([
|
||||
[remoteComment.localId, remoteComment],
|
||||
[localComment.localId, localComment],
|
||||
]),
|
||||
};
|
||||
|
|
|
@ -90,7 +90,7 @@ export function addComment(comment: Comment): AddCommentAction {
|
|||
|
||||
export function updateComment(
|
||||
commentId: number,
|
||||
update: CommentUpdate
|
||||
update: CommentUpdate,
|
||||
): UpdateCommentAction {
|
||||
return {
|
||||
type: UPDATE_COMMENT,
|
||||
|
@ -113,22 +113,24 @@ export function resolveComment(commentId: number): ResolveCommentAction {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
export function setFocusedComment(
|
||||
commentId: number | null,
|
||||
{ updatePinnedComment, forceFocus } = { updatePinnedComment: false, forceFocus: false }
|
||||
{ updatePinnedComment, forceFocus } = {
|
||||
updatePinnedComment: false,
|
||||
forceFocus: false,
|
||||
},
|
||||
): SetFocusedCommentAction {
|
||||
return {
|
||||
type: SET_FOCUSED_COMMENT,
|
||||
commentId,
|
||||
updatePinnedComment,
|
||||
forceFocus
|
||||
forceFocus,
|
||||
};
|
||||
}
|
||||
|
||||
export function addReply(
|
||||
commentId: number,
|
||||
reply: CommentReply
|
||||
reply: CommentReply,
|
||||
): AddReplyAction {
|
||||
return {
|
||||
type: ADD_REPLY,
|
||||
|
@ -140,7 +142,7 @@ export function addReply(
|
|||
export function updateReply(
|
||||
commentId: number,
|
||||
replyId: number,
|
||||
update: CommentReplyUpdate
|
||||
update: CommentReplyUpdate,
|
||||
): UpdateReplyAction {
|
||||
return {
|
||||
type: UPDATE_REPLY,
|
||||
|
@ -152,7 +154,7 @@ export function updateReply(
|
|||
|
||||
export function deleteReply(
|
||||
commentId: number,
|
||||
replyId: number
|
||||
replyId: number,
|
||||
): DeleteReplyAction {
|
||||
return {
|
||||
type: DELETE_REPLY,
|
||||
|
@ -161,7 +163,9 @@ export function deleteReply(
|
|||
};
|
||||
}
|
||||
|
||||
export function invalidateContentPath(contentPath: string): InvalidateContentPathAction {
|
||||
export function invalidateContentPath(
|
||||
contentPath: string,
|
||||
): InvalidateContentPathAction {
|
||||
return {
|
||||
type: INVALIDATE_CONTENT_PATH,
|
||||
contentPath,
|
||||
|
|
|
@ -10,7 +10,7 @@ export interface UpdateGlobalSettingsAction {
|
|||
export type Action = UpdateGlobalSettingsAction;
|
||||
|
||||
export function updateGlobalSettings(
|
||||
update: SettingsStateUpdate
|
||||
update: SettingsStateUpdate,
|
||||
): UpdateGlobalSettingsAction {
|
||||
return {
|
||||
type: UPDATE_GLOBAL_SETTINGS,
|
||||
|
|
|
@ -41,7 +41,8 @@ export function commentFromSomeoneElse() {
|
|||
author: {
|
||||
id: 2,
|
||||
name: 'Someone else',
|
||||
avatarUrl: 'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -72,8 +73,8 @@ export function commentFromSomeoneWithAReallyLongName() {
|
|||
author: {
|
||||
id: 1,
|
||||
name: 'This person has a really long name and it should wrap to the next line',
|
||||
avatarUrl: 'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
|
@ -12,20 +10,20 @@ import {
|
|||
deleteComment,
|
||||
resolveComment,
|
||||
setFocusedComment,
|
||||
addReply
|
||||
addReply,
|
||||
} from '../../actions/comments';
|
||||
import { LayoutController } from '../../utils/layout';
|
||||
import { getNextReplyId } from '../../utils/sequences';
|
||||
import CommentReplyComponent from '../CommentReply';
|
||||
import type { TranslatableStrings } from '../../main';
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
import TextArea from '../TextArea';
|
||||
|
||||
async function saveComment(comment: Comment, store: Store) {
|
||||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'saving',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -36,7 +34,7 @@ async function saveComment(comment: Comment, store: Store) {
|
|||
remoteId: comment.remoteId,
|
||||
author: comment.author,
|
||||
date: comment.date,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
/* eslint-disable-next-line no-console */
|
||||
|
@ -44,7 +42,7 @@ async function saveComment(comment: Comment, store: Store) {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'save_error',
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +51,7 @@ async function doDeleteComment(comment: Comment, store: Store) {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'deleting',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -64,15 +62,13 @@ async function doDeleteComment(comment: Comment, store: Store) {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'delete_error',
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function doResolveComment(comment: Comment, store: Store) {
|
||||
store.dispatch(
|
||||
resolveComment(comment.localId)
|
||||
);
|
||||
store.dispatch(resolveComment(comment.localId));
|
||||
}
|
||||
|
||||
export interface CommentProps {
|
||||
|
@ -99,7 +95,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
newReply: value,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -116,7 +112,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
newReply: '',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -126,7 +122,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
newReply: '',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
store.dispatch(setFocusedComment(null));
|
||||
|
@ -152,7 +148,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
reply={reply}
|
||||
strings={strings}
|
||||
isFocused={isFocused}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +206,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
newText: value,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -244,7 +240,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
onChange={onChangeText}
|
||||
placeholder="Enter your comments..."
|
||||
additionalAttributes={{
|
||||
'aria-describedby': descriptionId
|
||||
'aria-describedby': descriptionId,
|
||||
}}
|
||||
/>
|
||||
<div className="comment__actions">
|
||||
|
@ -275,7 +271,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
newText: value,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -292,7 +288,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
updateComment(comment.localId, {
|
||||
mode: 'default',
|
||||
newText: comment.text,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -313,7 +309,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
className="comment__input"
|
||||
value={comment.newText}
|
||||
additionalAttributes={{
|
||||
'aria-describedby': descriptionId
|
||||
'aria-describedby': descriptionId,
|
||||
}}
|
||||
onChange={onChangeText}
|
||||
/>
|
||||
|
@ -368,7 +364,12 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={comment} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={comment}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment__text">{comment.text}</p>
|
||||
{this.renderReplies({ hideNewReply: true })}
|
||||
<div className="comment__error">
|
||||
|
@ -400,13 +401,18 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'default',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={comment} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={comment}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment__text">{comment.text}</p>
|
||||
<div className="comment__confirm-delete">
|
||||
{strings.CONFIRM_DELETE_COMMENT}
|
||||
|
@ -435,7 +441,12 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={comment} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={comment}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment__text">{comment.text}</p>
|
||||
<div className="comment__progress">{strings.DELETING}</div>
|
||||
{this.renderReplies({ hideNewReply: true })}
|
||||
|
@ -458,13 +469,18 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'default',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={comment} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={comment}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment__text">{comment.text}</p>
|
||||
{this.renderReplies({ hideNewReply: true })}
|
||||
<div className="comment__error">
|
||||
|
@ -494,13 +510,16 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
// Show edit/delete buttons if this comment was authored by the current user
|
||||
let onEdit;
|
||||
let onDelete;
|
||||
if (comment.author === null || this.props.user && this.props.user.id === comment.author.id) {
|
||||
if (
|
||||
comment.author === null ||
|
||||
(this.props.user && this.props.user.id === comment.author.id)
|
||||
) {
|
||||
onEdit = () => {
|
||||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'editing',
|
||||
newText: comment.text,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -508,7 +527,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
store.dispatch(
|
||||
updateComment(comment.localId, {
|
||||
mode: 'delete_confirm',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -534,14 +553,14 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment__text">{comment.text}</p>
|
||||
{notice &&
|
||||
{notice && (
|
||||
<div className="comment__notice-placeholder">
|
||||
<div className="comment__notice" role="status">
|
||||
<Icon name="info-circle" />
|
||||
{notice}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
{this.renderReplies()}
|
||||
</>
|
||||
);
|
||||
|
@ -551,78 +570,90 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
let inner: React.ReactFragment;
|
||||
|
||||
switch (this.props.comment.mode) {
|
||||
case 'creating':
|
||||
inner = this.renderCreating();
|
||||
break;
|
||||
case 'creating':
|
||||
inner = this.renderCreating();
|
||||
break;
|
||||
|
||||
case 'editing':
|
||||
inner = this.renderEditing();
|
||||
break;
|
||||
case 'editing':
|
||||
inner = this.renderEditing();
|
||||
break;
|
||||
|
||||
case 'saving':
|
||||
inner = this.renderSaving();
|
||||
break;
|
||||
case 'saving':
|
||||
inner = this.renderSaving();
|
||||
break;
|
||||
|
||||
case 'save_error':
|
||||
inner = this.renderSaveError();
|
||||
break;
|
||||
case 'save_error':
|
||||
inner = this.renderSaveError();
|
||||
break;
|
||||
|
||||
case 'delete_confirm':
|
||||
inner = this.renderDeleteConfirm();
|
||||
break;
|
||||
case 'delete_confirm':
|
||||
inner = this.renderDeleteConfirm();
|
||||
break;
|
||||
|
||||
case 'deleting':
|
||||
inner = this.renderDeleting();
|
||||
break;
|
||||
case 'deleting':
|
||||
inner = this.renderDeleting();
|
||||
break;
|
||||
|
||||
case 'delete_error':
|
||||
inner = this.renderDeleteError();
|
||||
break;
|
||||
case 'delete_error':
|
||||
inner = this.renderDeleteError();
|
||||
break;
|
||||
|
||||
default:
|
||||
inner = this.renderDefault();
|
||||
break;
|
||||
default:
|
||||
inner = this.renderDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
this.props.store.dispatch(
|
||||
setFocusedComment(this.props.comment.localId,
|
||||
{ updatePinnedComment: false, forceFocus: this.props.isFocused && this.props.forceFocus }
|
||||
)
|
||||
setFocusedComment(this.props.comment.localId, {
|
||||
updatePinnedComment: false,
|
||||
forceFocus: this.props.isFocused && this.props.forceFocus,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const onDoubleClick = () => {
|
||||
this.props.store.dispatch(
|
||||
setFocusedComment(this.props.comment.localId, { updatePinnedComment: true, forceFocus: true })
|
||||
setFocusedComment(this.props.comment.localId, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const top = this.props.layout.getCommentPosition(
|
||||
this.props.comment.localId
|
||||
this.props.comment.localId,
|
||||
);
|
||||
|
||||
return (
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
preventScroll: true,
|
||||
clickOutsideDeactivates: true,
|
||||
onDeactivate: () => {
|
||||
this.props.store.dispatch(
|
||||
setFocusedComment(null, { updatePinnedComment: true, forceFocus: false })
|
||||
);
|
||||
},
|
||||
initialFocus: '[data-focus-target="true"]',
|
||||
} as any} // For some reason, the types for FocusTrap props don't yet include preventScroll.
|
||||
focusTrapOptions={
|
||||
{
|
||||
preventScroll: true,
|
||||
clickOutsideDeactivates: true,
|
||||
onDeactivate: () => {
|
||||
this.props.store.dispatch(
|
||||
setFocusedComment(null, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: false,
|
||||
}),
|
||||
);
|
||||
},
|
||||
initialFocus: '[data-focus-target="true"]',
|
||||
} as any
|
||||
} // For some reason, the types for FocusTrap props don't yet include preventScroll.
|
||||
active={this.props.isFocused && this.props.forceFocus}
|
||||
>
|
||||
<li
|
||||
tabIndex={-1}
|
||||
data-focus-target={this.props.isFocused && !['creating', 'editing'].includes(this.props.comment.mode)}
|
||||
key={this.props.comment.localId}
|
||||
className={
|
||||
`comment comment--mode-${this.props.comment.mode} ${this.props.isFocused ? 'comment--focused' : ''}`
|
||||
data-focus-target={
|
||||
this.props.isFocused &&
|
||||
!['creating', 'editing'].includes(this.props.comment.mode)
|
||||
}
|
||||
key={this.props.comment.localId}
|
||||
className={`comment comment--mode-${this.props.comment.mode} ${
|
||||
this.props.isFocused ? 'comment--focused' : ''
|
||||
}`}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: `${top}px`,
|
||||
|
@ -648,7 +679,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
if (this.props.isVisible) {
|
||||
this.props.layout.setCommentHeight(
|
||||
this.props.comment.localId,
|
||||
element.offsetHeight
|
||||
element.offsetHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -666,7 +697,7 @@ export default class CommentComponent extends React.Component<CommentProps> {
|
|||
if (this.props.isVisible && element instanceof HTMLElement) {
|
||||
this.props.layout.setCommentHeight(
|
||||
this.props.comment.localId,
|
||||
element.offsetHeight
|
||||
element.offsetHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,151 +1,151 @@
|
|||
.comment {
|
||||
@include box;
|
||||
@include box;
|
||||
|
||||
width: calc(100vw - 40px);
|
||||
max-width: calc(100vw - 19%);
|
||||
display: block;
|
||||
transition: top 0.5s ease 0s, right 0.5s ease 0s, height 0.5s ease 0s;
|
||||
pointer-events: auto;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 0;
|
||||
right: -2000px;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
width: calc(100vw - 40px);
|
||||
max-width: calc(100vw - 19%);
|
||||
display: block;
|
||||
transition: top 0.5s ease 0s, right 0.5s ease 0s, height 0.5s ease 0s;
|
||||
pointer-events: auto;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 0;
|
||||
right: -2000px;
|
||||
max-width: 400px;
|
||||
left: initial;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
width: calc(100vw - 40px);
|
||||
max-width: 400px;
|
||||
left: initial;
|
||||
}
|
||||
@include media-breakpoint-up(md) {
|
||||
max-width: 200px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
max-width: 275px;
|
||||
}
|
||||
|
||||
&--focused {
|
||||
right: 35px;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
max-width: 200px;
|
||||
right: 0;
|
||||
right: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
color: $color-box-text;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
margin-bottom: 0;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&--mode-deleting {
|
||||
color: $color-grey-1;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
border-top: 1px solid $color-comment-separator;
|
||||
}
|
||||
|
||||
&--mode-creating form {
|
||||
border-top: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&--mode-editing form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&--mode-deleting &__text {
|
||||
color: $color-grey-3;
|
||||
}
|
||||
|
||||
&__replies {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
@include button;
|
||||
}
|
||||
|
||||
&__actions,
|
||||
&__reply-actions {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&__actions &__button,
|
||||
&__reply-actions &__button {
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete &__button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete,
|
||||
&__error {
|
||||
color: $color-box-text;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
max-width: 275px;
|
||||
&::after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
color: $color-white;
|
||||
background-color: $color-red-dark;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
height: 26px;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: $color-white;
|
||||
background-color: $color-red-very-dark;
|
||||
border-color: $color-red-very-dark;
|
||||
padding: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 0.65em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&--focused {
|
||||
right: 35px;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
right: 50px;
|
||||
}
|
||||
&::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
color: $color-box-text;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
margin-bottom: 0;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
&__progress {
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&--mode-deleting {
|
||||
color: $color-grey-1;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
border-top: 1px solid $color-comment-separator;
|
||||
}
|
||||
|
||||
&--mode-creating form {
|
||||
border-top: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&--mode-editing form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&--mode-deleting &__text {
|
||||
color: $color-grey-3;
|
||||
}
|
||||
|
||||
&__replies {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__button {
|
||||
@include button;
|
||||
}
|
||||
|
||||
&__actions,
|
||||
&__reply-actions {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&__actions &__button,
|
||||
&__reply-actions &__button {
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete &__button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete,
|
||||
&__error {
|
||||
color: $color-box-text;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
color: $color-white;
|
||||
background-color: $color-red-dark;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
height: 26px;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: $color-white;
|
||||
background-color: $color-red-very-dark;
|
||||
border-color: $color-red-very-dark;
|
||||
padding: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 0.65em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__progress {
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&__reply-input {
|
||||
/* stylelint-disable-next-line declaration-no-important */
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
&__reply-input {
|
||||
/* stylelint-disable-next-line declaration-no-important */
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
import dateFormat from 'dateformat';
|
||||
import React, { FunctionComponent, useState, useEffect, useRef } from 'react';
|
||||
import Icon from '../../../Icon/Icon';
|
||||
|
@ -11,41 +9,39 @@ import { Author } from '../../state/comments';
|
|||
|
||||
// Details/Summary components that just become <details>/<summary> tags
|
||||
// except for IE11 where they become <div> tags to allow us to style them
|
||||
const Details: React.FunctionComponent<React.ComponentPropsWithoutRef<'details'>> = (
|
||||
({ children, open, ...extraProps }) => {
|
||||
if (IS_IE11) {
|
||||
return (
|
||||
<div className={'details-fallback' + (open ? ' details-fallback--open' : '')} {...extraProps}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<details open={open} {...extraProps}>
|
||||
{children}
|
||||
</details>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const Summary: React.FunctionComponent<React.ComponentPropsWithoutRef<'summary'>> = ({ children, ...extraProps }) => {
|
||||
const Details: React.FunctionComponent<
|
||||
React.ComponentPropsWithoutRef<'details'>
|
||||
> = ({ children, open, ...extraProps }) => {
|
||||
if (IS_IE11) {
|
||||
return (
|
||||
<button
|
||||
className="details-fallback__summary"
|
||||
<div
|
||||
className={'details-fallback' + (open ? ' details-fallback--open' : '')}
|
||||
{...extraProps}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<details open={open} {...extraProps}>
|
||||
{children}
|
||||
</details>
|
||||
);
|
||||
};
|
||||
|
||||
const Summary: React.FunctionComponent<
|
||||
React.ComponentPropsWithoutRef<'summary'>
|
||||
> = ({ children, ...extraProps }) => {
|
||||
if (IS_IE11) {
|
||||
return (
|
||||
<button className="details-fallback__summary" {...extraProps}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<summary {...extraProps}>
|
||||
{children}
|
||||
</summary>
|
||||
);
|
||||
return <summary {...extraProps}>{children}</summary>;
|
||||
};
|
||||
|
||||
interface CommentReply {
|
||||
|
@ -65,7 +61,14 @@ interface CommentHeaderProps {
|
|||
}
|
||||
|
||||
export const CommentHeader: FunctionComponent<CommentHeaderProps> = ({
|
||||
commentReply, store, strings, onResolve, onEdit, onDelete, descriptionId, focused
|
||||
commentReply,
|
||||
store,
|
||||
strings,
|
||||
onResolve,
|
||||
onEdit,
|
||||
onDelete,
|
||||
descriptionId,
|
||||
focused,
|
||||
}) => {
|
||||
const { author, date } = commentReply;
|
||||
|
||||
|
@ -115,7 +118,11 @@ export const CommentHeader: FunctionComponent<CommentHeaderProps> = ({
|
|||
}, [menuOpen]);
|
||||
|
||||
const handleClickOutside = (e: MouseEvent) => {
|
||||
if (menuContainerRef.current && e.target instanceof Node && !menuContainerRef.current.contains(e.target)) {
|
||||
if (
|
||||
menuContainerRef.current &&
|
||||
e.target instanceof Node &&
|
||||
!menuContainerRef.current.contains(e.target)
|
||||
) {
|
||||
setMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
@ -130,8 +137,11 @@ export const CommentHeader: FunctionComponent<CommentHeaderProps> = ({
|
|||
return (
|
||||
<div className="comment-header">
|
||||
<div className="comment-header__actions">
|
||||
{(onEdit || onDelete || onResolve) &&
|
||||
<div className="comment-header__action comment-header__action--more" ref={menuContainerRef}>
|
||||
{(onEdit || onDelete || onResolve) && (
|
||||
<div
|
||||
className="comment-header__action comment-header__action--more"
|
||||
ref={menuContainerRef}
|
||||
>
|
||||
<Details open={menuOpen} onClick={toggleMenu}>
|
||||
<Summary
|
||||
aria-label={strings.MORE_ACTIONS}
|
||||
|
@ -143,20 +153,47 @@ export const CommentHeader: FunctionComponent<CommentHeaderProps> = ({
|
|||
<Icon name="ellipsis-v" />
|
||||
</Summary>
|
||||
|
||||
<div className="comment-header__more-actions" role="menu" ref={menuRef}>
|
||||
{onEdit && <button type="button" role="menuitem" onClick={onClickEdit}>{strings.EDIT}</button>}
|
||||
{onDelete && <button type="button" role="menuitem" onClick={onClickDelete}>{strings.DELETE}</button>}
|
||||
{onResolve && <button type="button" role="menuitem" onClick={onClickResolve}>{strings.RESOLVE}</button>}
|
||||
<div
|
||||
className="comment-header__more-actions"
|
||||
role="menu"
|
||||
ref={menuRef}
|
||||
>
|
||||
{onEdit && (
|
||||
<button type="button" role="menuitem" onClick={onClickEdit}>
|
||||
{strings.EDIT}
|
||||
</button>
|
||||
)}
|
||||
{onDelete && (
|
||||
<button type="button" role="menuitem" onClick={onClickDelete}>
|
||||
{strings.DELETE}
|
||||
</button>
|
||||
)}
|
||||
{onResolve && (
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
onClick={onClickResolve}
|
||||
>
|
||||
{strings.RESOLVE}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</Details>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{author && author.avatarUrl &&
|
||||
<img className="comment-header__avatar" src={author.avatarUrl} role="presentation" />}
|
||||
{author && author.avatarUrl && (
|
||||
<img
|
||||
className="comment-header__avatar"
|
||||
src={author.avatarUrl}
|
||||
role="presentation"
|
||||
/>
|
||||
)}
|
||||
<span id={descriptionId}>
|
||||
<p className="comment-header__author">{author ? author.name : ''}</p>
|
||||
<p className="comment-header__date">{dateFormat(date, 'd mmm yyyy HH:MM')}</p>
|
||||
<p className="comment-header__date">
|
||||
{dateFormat(date, 'd mmm yyyy HH:MM')}
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,144 +1,151 @@
|
|||
.comment-header {
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
&__avatar {
|
||||
&__avatar {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
&__author,
|
||||
&__date {
|
||||
max-width: calc(
|
||||
100% - 110px
|
||||
); // Leave room for actions to the right and avatar to the left
|
||||
margin: 0;
|
||||
margin-left: 45px;
|
||||
font-size: 11px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
&__date {
|
||||
color: $color-grey-25;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&__action {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
border-radius: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
|
||||
&:hover {
|
||||
background-color: $color-grey-7;
|
||||
}
|
||||
|
||||
> button,
|
||||
> details > summary,
|
||||
.details-fallback > .details-fallback__summary {
|
||||
// IE11 uses divs instead with these classes
|
||||
// Hides triangle on Firefox
|
||||
list-style-type: none;
|
||||
// Hides triangle on Chrome
|
||||
&::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
background-color: unset;
|
||||
border: unset;
|
||||
-moz-outline-radius: 10px;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
top: 7.5px;
|
||||
left: 7.5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&__author,
|
||||
&__date {
|
||||
max-width: calc(100% - 110px); // Leave room for actions to the right and avatar to the left
|
||||
margin: 0;
|
||||
margin-left: 45px;
|
||||
font-size: 11px;
|
||||
line-height: 15px;
|
||||
}
|
||||
> details,
|
||||
> .details-fallback {
|
||||
// IE11 uses divs instead with these classes
|
||||
position: relative;
|
||||
|
||||
&__date {
|
||||
color: $color-grey-25;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
> div {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
&__action {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
border-radius: 5px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
&--more {
|
||||
> button,
|
||||
> details > summary,
|
||||
> .details-fallback > .details-fallback__summary {
|
||||
// IE11 uses divs instead with these classes
|
||||
color: #767676;
|
||||
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:hover {
|
||||
background-color: $color-grey-7;
|
||||
color: $color-grey-25;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> button,
|
||||
> details > summary,
|
||||
.details-fallback > .details-fallback__summary { // IE11 uses divs instead with these classes
|
||||
// Hides triangle on Firefox
|
||||
list-style-type: none;
|
||||
// Hides triangle on Chrome
|
||||
&::-webkit-details-marker { display: none; }
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: relative;
|
||||
background-color: unset;
|
||||
border: unset;
|
||||
-moz-outline-radius: 10px;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
&__more-actions {
|
||||
background-color: #333;
|
||||
color: $color-grey-5;
|
||||
text-transform: none;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
border-radius: 3px;
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 7.5px;
|
||||
left: 7.5px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> details,
|
||||
> .details-fallback { // IE11 uses divs instead with these classes
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
&--more {
|
||||
> button,
|
||||
> details > summary,
|
||||
> .details-fallback > .details-fallback__summary { // IE11 uses divs instead with these classes
|
||||
color: #767676;
|
||||
|
||||
// stylelint-disable-next-line max-nesting-depth
|
||||
&:hover {
|
||||
color: $color-grey-25;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
border: 6px solid transparent;
|
||||
border-bottom-color: #333;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
right: 9px;
|
||||
}
|
||||
|
||||
&__more-actions {
|
||||
background-color: #333;
|
||||
color: $color-grey-5;
|
||||
text-transform: none;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
border-radius: 3px;
|
||||
button {
|
||||
display: block;
|
||||
background: none;
|
||||
border: 0;
|
||||
color: #fff;
|
||||
padding: 5px 10px;
|
||||
font-size: 13px;
|
||||
width: 100px;
|
||||
text-align: left;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
border: 6px solid transparent;
|
||||
border-bottom-color: #333;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
right: 9px;
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
background: none;
|
||||
border: 0;
|
||||
color: #fff;
|
||||
padding: 5px 10px;
|
||||
font-size: 13px;
|
||||
width: 100px;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.comment--mode-deleting .comment-header,
|
||||
.comment-reply--mode-deleting .comment-header {
|
||||
opacity: 0.5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
// IE11 only uses these classes
|
||||
.details-fallback .comment-header__more-actions {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.details-fallback--open .comment-header__more-actions {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ export function replyFromSomeoneElse() {
|
|||
author: {
|
||||
id: 2,
|
||||
name: 'Someone else',
|
||||
avatarUrl: 'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/31c3d5cc27d1faa321c2413589e8a53f?s=200&d=robohash&r=x',
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { Store } from '../../state';
|
||||
import type { Comment, CommentReply, Author } from '../../state/comments';
|
||||
import { updateReply, deleteReply } from '../../actions/comments';
|
||||
import type { TranslatableStrings } from '../../main';
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
import { CommentHeader } from '../CommentHeader';
|
||||
import TextArea from '../TextArea';
|
||||
import Icon from '../../../Icon/Icon';
|
||||
|
||||
export async function saveCommentReply(
|
||||
comment: Comment,
|
||||
reply: CommentReply,
|
||||
store: Store
|
||||
store: Store,
|
||||
) {
|
||||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'saving',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -27,7 +25,7 @@ export async function saveCommentReply(
|
|||
mode: 'default',
|
||||
text: reply.newText,
|
||||
author: reply.author,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
/* eslint-disable-next-line no-console */
|
||||
|
@ -35,7 +33,7 @@ export async function saveCommentReply(
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'save_error',
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +41,12 @@ export async function saveCommentReply(
|
|||
async function deleteCommentReply(
|
||||
comment: Comment,
|
||||
reply: CommentReply,
|
||||
store: Store
|
||||
store: Store,
|
||||
) {
|
||||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'deleting',
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -57,7 +55,7 @@ async function deleteCommentReply(
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'delete_error',
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +77,7 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
newText: value,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -95,7 +93,7 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'default',
|
||||
newText: reply.text,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -139,7 +137,12 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={reply} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={reply}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
<div className="comment-reply__progress">{strings.SAVING}</div>
|
||||
</>
|
||||
|
@ -157,7 +160,12 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={reply} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={reply}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
<div className="comment-reply__error">
|
||||
{strings.SAVE_ERROR}
|
||||
|
@ -188,13 +196,18 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'default',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={reply} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={reply}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
<div className="comment-reply__confirm-delete">
|
||||
{strings.CONFIRM_DELETE_COMMENT}
|
||||
|
@ -222,7 +235,12 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={reply} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={reply}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
<div className="comment-reply__progress">{strings.DELETING}</div>
|
||||
</>
|
||||
|
@ -244,13 +262,18 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'default',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommentHeader commentReply={reply} store={store} strings={strings} focused={isFocused} />
|
||||
<CommentHeader
|
||||
commentReply={reply}
|
||||
store={store}
|
||||
strings={strings}
|
||||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
<div className="comment-reply__error">
|
||||
{strings.DELETE_ERROR}
|
||||
|
@ -279,13 +302,16 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
// Show edit/delete buttons if this reply was authored by the current user
|
||||
let onEdit;
|
||||
let onDelete;
|
||||
if (reply.author === null || this.props.user && this.props.user.id === reply.author.id) {
|
||||
if (
|
||||
reply.author === null ||
|
||||
(this.props.user && this.props.user.id === reply.author.id)
|
||||
) {
|
||||
onEdit = () => {
|
||||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'editing',
|
||||
newText: reply.text,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -293,7 +319,7 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
store.dispatch(
|
||||
updateReply(comment.localId, reply.localId, {
|
||||
mode: 'delete_confirm',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -315,14 +341,14 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
focused={isFocused}
|
||||
/>
|
||||
<p className="comment-reply__text">{reply.text}</p>
|
||||
{notice &&
|
||||
{notice && (
|
||||
<div className="comment__notice-placeholder">
|
||||
<div className="comment__notice" role="status">
|
||||
<Icon name="info-circle" />
|
||||
{notice}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -331,33 +357,33 @@ export default class CommentReplyComponent extends React.Component<CommentReplyP
|
|||
let inner: React.ReactFragment;
|
||||
|
||||
switch (this.props.reply.mode) {
|
||||
case 'editing':
|
||||
inner = this.renderEditing();
|
||||
break;
|
||||
case 'editing':
|
||||
inner = this.renderEditing();
|
||||
break;
|
||||
|
||||
case 'saving':
|
||||
inner = this.renderSaving();
|
||||
break;
|
||||
case 'saving':
|
||||
inner = this.renderSaving();
|
||||
break;
|
||||
|
||||
case 'save_error':
|
||||
inner = this.renderSaveError();
|
||||
break;
|
||||
case 'save_error':
|
||||
inner = this.renderSaveError();
|
||||
break;
|
||||
|
||||
case 'delete_confirm':
|
||||
inner = this.renderDeleteConfirm();
|
||||
break;
|
||||
case 'delete_confirm':
|
||||
inner = this.renderDeleteConfirm();
|
||||
break;
|
||||
|
||||
case 'deleting':
|
||||
inner = this.renderDeleting();
|
||||
break;
|
||||
case 'deleting':
|
||||
inner = this.renderDeleting();
|
||||
break;
|
||||
|
||||
case 'delete_error':
|
||||
inner = this.renderDeleteError();
|
||||
break;
|
||||
case 'delete_error':
|
||||
inner = this.renderDeleteError();
|
||||
break;
|
||||
|
||||
default:
|
||||
inner = this.renderDefault();
|
||||
break;
|
||||
default:
|
||||
inner = this.renderDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,109 +1,109 @@
|
|||
.comment-reply {
|
||||
padding-top: 20px;
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
border-top: 1px solid $color-comment-separator;
|
||||
padding-top: 20px;
|
||||
pointer-events: auto;
|
||||
position: relative;
|
||||
border-top: 1px solid $color-comment-separator;
|
||||
|
||||
&__text {
|
||||
color: $color-box-text;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
margin-bottom: 0;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
&__text {
|
||||
color: $color-box-text;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
margin-bottom: 0;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&--mode-deleting {
|
||||
color: $color-grey-1;
|
||||
}
|
||||
&--mode-deleting {
|
||||
color: $color-grey-1;
|
||||
}
|
||||
}
|
||||
|
||||
&--mode-deleting &__avatar {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&--mode-deleting &__text {
|
||||
color: $color-grey-3;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
@include button;
|
||||
}
|
||||
|
||||
&__actions,
|
||||
&__confirm-delete,
|
||||
&__progress,
|
||||
&__error {
|
||||
&::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&__actions &__button {
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete &__button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete,
|
||||
&__error {
|
||||
color: $color-box-text;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&--mode-deleting &__avatar {
|
||||
opacity: 0.5;
|
||||
&::after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&--mode-deleting &__text {
|
||||
color: $color-grey-3;
|
||||
&__error {
|
||||
color: $color-white;
|
||||
background-color: $color-red-dark;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
height: 26px;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: $color-white;
|
||||
background-color: $color-red-very-dark;
|
||||
border-color: $color-red-very-dark;
|
||||
padding: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 0.65em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__button {
|
||||
@include button;
|
||||
}
|
||||
|
||||
&__actions,
|
||||
&__confirm-delete,
|
||||
&__progress,
|
||||
&__error {
|
||||
&::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
&__actions &__button {
|
||||
margin-right: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete &__button {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&__confirm-delete,
|
||||
&__error {
|
||||
color: $color-box-text;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
margin-top: 10px;
|
||||
|
||||
button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
color: $color-white;
|
||||
background-color: $color-red-dark;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
height: 26px;
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
color: $color-white;
|
||||
background-color: $color-red-very-dark;
|
||||
border-color: $color-red-very-dark;
|
||||
padding: 2px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
font-size: 0.65em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
&__progress {
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
&__progress {
|
||||
margin-top: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,56 +7,64 @@ export interface TextAreaProps {
|
|||
onChange?(newValue: string): void;
|
||||
focusOnMount?: boolean;
|
||||
focusTarget?: boolean;
|
||||
additionalAttributes?: React.ComponentPropsWithoutRef<'textarea'>
|
||||
additionalAttributes?: React.ComponentPropsWithoutRef<'textarea'>;
|
||||
}
|
||||
|
||||
const TextArea = React.forwardRef<HTMLTextAreaElement | null, TextAreaProps>(({
|
||||
value,
|
||||
className,
|
||||
placeholder,
|
||||
onChange,
|
||||
focusOnMount,
|
||||
focusTarget = false,
|
||||
additionalAttributes = {}
|
||||
}, ref) => {
|
||||
const onChangeValue = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
if (onChange) {
|
||||
onChange(e.target.value);
|
||||
}
|
||||
};
|
||||
const TextArea = React.forwardRef<HTMLTextAreaElement | null, TextAreaProps>(
|
||||
(
|
||||
{
|
||||
value,
|
||||
className,
|
||||
placeholder,
|
||||
onChange,
|
||||
focusOnMount,
|
||||
focusTarget = false,
|
||||
additionalAttributes = {},
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const onChangeValue = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
if (onChange) {
|
||||
onChange(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
// Resize the textarea whenever the value is changed
|
||||
const textAreaElement = React.useRef<HTMLTextAreaElement>(null);
|
||||
React.useImperativeHandle<HTMLTextAreaElement | null, HTMLTextAreaElement | null>(ref, () => textAreaElement.current);
|
||||
// Resize the textarea whenever the value is changed
|
||||
const textAreaElement = React.useRef<HTMLTextAreaElement>(null);
|
||||
React.useImperativeHandle<
|
||||
HTMLTextAreaElement | null,
|
||||
HTMLTextAreaElement | null
|
||||
>(ref, () => textAreaElement.current);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (textAreaElement.current) {
|
||||
textAreaElement.current.style.height = '';
|
||||
textAreaElement.current.style.height =
|
||||
textAreaElement.current.scrollHeight + 'px';
|
||||
}
|
||||
}, [value, textAreaElement]);
|
||||
React.useEffect(() => {
|
||||
if (textAreaElement.current) {
|
||||
textAreaElement.current.style.height = '';
|
||||
textAreaElement.current.style.height =
|
||||
textAreaElement.current.scrollHeight + 'px';
|
||||
}
|
||||
}, [value, textAreaElement]);
|
||||
|
||||
// Focus the textarea when it is mounted
|
||||
React.useEffect(() => {
|
||||
if (focusOnMount && textAreaElement.current) {
|
||||
textAreaElement.current.focus();
|
||||
}
|
||||
}, [textAreaElement]);
|
||||
// Focus the textarea when it is mounted
|
||||
React.useEffect(() => {
|
||||
if (focusOnMount && textAreaElement.current) {
|
||||
textAreaElement.current.focus();
|
||||
}
|
||||
}, [textAreaElement]);
|
||||
|
||||
return (
|
||||
<textarea
|
||||
data-focus-target={focusTarget}
|
||||
rows={1}
|
||||
style={{ resize: 'none', overflowY: 'hidden' }}
|
||||
className={className}
|
||||
placeholder={placeholder}
|
||||
ref={textAreaElement}
|
||||
onChange={onChangeValue}
|
||||
value={value}
|
||||
{...additionalAttributes}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<textarea
|
||||
data-focus-target={focusTarget}
|
||||
rows={1}
|
||||
style={{ resize: 'none', overflowY: 'hidden' }}
|
||||
className={className}
|
||||
placeholder={placeholder}
|
||||
ref={textAreaElement}
|
||||
onChange={onChangeValue}
|
||||
value={value}
|
||||
{...additionalAttributes}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default TextArea;
|
||||
|
|
|
@ -23,125 +23,125 @@ $box-border-radius: 5px;
|
|||
$box-padding: 10px;
|
||||
|
||||
@mixin focus-outline {
|
||||
outline: $color-focus-outline solid 3px;
|
||||
outline: $color-focus-outline solid 3px;
|
||||
}
|
||||
|
||||
@mixin box {
|
||||
background-color: $color-box-background;
|
||||
border: 1px solid $color-box-border;
|
||||
padding: $box-padding;
|
||||
font-size: 11px;
|
||||
border-radius: $box-border-radius;
|
||||
background-color: $color-box-background;
|
||||
border: 1px solid $color-box-border;
|
||||
padding: $box-padding;
|
||||
font-size: 11px;
|
||||
border-radius: $box-border-radius;
|
||||
color: $color-box-text;
|
||||
|
||||
&--focused {
|
||||
border-color: #bbb;
|
||||
box-shadow: 3px 2px 3px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: $font-sans;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
background-color: $color-textarea-background;
|
||||
border: 1px solid $color-textarea-border;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
-moz-outline-radius: 8px;
|
||||
color: $color-box-text;
|
||||
|
||||
&--focused {
|
||||
border-color: #bbb;
|
||||
box-shadow: 3px 2px 3px -1px rgba(0, 0, 0, 0.1);
|
||||
&::placeholder {
|
||||
color: $color-textarea-placeholder-text;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: $font-sans;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
background-color: $color-textarea-background;
|
||||
border: 1px solid $color-textarea-border;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
-moz-outline-radius: 8px;
|
||||
color: $color-box-text;
|
||||
|
||||
&::placeholder {
|
||||
color: $color-textarea-placeholder-text;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $color-textarea-background-focused;
|
||||
border-color: $color-textarea-border-focused;
|
||||
outline: unset;
|
||||
}
|
||||
&:focus {
|
||||
background-color: $color-textarea-background-focused;
|
||||
border-color: $color-textarea-border-focused;
|
||||
outline: unset;
|
||||
}
|
||||
}
|
||||
|
||||
*:focus {
|
||||
@include focus-outline;
|
||||
*:focus {
|
||||
@include focus-outline;
|
||||
}
|
||||
|
||||
&__notice-placeholder {
|
||||
position: relative;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
&__notice {
|
||||
background-color: $color-amber-1;
|
||||
position: absolute;
|
||||
left: -$box-padding;
|
||||
bottom: 0;
|
||||
width: calc(100% + #{$box-padding} * 2);
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
svg.icon {
|
||||
color: $color-amber-0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 10px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
&__notice-placeholder {
|
||||
position: relative;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
&__notice {
|
||||
background-color: $color-amber-1;
|
||||
position: absolute;
|
||||
left: -$box-padding;
|
||||
bottom: 0;
|
||||
width: calc(100% + #{$box-padding} * 2);
|
||||
padding: 5px 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
svg.icon {
|
||||
color: $color-amber-0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 10px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
> :last-child &__notice {
|
||||
bottom: -$box-padding;
|
||||
border-bottom-left-radius: $box-border-radius;
|
||||
border-bottom-right-radius: $box-border-radius;
|
||||
}
|
||||
> :last-child &__notice {
|
||||
bottom: -$box-padding;
|
||||
border-bottom-left-radius: $box-border-radius;
|
||||
border-bottom-right-radius: $box-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin button {
|
||||
background-color: inherit;
|
||||
border: 1px solid $color-grey-3;
|
||||
border-radius: 3px;
|
||||
-moz-outline-radius: 6px;
|
||||
color: $color-teal;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
height: 25px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
background-color: inherit;
|
||||
border: 1px solid $color-grey-3;
|
||||
border-radius: 3px;
|
||||
-moz-outline-radius: 6px;
|
||||
color: $color-teal;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
height: 25px;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
|
||||
&--primary {
|
||||
color: $color-white;
|
||||
border: 1px solid $color-teal;
|
||||
background-color: $color-teal;
|
||||
}
|
||||
&--primary {
|
||||
color: $color-white;
|
||||
border: 1px solid $color-teal;
|
||||
background-color: $color-teal;
|
||||
}
|
||||
|
||||
&--red {
|
||||
color: $color-white;
|
||||
border: 1px solid $color-red-very-dark;
|
||||
background-color: $color-red-very-dark;
|
||||
}
|
||||
&--red {
|
||||
color: $color-white;
|
||||
border: 1px solid $color-red-very-dark;
|
||||
background-color: $color-red-very-dark;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
&:disabled {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
// Disable Firefox's focus styling because we add our own.
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
// Disable Firefox's focus styling because we add our own.
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.comments-list {
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
z-index: 50;
|
||||
font-family: $font-sans;
|
||||
pointer-events: none;
|
||||
width: 400px;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
z-index: 50;
|
||||
font-family: $font-sans;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// stylelint-disable no-invalid-position-at-import-rule
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
setFocusedComment,
|
||||
updateComment,
|
||||
commentActionFunctions,
|
||||
invalidateContentPath
|
||||
invalidateContentPath,
|
||||
} from './actions/comments';
|
||||
import { updateGlobalSettings } from './actions/settings';
|
||||
import {
|
||||
|
@ -24,7 +24,7 @@ import {
|
|||
selectEnabled,
|
||||
selectFocused,
|
||||
selectIsDirty,
|
||||
selectCommentCount
|
||||
selectCommentCount,
|
||||
} from './selectors';
|
||||
import CommentComponent from './components/Comment';
|
||||
import { CommentFormSetComponent } from './components/Form';
|
||||
|
@ -72,7 +72,6 @@ export const defaultStrings = {
|
|||
SAVE_PAGE_TO_SAVE_REPLY: 'Save the page to save this reply',
|
||||
};
|
||||
|
||||
|
||||
// This is done as this is serialized pretty directly from the Django model
|
||||
export interface InitialCommentReply {
|
||||
pk: number;
|
||||
|
@ -97,9 +96,14 @@ export interface InitialComment {
|
|||
}
|
||||
/* eslint-enable */
|
||||
|
||||
|
||||
const getAuthor = (authors: Map<string, {name: string, avatar_url: string}>, id: any): Author => {
|
||||
const authorData = getOrDefault(authors, String(id), { name: '', avatar_url: '' });
|
||||
const getAuthor = (
|
||||
authors: Map<string, { name: string; avatar_url: string }>,
|
||||
id: any,
|
||||
): Author => {
|
||||
const authorData = getOrDefault(authors, String(id), {
|
||||
name: '',
|
||||
avatar_url: '',
|
||||
});
|
||||
|
||||
return {
|
||||
id,
|
||||
|
@ -112,7 +116,7 @@ function renderCommentsUi(
|
|||
store: Store,
|
||||
layout: LayoutController,
|
||||
comments: Comment[],
|
||||
strings: TranslatableStrings
|
||||
strings: TranslatableStrings,
|
||||
): React.ReactElement {
|
||||
const state = store.getState();
|
||||
const { commentsEnabled, user, currentTab } = state.settings;
|
||||
|
@ -123,7 +127,9 @@ function renderCommentsUi(
|
|||
commentsToRender = [];
|
||||
}
|
||||
// Hide all resolved/deleted comments
|
||||
commentsToRender = commentsToRender.filter(({ deleted, resolved }) => !(deleted || resolved));
|
||||
commentsToRender = commentsToRender.filter(
|
||||
({ deleted, resolved }) => !(deleted || resolved),
|
||||
);
|
||||
const commentsRendered = commentsToRender.map((comment) => (
|
||||
<CommentComponent
|
||||
key={comment.localId}
|
||||
|
@ -137,9 +143,7 @@ function renderCommentsUi(
|
|||
strings={strings}
|
||||
/>
|
||||
));
|
||||
return (
|
||||
<ol className="comments-list">{commentsRendered}</ol>
|
||||
);
|
||||
return <ol className="comments-list">{commentsRendered}</ol>;
|
||||
/* eslint-enable react/no-danger */
|
||||
}
|
||||
|
||||
|
@ -148,47 +152,39 @@ export class CommentApp {
|
|||
layout: LayoutController;
|
||||
utils = {
|
||||
selectCommentsForContentPathFactory,
|
||||
selectCommentFactory
|
||||
}
|
||||
selectCommentFactory,
|
||||
};
|
||||
selectors = {
|
||||
selectComments,
|
||||
selectEnabled,
|
||||
selectFocused,
|
||||
selectIsDirty,
|
||||
selectCommentCount
|
||||
}
|
||||
selectCommentCount,
|
||||
};
|
||||
actions = commentActionFunctions;
|
||||
|
||||
constructor() {
|
||||
this.store = createStore(reducer, {
|
||||
settings: INITIAL_SETTINGS_STATE
|
||||
settings: INITIAL_SETTINGS_STATE,
|
||||
});
|
||||
this.layout = new LayoutController();
|
||||
}
|
||||
|
||||
setUser(userId: any, authors: Map<string, {name: string, avatar_url: string}>) {
|
||||
|
||||
setUser(
|
||||
userId: any,
|
||||
authors: Map<string, { name: string; avatar_url: string }>,
|
||||
) {
|
||||
this.store.dispatch(
|
||||
updateGlobalSettings({
|
||||
user: getAuthor(authors, userId)
|
||||
})
|
||||
user: getAuthor(authors, userId),
|
||||
}),
|
||||
);
|
||||
}
|
||||
updateAnnotation(
|
||||
annotation: Annotation,
|
||||
commentId: number
|
||||
) {
|
||||
updateAnnotation(annotation: Annotation, commentId: number) {
|
||||
this.attachAnnotationLayout(annotation, commentId);
|
||||
this.store.dispatch(
|
||||
updateComment(
|
||||
commentId,
|
||||
{ annotation: annotation }
|
||||
)
|
||||
);
|
||||
this.store.dispatch(updateComment(commentId, { annotation: annotation }));
|
||||
}
|
||||
attachAnnotationLayout(
|
||||
annotation: Annotation,
|
||||
commentId: number
|
||||
) {
|
||||
attachAnnotationLayout(annotation: Annotation, commentId: number) {
|
||||
// Attach an annotation to an existing comment in the layout
|
||||
|
||||
// const layout engine know the annotation so it would position the comment correctly
|
||||
|
@ -214,19 +210,26 @@ export class CommentApp {
|
|||
Date.now(),
|
||||
{
|
||||
mode: 'creating',
|
||||
}
|
||||
)
|
||||
)
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Focus and pin the comment
|
||||
this.store.dispatch(setFocusedComment(commentId, { updatePinnedComment: true, forceFocus: true }));
|
||||
this.store.dispatch(
|
||||
setFocusedComment(commentId, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
}),
|
||||
);
|
||||
return commentId;
|
||||
}
|
||||
setVisible(visible: boolean) {
|
||||
this.store.dispatch(updateGlobalSettings({
|
||||
commentsEnabled: visible,
|
||||
}));
|
||||
this.store.dispatch(
|
||||
updateGlobalSettings({
|
||||
commentsEnabled: visible,
|
||||
}),
|
||||
);
|
||||
}
|
||||
invalidateContentPath(contentPath: string) {
|
||||
// Called when a given content path on the form is no longer valid (eg, a block has been deleted)
|
||||
|
@ -237,9 +240,9 @@ export class CommentApp {
|
|||
outputElement: HTMLElement,
|
||||
userId: any,
|
||||
initialComments: InitialComment[],
|
||||
|
||||
authors: Map<string, {name: string, avatar_url: string}>,
|
||||
translationStrings: TranslatableStrings | null
|
||||
|
||||
authors: Map<string, { name: string; avatar_url: string }>,
|
||||
translationStrings: TranslatableStrings | null,
|
||||
) {
|
||||
let pinnedComment: number | null = null;
|
||||
this.setUser(userId, authors);
|
||||
|
@ -258,14 +261,18 @@ export class CommentApp {
|
|||
|
||||
const render = () => {
|
||||
const state = this.store.getState();
|
||||
const commentList: Comment[] = Array.from(state.comments.comments.values());
|
||||
const commentList: Comment[] = Array.from(
|
||||
state.comments.comments.values(),
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<CommentFormSetComponent
|
||||
comments={commentList.filter(comment => comment.mode !== 'creating')}
|
||||
comments={commentList.filter(
|
||||
(comment) => comment.mode !== 'creating',
|
||||
)}
|
||||
remoteCommentCount={state.comments.remoteCommentCount}
|
||||
/>,
|
||||
outputElement
|
||||
outputElement,
|
||||
);
|
||||
|
||||
// Check if the pinned comment has changed
|
||||
|
@ -287,10 +294,10 @@ export class CommentApp {
|
|||
if (this.layout.refreshLayout()) {
|
||||
ReactDOM.render(
|
||||
renderCommentsUi(this.store, this.layout, commentList, strings),
|
||||
element
|
||||
element,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -312,10 +319,10 @@ export class CommentApp {
|
|||
remoteId: comment.pk,
|
||||
text: comment.text,
|
||||
deleted: comment.deleted,
|
||||
resolved: comment.resolved
|
||||
}
|
||||
)
|
||||
)
|
||||
resolved: comment.resolved,
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Create replies
|
||||
|
@ -330,18 +337,23 @@ export class CommentApp {
|
|||
{
|
||||
remoteId: reply.pk,
|
||||
text: reply.text,
|
||||
deleted: reply.deleted
|
||||
}
|
||||
)
|
||||
)
|
||||
deleted: reply.deleted,
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// If this is the initial focused comment. Focus and pin it
|
||||
|
||||
|
||||
// TODO: Scroll to this comment
|
||||
if (initialFocusedCommentId && comment.pk === initialFocusedCommentId) {
|
||||
this.store.dispatch(setFocusedComment(commentId, { updatePinnedComment: true, forceFocus: true }));
|
||||
this.store.dispatch(
|
||||
setFocusedComment(commentId, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,10 +365,17 @@ export class CommentApp {
|
|||
document.body.addEventListener('mousedown', (e) => {
|
||||
if (e.target instanceof HTMLElement) {
|
||||
// ignore if click target is a comment or an annotation
|
||||
if (!e.target.closest('#comments, [data-annotation], [data-comment-add]')) {
|
||||
if (
|
||||
!e.target.closest('#comments, [data-annotation], [data-comment-add]')
|
||||
) {
|
||||
// Running store.dispatch directly here seems to prevent the event from being handled anywhere else
|
||||
setTimeout(() => {
|
||||
this.store.dispatch(setFocusedComment(null, { updatePinnedComment: true, forceFocus: false }));
|
||||
this.store.dispatch(
|
||||
setFocusedComment(null, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: false,
|
||||
}),
|
||||
);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +384,9 @@ export class CommentApp {
|
|||
document.body.addEventListener('commentAnchorVisibilityChange', () => {
|
||||
// If any streamfield blocks or panels have collapsed or expanded
|
||||
// check if we need to rerender
|
||||
this.layout.refreshDesiredPositions(this.store.getState().settings.currentTab);
|
||||
this.layout.refreshDesiredPositions(
|
||||
this.store.getState().settings.currentTab,
|
||||
);
|
||||
if (this.layout.refreshLayout()) {
|
||||
render();
|
||||
}
|
||||
|
|
|
@ -4,14 +4,16 @@ import type { State } from '../state';
|
|||
|
||||
export const selectComments = (state: State) => state.comments.comments;
|
||||
export const selectFocused = (state: State) => state.comments.focusedComment;
|
||||
export const selectRemoteCommentCount = (state: State) => state.comments.remoteCommentCount;
|
||||
export const selectRemoteCommentCount = (state: State) =>
|
||||
state.comments.remoteCommentCount;
|
||||
|
||||
export function selectCommentsForContentPathFactory(contentpath: string) {
|
||||
return createSelector(selectComments, (comments) =>
|
||||
[...comments.values()].filter(
|
||||
(comment: Comment) =>
|
||||
comment.contentpath === contentpath && !(comment.deleted || comment.resolved)
|
||||
)
|
||||
comment.contentpath === contentpath &&
|
||||
!(comment.deleted || comment.resolved),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -22,9 +24,7 @@ export function selectCommentFactory(localId: number) {
|
|||
return undefined;
|
||||
}
|
||||
return comment;
|
||||
}
|
||||
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export const selectEnabled = (state: State) => state.settings.commentsEnabled;
|
||||
|
@ -36,20 +36,23 @@ export const selectIsDirty = createSelector(
|
|||
if (remoteCommentCount !== comments.size) {
|
||||
return true;
|
||||
}
|
||||
return Array.from(comments.values()).some(comment => {
|
||||
if (comment.deleted ||
|
||||
return Array.from(comments.values()).some((comment) => {
|
||||
if (
|
||||
comment.deleted ||
|
||||
comment.resolved ||
|
||||
comment.replies.size !== comment.remoteReplyCount ||
|
||||
comment.originalText !== comment.text
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return Array.from(comment.replies.values()).some(reply => reply.deleted || reply.originalText !== reply.text);
|
||||
return Array.from(comment.replies.values()).some(
|
||||
(reply) => reply.deleted || reply.originalText !== reply.text,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
export const selectCommentCount = (state: State) => (
|
||||
[...state.comments.comments.values()].filter(
|
||||
(comment: Comment) => !comment.deleted && !comment.resolved
|
||||
).length
|
||||
},
|
||||
);
|
||||
|
||||
export const selectCommentCount = (state: State) =>
|
||||
[...state.comments.comments.values()].filter(
|
||||
(comment: Comment) => !comment.deleted && !comment.resolved,
|
||||
).length;
|
||||
|
|
|
@ -18,12 +18,10 @@ test('Select comments for contentpath', () => {
|
|||
comments: basicCommentsState,
|
||||
settings: INITIAL_SETTINGS_STATE,
|
||||
};
|
||||
const testContentPathSelector = selectCommentsForContentPathFactory(
|
||||
'test_contentpath'
|
||||
);
|
||||
const testContentPathSelector2 = selectCommentsForContentPathFactory(
|
||||
'test_contentpath_2'
|
||||
);
|
||||
const testContentPathSelector =
|
||||
selectCommentsForContentPathFactory('test_contentpath');
|
||||
const testContentPathSelector2 =
|
||||
selectCommentsForContentPathFactory('test_contentpath_2');
|
||||
const selectedComments = testContentPathSelector(state);
|
||||
expect(selectedComments.length).toBe(1);
|
||||
expect(selectedComments[0].contentpath).toBe('test_contentpath');
|
||||
|
@ -35,79 +33,90 @@ test('Select comments for contentpath', () => {
|
|||
test('Select is dirty', () => {
|
||||
const state = {
|
||||
comments: INITIAL_COMMENTS_STATE,
|
||||
settings: INITIAL_SETTINGS_STATE
|
||||
settings: INITIAL_SETTINGS_STATE,
|
||||
};
|
||||
const stateWithUnsavedComment = reducer(state, actions.addComment(newComment(
|
||||
'test_contentpath',
|
||||
'test_position',
|
||||
1,
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
{
|
||||
remoteId: null,
|
||||
text: 'my new comment'
|
||||
}
|
||||
)));
|
||||
const stateWithUnsavedComment = reducer(
|
||||
state,
|
||||
actions.addComment(
|
||||
newComment('test_contentpath', 'test_position', 1, null, null, 0, {
|
||||
remoteId: null,
|
||||
text: 'my new comment',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithUnsavedComment)).toBe(true);
|
||||
|
||||
const stateWithSavedComment = reducer(state, actions.addComment(newComment(
|
||||
'test_contentpath',
|
||||
'test_position',
|
||||
1,
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
{
|
||||
remoteId: 1,
|
||||
text: 'my saved comment'
|
||||
}
|
||||
)));
|
||||
const stateWithSavedComment = reducer(
|
||||
state,
|
||||
actions.addComment(
|
||||
newComment('test_contentpath', 'test_position', 1, null, null, 0, {
|
||||
remoteId: 1,
|
||||
text: 'my saved comment',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithSavedComment)).toBe(false);
|
||||
|
||||
const stateWithDeletedComment = reducer(stateWithSavedComment, actions.deleteComment(1));
|
||||
const stateWithDeletedComment = reducer(
|
||||
stateWithSavedComment,
|
||||
actions.deleteComment(1),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithDeletedComment)).toBe(true);
|
||||
|
||||
const stateWithResolvedComment = reducer(stateWithSavedComment, actions.updateComment(1, { resolved: true }));
|
||||
const stateWithResolvedComment = reducer(
|
||||
stateWithSavedComment,
|
||||
actions.updateComment(1, { resolved: true }),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithResolvedComment)).toBe(true);
|
||||
|
||||
const stateWithEditedComment = reducer(stateWithSavedComment, actions.updateComment(1, { text: 'edited_text' }));
|
||||
const stateWithEditedComment = reducer(
|
||||
stateWithSavedComment,
|
||||
actions.updateComment(1, { text: 'edited_text' }),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithEditedComment)).toBe(true);
|
||||
|
||||
const stateWithUnsavedReply = reducer(stateWithSavedComment, actions.addReply(1, newCommentReply(
|
||||
2,
|
||||
null,
|
||||
0,
|
||||
{
|
||||
remoteId: null,
|
||||
text: 'new reply'
|
||||
}
|
||||
)));
|
||||
const stateWithUnsavedReply = reducer(
|
||||
stateWithSavedComment,
|
||||
actions.addReply(
|
||||
1,
|
||||
newCommentReply(2, null, 0, {
|
||||
remoteId: null,
|
||||
text: 'new reply',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithUnsavedReply)).toBe(true);
|
||||
|
||||
const stateWithSavedReply = reducer(stateWithSavedComment, actions.addReply(1, newCommentReply(
|
||||
2,
|
||||
null,
|
||||
0,
|
||||
{
|
||||
remoteId: 2,
|
||||
text: 'new saved reply'
|
||||
}
|
||||
)));
|
||||
const stateWithSavedReply = reducer(
|
||||
stateWithSavedComment,
|
||||
actions.addReply(
|
||||
1,
|
||||
newCommentReply(2, null, 0, {
|
||||
remoteId: 2,
|
||||
text: 'new saved reply',
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithSavedReply)).toBe(false);
|
||||
|
||||
const stateWithDeletedReply = reducer(stateWithSavedReply, actions.deleteReply(1, 2));
|
||||
const stateWithDeletedReply = reducer(
|
||||
stateWithSavedReply,
|
||||
actions.deleteReply(1, 2),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithDeletedReply)).toBe(true);
|
||||
|
||||
const stateWithEditedReply = reducer(stateWithSavedReply, actions.updateReply(1, 2, { text: 'edited_text' }));
|
||||
const stateWithEditedReply = reducer(
|
||||
stateWithSavedReply,
|
||||
actions.updateReply(1, 2, { text: 'edited_text' }),
|
||||
);
|
||||
|
||||
expect(selectIsDirty(stateWithEditedReply)).toBe(true);
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ test('New comment added to state', () => {
|
|||
const newState = reducer(basicCommentsState, commentAction);
|
||||
expect(newState.comments.get(newComment.localId)).toBe(newComment);
|
||||
expect(newState.remoteCommentCount).toBe(
|
||||
basicCommentsState.remoteCommentCount
|
||||
basicCommentsState.remoteCommentCount,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -68,13 +68,13 @@ test('Remote comment added to state', () => {
|
|||
const newState = reducer(basicCommentsState, commentAction);
|
||||
expect(newState.comments.get(newComment.localId)).toBe(newComment);
|
||||
expect(newState.remoteCommentCount).toBe(
|
||||
basicCommentsState.remoteCommentCount + 1
|
||||
basicCommentsState.remoteCommentCount + 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('Existing comment updated', () => {
|
||||
const commentUpdate: CommentUpdate = {
|
||||
mode: 'editing'
|
||||
mode: 'editing',
|
||||
};
|
||||
const updateAction = actions.updateComment(1, commentUpdate);
|
||||
const newState = reducer(basicCommentsState, updateAction);
|
||||
|
@ -111,7 +111,7 @@ test('Remote comment deleted', () => {
|
|||
expect(newState.focusedComment).toBe(null);
|
||||
expect(newState.pinnedComment).toBe(null);
|
||||
expect(newState.remoteCommentCount).toBe(
|
||||
basicCommentsState.remoteCommentCount
|
||||
basicCommentsState.remoteCommentCount,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -127,12 +127,15 @@ test('Remote comment resolved', () => {
|
|||
expect(newState.focusedComment).toBe(null);
|
||||
expect(newState.pinnedComment).toBe(null);
|
||||
expect(newState.remoteCommentCount).toBe(
|
||||
basicCommentsState.remoteCommentCount
|
||||
basicCommentsState.remoteCommentCount,
|
||||
);
|
||||
});
|
||||
|
||||
test('Comment focused', () => {
|
||||
const focusAction = actions.setFocusedComment(4, { updatePinnedComment: true, forceFocus: true });
|
||||
const focusAction = actions.setFocusedComment(4, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
});
|
||||
const newState = reducer(basicCommentsState, focusAction);
|
||||
expect(newState.focusedComment).toBe(4);
|
||||
expect(newState.pinnedComment).toBe(4);
|
||||
|
@ -140,7 +143,10 @@ test('Comment focused', () => {
|
|||
});
|
||||
|
||||
test('Invalid comment not focused', () => {
|
||||
const focusAction = actions.setFocusedComment(9000, { updatePinnedComment: true, forceFocus: true });
|
||||
const focusAction = actions.setFocusedComment(9000, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
});
|
||||
const newState = reducer(basicCommentsState, focusAction);
|
||||
expect(newState.focusedComment).toBe(basicCommentsState.focusedComment);
|
||||
expect(newState.pinnedComment).toBe(basicCommentsState.pinnedComment);
|
||||
|
@ -194,7 +200,9 @@ test('Remote reply added', () => {
|
|||
expect(stateReply).toBeDefined();
|
||||
expect(stateReply).toBe(reply);
|
||||
if (originalComment) {
|
||||
expect(comment.remoteReplyCount).toBe(originalComment.remoteReplyCount + 1);
|
||||
expect(comment.remoteReplyCount).toBe(
|
||||
originalComment.remoteReplyCount + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -54,8 +54,8 @@ export function newCommentReply(
|
|||
remoteId = null,
|
||||
mode = 'default',
|
||||
text = '',
|
||||
deleted = false
|
||||
}: NewReplyOptions
|
||||
deleted = false,
|
||||
}: NewReplyOptions,
|
||||
): CommentReply {
|
||||
return {
|
||||
localId,
|
||||
|
@ -130,7 +130,7 @@ export function newComment(
|
|||
resolved = false,
|
||||
deleted = false,
|
||||
replies = new Map(),
|
||||
}: NewCommentOptions
|
||||
}: NewCommentOptions,
|
||||
): Comment {
|
||||
return {
|
||||
contentpath,
|
||||
|
@ -150,7 +150,7 @@ export function newComment(
|
|||
resolved,
|
||||
remoteReplyCount: Array.from(replies.values()).reduce(
|
||||
(n, reply) => (reply.remoteId !== null ? n + 1 : n),
|
||||
0
|
||||
0,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
@ -174,142 +174,145 @@ export const INITIAL_STATE: CommentsState = {
|
|||
remoteCommentCount: 0,
|
||||
};
|
||||
|
||||
export const reducer = produce((draft: CommentsState, action: actions.Action) => {
|
||||
/* eslint-disable no-param-reassign */
|
||||
const deleteComment = (comment: Comment) => {
|
||||
if (!comment.remoteId) {
|
||||
// If the comment doesn't exist in the database, there's no need to keep it around locally
|
||||
draft.comments.delete(comment.localId);
|
||||
} else {
|
||||
comment.deleted = true;
|
||||
}
|
||||
export const reducer = produce(
|
||||
(draft: CommentsState, action: actions.Action) => {
|
||||
/* eslint-disable no-param-reassign */
|
||||
const deleteComment = (comment: Comment) => {
|
||||
if (!comment.remoteId) {
|
||||
// If the comment doesn't exist in the database, there's no need to keep it around locally
|
||||
draft.comments.delete(comment.localId);
|
||||
} else {
|
||||
comment.deleted = true;
|
||||
}
|
||||
|
||||
// Unset focusedComment if the focused comment is the one being deleted
|
||||
if (draft.focusedComment === comment.localId) {
|
||||
draft.focusedComment = null;
|
||||
draft.forceFocus = false;
|
||||
}
|
||||
if (draft.pinnedComment === comment.localId) {
|
||||
draft.pinnedComment = null;
|
||||
}
|
||||
};
|
||||
// Unset focusedComment if the focused comment is the one being deleted
|
||||
if (draft.focusedComment === comment.localId) {
|
||||
draft.focusedComment = null;
|
||||
draft.forceFocus = false;
|
||||
}
|
||||
if (draft.pinnedComment === comment.localId) {
|
||||
draft.pinnedComment = null;
|
||||
}
|
||||
};
|
||||
|
||||
const resolveComment = (comment: Comment) => {
|
||||
if (!comment.remoteId) {
|
||||
// If the comment doesn't exist in the database, there's no need to keep it around locally
|
||||
draft.comments.delete(comment.localId);
|
||||
} else {
|
||||
comment.resolved = true;
|
||||
}
|
||||
// Unset focusedComment if the focused comment is the one being resolved
|
||||
if (draft.focusedComment === comment.localId) {
|
||||
draft.focusedComment = null;
|
||||
}
|
||||
if (draft.pinnedComment === comment.localId) {
|
||||
draft.pinnedComment = null;
|
||||
}
|
||||
};
|
||||
const resolveComment = (comment: Comment) => {
|
||||
if (!comment.remoteId) {
|
||||
// If the comment doesn't exist in the database, there's no need to keep it around locally
|
||||
draft.comments.delete(comment.localId);
|
||||
} else {
|
||||
comment.resolved = true;
|
||||
}
|
||||
// Unset focusedComment if the focused comment is the one being resolved
|
||||
if (draft.focusedComment === comment.localId) {
|
||||
draft.focusedComment = null;
|
||||
}
|
||||
if (draft.pinnedComment === comment.localId) {
|
||||
draft.pinnedComment = null;
|
||||
}
|
||||
};
|
||||
|
||||
switch (action.type) {
|
||||
case actions.ADD_COMMENT: {
|
||||
draft.comments.set(action.comment.localId, action.comment);
|
||||
if (action.comment.remoteId) {
|
||||
draft.remoteCommentCount += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.UPDATE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (comment) {
|
||||
if (action.update.newText && action.update.newText.length === 0) {
|
||||
switch (action.type) {
|
||||
case actions.ADD_COMMENT: {
|
||||
draft.comments.set(action.comment.localId, action.comment);
|
||||
if (action.comment.remoteId) {
|
||||
draft.remoteCommentCount += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
update(comment, action.update);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.DELETE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
|
||||
deleteComment(comment);
|
||||
break;
|
||||
}
|
||||
case actions.RESOLVE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
|
||||
resolveComment(comment);
|
||||
break;
|
||||
}
|
||||
case actions.SET_FOCUSED_COMMENT: {
|
||||
if ((action.commentId === null) || (draft.comments.has(action.commentId))) {
|
||||
draft.focusedComment = action.commentId;
|
||||
if (action.updatePinnedComment) {
|
||||
draft.pinnedComment = action.commentId;
|
||||
case actions.UPDATE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (comment) {
|
||||
if (action.update.newText && action.update.newText.length === 0) {
|
||||
break;
|
||||
}
|
||||
update(comment, action.update);
|
||||
}
|
||||
break;
|
||||
}
|
||||
draft.forceFocus = action.forceFocus;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.ADD_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if ((!comment) || action.reply.text.length === 0) {
|
||||
break;
|
||||
}
|
||||
if (action.reply.remoteId) {
|
||||
comment.remoteReplyCount += 1;
|
||||
}
|
||||
comment.replies.set(action.reply.localId, action.reply);
|
||||
break;
|
||||
}
|
||||
case actions.UPDATE_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
const reply = comment.replies.get(action.replyId);
|
||||
if (!reply) {
|
||||
break;
|
||||
}
|
||||
if (action.update.newText && action.update.newText.length === 0) {
|
||||
break;
|
||||
}
|
||||
update(reply, action.update);
|
||||
break;
|
||||
}
|
||||
case actions.DELETE_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
const reply = comment.replies.get(action.replyId);
|
||||
if (!reply) {
|
||||
break;
|
||||
}
|
||||
if (!reply.remoteId) {
|
||||
// The reply doesn't exist in the database, so we don't need to store it locally
|
||||
comment.replies.delete(reply.localId);
|
||||
} else {
|
||||
reply.deleted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.INVALIDATE_CONTENT_PATH: {
|
||||
// Delete any comments that exist in the contentpath
|
||||
const comments = draft.comments;
|
||||
for (const comment of comments.values()) {
|
||||
if (comment.contentpath.startsWith(action.contentPath)) {
|
||||
case actions.DELETE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
|
||||
deleteComment(comment);
|
||||
break;
|
||||
}
|
||||
case actions.RESOLVE_COMMENT: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
|
||||
resolveComment(comment);
|
||||
break;
|
||||
}
|
||||
case actions.SET_FOCUSED_COMMENT: {
|
||||
if (action.commentId === null || draft.comments.has(action.commentId)) {
|
||||
draft.focusedComment = action.commentId;
|
||||
if (action.updatePinnedComment) {
|
||||
draft.pinnedComment = action.commentId;
|
||||
}
|
||||
draft.forceFocus = action.forceFocus;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.ADD_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment || action.reply.text.length === 0) {
|
||||
break;
|
||||
}
|
||||
if (action.reply.remoteId) {
|
||||
comment.remoteReplyCount += 1;
|
||||
}
|
||||
comment.replies.set(action.reply.localId, action.reply);
|
||||
break;
|
||||
}
|
||||
case actions.UPDATE_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
const reply = comment.replies.get(action.replyId);
|
||||
if (!reply) {
|
||||
break;
|
||||
}
|
||||
if (action.update.newText && action.update.newText.length === 0) {
|
||||
break;
|
||||
}
|
||||
update(reply, action.update);
|
||||
break;
|
||||
}
|
||||
case actions.DELETE_REPLY: {
|
||||
const comment = draft.comments.get(action.commentId);
|
||||
if (!comment) {
|
||||
break;
|
||||
}
|
||||
const reply = comment.replies.get(action.replyId);
|
||||
if (!reply) {
|
||||
break;
|
||||
}
|
||||
if (!reply.remoteId) {
|
||||
// The reply doesn't exist in the database, so we don't need to store it locally
|
||||
comment.replies.delete(reply.localId);
|
||||
} else {
|
||||
reply.deleted = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case actions.INVALIDATE_CONTENT_PATH: {
|
||||
// Delete any comments that exist in the contentpath
|
||||
const comments = draft.comments;
|
||||
for (const comment of comments.values()) {
|
||||
if (comment.contentpath.startsWith(action.contentPath)) {
|
||||
resolveComment(comment);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, INITIAL_STATE);
|
||||
},
|
||||
INITIAL_STATE,
|
||||
);
|
||||
|
|
|
@ -18,12 +18,15 @@ export const INITIAL_STATE: SettingsState = {
|
|||
currentTab: null,
|
||||
};
|
||||
|
||||
export const reducer = produce((draft: SettingsState, action: actions.Action) => {
|
||||
switch (action.type) {
|
||||
case actions.UPDATE_GLOBAL_SETTINGS:
|
||||
update(draft, action.update);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}, INITIAL_STATE);
|
||||
export const reducer = produce(
|
||||
(draft: SettingsState, action: actions.Action) => {
|
||||
switch (action.type) {
|
||||
case actions.UPDATE_GLOBAL_SETTINGS:
|
||||
update(draft, action.update);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
INITIAL_STATE,
|
||||
);
|
||||
|
|
|
@ -51,11 +51,15 @@ export class LayoutController {
|
|||
return;
|
||||
}
|
||||
|
||||
const currentNodeTop = annotation.getAnchorNode(commentId === this.pinnedComment).getBoundingClientRect().top;
|
||||
const currentNodeTop = annotation
|
||||
.getAnchorNode(commentId === this.pinnedComment)
|
||||
.getBoundingClientRect().top;
|
||||
|
||||
this.commentDesiredPositions.set(
|
||||
commentId,
|
||||
currentNodeTop !== 0 ? currentNodeTop + document.documentElement.scrollTop + OFFSET : 0
|
||||
currentNodeTop !== 0
|
||||
? currentNodeTop + document.documentElement.scrollTop + OFFSET
|
||||
: 0,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -102,32 +106,35 @@ export class LayoutController {
|
|||
height: getOrDefault(this.commentHeights, commentId, 0),
|
||||
comments: [commentId],
|
||||
containsPinnedComment:
|
||||
this.pinnedComment !== null && commentId === this.pinnedComment,
|
||||
this.pinnedComment !== null && commentId === this.pinnedComment,
|
||||
pinnedCommentPosition: 0,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Group blocks by tabs
|
||||
const blocksByTab: Map<string | null, Block[]> = new Map();
|
||||
allBlocks.forEach(block => {
|
||||
allBlocks.forEach((block) => {
|
||||
const blocks = blocksByTab.get(block.tab) || [];
|
||||
blocks.push(block);
|
||||
blocksByTab.set(block.tab, blocks);
|
||||
});
|
||||
|
||||
// Get location of pinned comment
|
||||
const pinnedCommentPosition = this.pinnedComment ?
|
||||
this.commentDesiredPositions.get(this.pinnedComment) : undefined;
|
||||
const pinnedCommentTab = this.pinnedComment ?
|
||||
this.commentTabs.get(this.pinnedComment) : undefined;
|
||||
const pinnedCommentPosition = this.pinnedComment
|
||||
? this.commentDesiredPositions.get(this.pinnedComment)
|
||||
: undefined;
|
||||
const pinnedCommentTab = this.pinnedComment
|
||||
? this.commentTabs.get(this.pinnedComment)
|
||||
: undefined;
|
||||
|
||||
// For each tab, resolve positions of all the comments
|
||||
Array.from(blocksByTab.entries()).forEach(([tab, blocks]) => {
|
||||
const pinnedCommentOnThisTab = this.pinnedComment && pinnedCommentTab === tab;
|
||||
const pinnedCommentOnThisTab =
|
||||
this.pinnedComment && pinnedCommentTab === tab;
|
||||
|
||||
// Sort blocks
|
||||
blocks.sort(
|
||||
(block, comparisonBlock) => block.position - comparisonBlock.position
|
||||
(block, comparisonBlock) => block.position - comparisonBlock.position,
|
||||
);
|
||||
|
||||
// Resolve overlapping blocks
|
||||
|
@ -173,8 +180,7 @@ export class LayoutController {
|
|||
previousBlock.containsPinnedComment
|
||||
) {
|
||||
previousBlock.position =
|
||||
pinnedCommentPosition -
|
||||
previousBlock.pinnedCommentPosition;
|
||||
pinnedCommentPosition - previousBlock.pinnedCommentPosition;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -212,7 +218,10 @@ export class LayoutController {
|
|||
}
|
||||
|
||||
getCommentVisible(tab: string | null, commentId: number): boolean {
|
||||
return this.getCommentTabVisible(tab, commentId) && getOrDefault(this.commentDesiredPositions, commentId, 1) > 0;
|
||||
return (
|
||||
this.getCommentTabVisible(tab, commentId) &&
|
||||
getOrDefault(this.commentDesiredPositions, commentId, 1) > 0
|
||||
);
|
||||
}
|
||||
|
||||
getCommentPosition(commentId: number) {
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Store } from '../state';
|
||||
import {
|
||||
addComment,
|
||||
setFocusedComment,
|
||||
addReply,
|
||||
} from '../actions/comments';
|
||||
import { addComment, setFocusedComment, addReply } from '../actions/comments';
|
||||
import {
|
||||
Author,
|
||||
Comment,
|
||||
|
@ -35,7 +31,7 @@ export function RenderCommentsForStorybook({
|
|||
const layout = new LayoutController();
|
||||
|
||||
const commentsToRender: Comment[] = Array.from(
|
||||
state.comments.comments.values()
|
||||
state.comments.comments.values(),
|
||||
);
|
||||
|
||||
const commentsRendered = commentsToRender.map((comment) => (
|
||||
|
@ -47,7 +43,8 @@ export function RenderCommentsForStorybook({
|
|||
author || {
|
||||
id: 1,
|
||||
name: 'Admin',
|
||||
avatarUrl: 'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
}
|
||||
}
|
||||
comment={comment}
|
||||
|
@ -57,9 +54,7 @@ export function RenderCommentsForStorybook({
|
|||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<ol className="comments-list">{commentsRendered}</ol>
|
||||
);
|
||||
return <ol className="comments-list">{commentsRendered}</ol>;
|
||||
}
|
||||
|
||||
interface AddTestCommentOptions extends NewCommentOptions {
|
||||
|
@ -69,7 +64,7 @@ interface AddTestCommentOptions extends NewCommentOptions {
|
|||
|
||||
export function addTestComment(
|
||||
store: Store,
|
||||
options: AddTestCommentOptions
|
||||
options: AddTestCommentOptions,
|
||||
): number {
|
||||
const commentId = getNextCommentId();
|
||||
|
||||
|
@ -78,7 +73,8 @@ export function addTestComment(
|
|||
const author = options.author || {
|
||||
id: 1,
|
||||
name: 'Admin',
|
||||
avatarUrl: 'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
};
|
||||
|
||||
// We must have a remoteId unless the comment is being created
|
||||
|
@ -93,8 +89,16 @@ export function addTestComment(
|
|||
|
||||
store.dispatch(
|
||||
addComment(
|
||||
newComment('test', '', commentId, null, author, Date.now(), addCommentOptions)
|
||||
)
|
||||
newComment(
|
||||
'test',
|
||||
'',
|
||||
commentId,
|
||||
null,
|
||||
author,
|
||||
Date.now(),
|
||||
addCommentOptions,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (options.focused) {
|
||||
|
@ -112,13 +116,14 @@ interface AddTestReplyOptions extends NewReplyOptions {
|
|||
export function addTestReply(
|
||||
store: Store,
|
||||
commentId: number,
|
||||
options: AddTestReplyOptions
|
||||
options: AddTestReplyOptions,
|
||||
) {
|
||||
const addReplyOptions = options;
|
||||
const author = options.author || {
|
||||
id: 1,
|
||||
name: 'Admin',
|
||||
avatarUrl: 'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
avatarUrl:
|
||||
'https://gravatar.com/avatar/e31ec811942afbf7b9ce0ac5affe426f?s=200&d=robohash&r=x',
|
||||
};
|
||||
|
||||
if (!options.remoteId) {
|
||||
|
@ -126,6 +131,9 @@ export function addTestReply(
|
|||
}
|
||||
|
||||
store.dispatch(
|
||||
addReply(commentId, newCommentReply(1, author, Date.now(), addReplyOptions))
|
||||
addReply(
|
||||
commentId,
|
||||
newCommentReply(1, author, Date.now(), addReplyOptions),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
.Draftail-Toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.Draftail-ToolbarGroup:last-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.Draftail-ToolbarGroup:last-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Draftail-CommentControl {
|
||||
float: right;
|
||||
color: $color-teal;
|
||||
}
|
||||
.Draftail-CommentControl {
|
||||
float: right;
|
||||
color: $color-teal;
|
||||
}
|
||||
}
|
||||
|
||||
.Draftail-CommentControl .Draftail-ToolbarButton {
|
||||
.icon-comment-large-outline {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-comment-large-reversed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
|
||||
.icon-comment-large-outline {
|
||||
display: block;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-comment-large-reversed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
|
||||
.icon-comment-large-outline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-comment-large-reversed {
|
||||
display: block;
|
||||
}
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ describe('CommentableEditor', () => {
|
|||
const contentpath = 'test-contentpath';
|
||||
const getComments = (app: CommentApp) =>
|
||||
app.utils.selectCommentsForContentPathFactory(contentpath)(
|
||||
app.store.getState()
|
||||
app.store.getState(),
|
||||
);
|
||||
beforeAll(() => {
|
||||
const commentsElement = document.createElement('div');
|
||||
|
@ -113,7 +113,7 @@ describe('CommentableEditor', () => {
|
|||
commentApp.setVisible(true);
|
||||
const editor = mount(getEditorComponent(commentApp));
|
||||
const controls = editor.findWhere(
|
||||
(n) => n.name() === 'ToolbarButton' && n.prop('name') === 'comment'
|
||||
(n) => n.name() === 'ToolbarButton' && n.prop('name') === 'comment',
|
||||
);
|
||||
expect(controls).toHaveLength(1);
|
||||
editor.unmount();
|
||||
|
@ -122,7 +122,7 @@ describe('CommentableEditor', () => {
|
|||
commentApp.store.dispatch(updateGlobalSettings({ commentsEnabled: false }));
|
||||
const editor = mount(getEditorComponent(commentApp));
|
||||
const controls = editor.findWhere(
|
||||
(n) => n.name() === 'ToolbarButton' && n.prop('name') === 'comment'
|
||||
(n) => n.name() === 'ToolbarButton' && n.prop('name') === 'comment',
|
||||
);
|
||||
expect(controls).toHaveLength(0);
|
||||
editor.unmount();
|
||||
|
@ -130,8 +130,8 @@ describe('CommentableEditor', () => {
|
|||
it('can update comment positions', () => {
|
||||
commentApp.store.dispatch(
|
||||
commentApp.actions.addComment(
|
||||
newComment('test-contentpath', 'old_position', 1, null, null, 0, {})
|
||||
)
|
||||
newComment('test-contentpath', 'old_position', 1, null, null, 0, {}),
|
||||
),
|
||||
);
|
||||
// Test that a comment with no annotation will not have its position updated
|
||||
updateCommentPositions({
|
||||
|
@ -140,7 +140,7 @@ describe('CommentableEditor', () => {
|
|||
commentApp: commentApp,
|
||||
});
|
||||
expect(commentApp.store.getState().comments.comments.get(1)?.position).toBe(
|
||||
'old_position'
|
||||
'old_position',
|
||||
);
|
||||
|
||||
commentApp.updateAnnotation(new DraftailInlineAnnotation(fieldNode), 1);
|
||||
|
@ -152,7 +152,7 @@ describe('CommentableEditor', () => {
|
|||
commentApp: commentApp,
|
||||
});
|
||||
expect(commentApp.store.getState().comments.comments.get(1)?.position).toBe(
|
||||
'[]'
|
||||
'[]',
|
||||
);
|
||||
|
||||
// Test that a comment with a style range has that style range recorded accurately in the state
|
||||
|
@ -162,7 +162,7 @@ describe('CommentableEditor', () => {
|
|||
commentApp: commentApp,
|
||||
});
|
||||
expect(commentApp.store.getState().comments.comments.get(1)?.position).toBe(
|
||||
'[{"key":"a","start":0,"end":1}]'
|
||||
'[{"key":"a","start":0,"end":1}]',
|
||||
);
|
||||
});
|
||||
it('can add comments to editor', () => {
|
||||
|
@ -175,9 +175,9 @@ describe('CommentableEditor', () => {
|
|||
null,
|
||||
null,
|
||||
0,
|
||||
{}
|
||||
)
|
||||
)
|
||||
{},
|
||||
),
|
||||
),
|
||||
);
|
||||
// Test that comment styles are correctly added to the editor,
|
||||
// and the comments in the state have annotations assigned
|
||||
|
@ -185,7 +185,7 @@ describe('CommentableEditor', () => {
|
|||
createEditorStateFromRaw(content).getCurrentContent(),
|
||||
getComments(commentApp),
|
||||
commentApp,
|
||||
() => new DraftailInlineAnnotation(fieldNode)
|
||||
() => new DraftailInlineAnnotation(fieldNode),
|
||||
);
|
||||
newContentState.getFirstBlock().findStyleRanges(
|
||||
(metadata) => !metadata.getStyle().isEmpty(),
|
||||
|
@ -194,14 +194,14 @@ describe('CommentableEditor', () => {
|
|||
newContentState
|
||||
.getFirstBlock()
|
||||
.getInlineStyleAt(start)
|
||||
.has('COMMENT-1')
|
||||
.has('COMMENT-1'),
|
||||
).toBe(true);
|
||||
expect(start).toBe(0);
|
||||
expect(end).toBe(1);
|
||||
}
|
||||
},
|
||||
);
|
||||
expect(
|
||||
commentApp.store.getState().comments.comments.get(1)?.annotation
|
||||
commentApp.store.getState().comments.comments.get(1)?.annotation,
|
||||
).not.toBe(null);
|
||||
});
|
||||
it('can find the least common comment id', () => {
|
||||
|
|
|
@ -17,11 +17,19 @@ import {
|
|||
Modifier,
|
||||
RawDraftContentState,
|
||||
RichUtils,
|
||||
SelectionState
|
||||
SelectionState,
|
||||
} from 'draft-js';
|
||||
import type { DraftEditorLeaf } from 'draft-js/lib/DraftEditorLeaf.react';
|
||||
import { filterInlineStyles } from 'draftjs-filters';
|
||||
import React, { MutableRefObject, ReactNode, ReactText, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, {
|
||||
MutableRefObject,
|
||||
ReactNode,
|
||||
ReactText,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
|
||||
import { STRINGS } from '../../../config/wagtailConfig';
|
||||
|
@ -56,10 +64,10 @@ export class DraftailInlineAnnotation implements Annotation {
|
|||
* Create an inline annotation
|
||||
* @param {Element} field - an element to provide the fallback position for comments without any inline decorators
|
||||
*/
|
||||
field: Element
|
||||
decoratorRefs: Map<DecoratorRef, BlockKey>
|
||||
focusedBlockKey: BlockKey
|
||||
cachedMedianRef: DecoratorRef | null
|
||||
field: Element;
|
||||
decoratorRefs: Map<DecoratorRef, BlockKey>;
|
||||
focusedBlockKey: BlockKey;
|
||||
cachedMedianRef: DecoratorRef | null;
|
||||
|
||||
constructor(field: Element) {
|
||||
this.field = field;
|
||||
|
@ -90,7 +98,8 @@ export class DraftailInlineAnnotation implements Annotation {
|
|||
}
|
||||
static getMedianRef(refArray: Array<DecoratorRef>) {
|
||||
const refs = refArray.sort(
|
||||
(firstRef, secondRef) => this.getHeightForRef(firstRef) - this.getHeightForRef(secondRef)
|
||||
(firstRef, secondRef) =>
|
||||
this.getHeightForRef(firstRef) - this.getHeightForRef(secondRef),
|
||||
);
|
||||
const length = refs.length;
|
||||
if (length > 0) {
|
||||
|
@ -111,13 +120,13 @@ export class DraftailInlineAnnotation implements Annotation {
|
|||
// if the highlight has somehow been split up
|
||||
medianRef = DraftailInlineAnnotation.getMedianRef(
|
||||
Array.from(this.decoratorRefs.keys()).filter(
|
||||
(ref) => this.decoratorRefs.get(ref) === this.focusedBlockKey
|
||||
)
|
||||
(ref) => this.decoratorRefs.get(ref) === this.focusedBlockKey,
|
||||
),
|
||||
);
|
||||
} else if (!this.cachedMedianRef) {
|
||||
// Our cache is empty - try to update it
|
||||
medianRef = DraftailInlineAnnotation.getMedianRef(
|
||||
Array.from(this.decoratorRefs.keys())
|
||||
Array.from(this.decoratorRefs.keys()),
|
||||
);
|
||||
this.cachedMedianRef = medianRef;
|
||||
} else {
|
||||
|
@ -130,22 +139,28 @@ export class DraftailInlineAnnotation implements Annotation {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function applyInlineStyleToRange({ contentState, style, blockKey, start, end }:
|
||||
{contentState: ContentState,
|
||||
style: string,
|
||||
blockKey: BlockKey,
|
||||
start: number,
|
||||
end: number}
|
||||
) {
|
||||
return Modifier.applyInlineStyle(contentState,
|
||||
function applyInlineStyleToRange({
|
||||
contentState,
|
||||
style,
|
||||
blockKey,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
contentState: ContentState;
|
||||
style: string;
|
||||
blockKey: BlockKey;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
return Modifier.applyInlineStyle(
|
||||
contentState,
|
||||
new SelectionState({
|
||||
anchorKey: blockKey,
|
||||
anchorOffset: start,
|
||||
focusKey: blockKey,
|
||||
focusOffset: end
|
||||
focusOffset: end,
|
||||
}),
|
||||
style
|
||||
style,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -158,11 +173,16 @@ function getFullSelectionState(contentState: ContentState) {
|
|||
anchorKey: contentState.getFirstBlock().getKey(),
|
||||
anchorOffset: 0,
|
||||
focusKey: lastBlock.getKey(),
|
||||
focusOffset: lastBlock.getLength()
|
||||
focusOffset: lastBlock.getLength(),
|
||||
});
|
||||
}
|
||||
|
||||
function addNewComment(editorState: EditorState, fieldNode: Element, commentApp: CommentApp, contentPath: string) {
|
||||
function addNewComment(
|
||||
editorState: EditorState,
|
||||
fieldNode: Element,
|
||||
commentApp: CommentApp,
|
||||
contentPath: string,
|
||||
) {
|
||||
let state = editorState;
|
||||
const annotation = new DraftailInlineAnnotation(fieldNode);
|
||||
const commentId = commentApp.makeComment(annotation, contentPath, '[]');
|
||||
|
@ -170,40 +190,47 @@ function addNewComment(editorState: EditorState, fieldNode: Element, commentApp:
|
|||
// If the selection is collapsed, add the comment highlight on the whole field
|
||||
state = EditorState.acceptSelection(
|
||||
editorState,
|
||||
selection.isCollapsed() ? getFullSelectionState(editorState.getCurrentContent()) : selection
|
||||
selection.isCollapsed()
|
||||
? getFullSelectionState(editorState.getCurrentContent())
|
||||
: selection,
|
||||
);
|
||||
|
||||
return (
|
||||
EditorState.acceptSelection(
|
||||
RichUtils.toggleInlineStyle(
|
||||
state,
|
||||
`${COMMENT_STYLE_IDENTIFIER}${commentId}`
|
||||
),
|
||||
selection
|
||||
)
|
||||
return EditorState.acceptSelection(
|
||||
RichUtils.toggleInlineStyle(
|
||||
state,
|
||||
`${COMMENT_STYLE_IDENTIFIER}${commentId}`,
|
||||
),
|
||||
selection,
|
||||
);
|
||||
}
|
||||
|
||||
interface ControlProps {
|
||||
getEditorState: () => EditorState,
|
||||
onChange: (editorState: EditorState) => void
|
||||
getEditorState: () => EditorState;
|
||||
onChange: (editorState: EditorState) => void;
|
||||
}
|
||||
|
||||
function getCommentControl(commentApp: CommentApp, contentPath: string, fieldNode: Element) {
|
||||
function getCommentControl(
|
||||
commentApp: CommentApp,
|
||||
contentPath: string,
|
||||
fieldNode: Element,
|
||||
) {
|
||||
return ({ getEditorState, onChange }: ControlProps) => (
|
||||
<span className="Draftail-CommentControl" data-comment-add>
|
||||
<ToolbarButton
|
||||
name="comment"
|
||||
active={false}
|
||||
title={`${STRINGS.ADD_A_COMMENT}\n${IS_MAC_OS ? '⌘ + Alt + M' : 'Ctrl + Alt + M'}`}
|
||||
title={`${STRINGS.ADD_A_COMMENT}\n${
|
||||
IS_MAC_OS ? '⌘ + Alt + M' : 'Ctrl + Alt + M'
|
||||
}`}
|
||||
icon={
|
||||
<>
|
||||
<Icon name="comment-large-outline" /> <Icon name="comment-large-reversed" />
|
||||
<Icon name="comment-large-outline" />{' '}
|
||||
<Icon name="comment-large-reversed" />
|
||||
</>
|
||||
}
|
||||
onClick={() => {
|
||||
onChange(
|
||||
addNewComment(getEditorState(), fieldNode, commentApp, contentPath)
|
||||
addNewComment(getEditorState(), fieldNode, commentApp, contentPath),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -222,50 +249,59 @@ function getIdForCommentStyle(style: string) {
|
|||
function findCommentStyleRanges(
|
||||
contentBlock: ContentBlock,
|
||||
callback: (start: number, end: number) => void,
|
||||
filterFn?: (metadata: CharacterMetadata) => boolean) {
|
||||
filterFn?: (metadata: CharacterMetadata) => boolean,
|
||||
) {
|
||||
// Find comment style ranges that do not overlap an existing entity
|
||||
const filterFunction = filterFn || ((metadata: CharacterMetadata) => metadata.getStyle().some(styleIsComment));
|
||||
const filterFunction =
|
||||
filterFn ||
|
||||
((metadata: CharacterMetadata) => metadata.getStyle().some(styleIsComment));
|
||||
const entityRanges: Array<[number, number]> = [];
|
||||
contentBlock.findEntityRanges(
|
||||
character => character.getEntity() !== null,
|
||||
(start, end) => entityRanges.push([start, end])
|
||||
(character) => character.getEntity() !== null,
|
||||
(start, end) => entityRanges.push([start, end]),
|
||||
);
|
||||
contentBlock.findStyleRanges(
|
||||
filterFunction,
|
||||
(start, end) => {
|
||||
const interferingEntityRanges = entityRanges.filter(value => value[1] > start).filter(value => value[0] < end);
|
||||
let currentPosition = start;
|
||||
interferingEntityRanges.forEach((value) => {
|
||||
const [entityStart, entityEnd] = value;
|
||||
if (entityStart > currentPosition) {
|
||||
callback(currentPosition, entityStart);
|
||||
}
|
||||
currentPosition = entityEnd;
|
||||
});
|
||||
if (currentPosition < end) {
|
||||
callback(start, end);
|
||||
contentBlock.findStyleRanges(filterFunction, (start, end) => {
|
||||
const interferingEntityRanges = entityRanges
|
||||
.filter((value) => value[1] > start)
|
||||
.filter((value) => value[0] < end);
|
||||
let currentPosition = start;
|
||||
interferingEntityRanges.forEach((value) => {
|
||||
const [entityStart, entityEnd] = value;
|
||||
if (entityStart > currentPosition) {
|
||||
callback(currentPosition, entityStart);
|
||||
}
|
||||
currentPosition = entityEnd;
|
||||
});
|
||||
if (currentPosition < end) {
|
||||
callback(start, end);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function updateCommentPositions({ editorState, comments, commentApp }:
|
||||
{
|
||||
editorState: EditorState,
|
||||
comments: Array<Comment>,
|
||||
commentApp: CommentApp
|
||||
}) {
|
||||
export function updateCommentPositions({
|
||||
editorState,
|
||||
comments,
|
||||
commentApp,
|
||||
}: {
|
||||
editorState: EditorState;
|
||||
comments: Array<Comment>;
|
||||
commentApp: CommentApp;
|
||||
}) {
|
||||
// Construct a map of comment id -> array of style ranges
|
||||
const commentPositions = new Map();
|
||||
|
||||
editorState.getCurrentContent().getBlocksAsArray().forEach(
|
||||
(block) => {
|
||||
editorState
|
||||
.getCurrentContent()
|
||||
.getBlocksAsArray()
|
||||
.forEach((block) => {
|
||||
const key = block.getKey();
|
||||
block.findStyleRanges((metadata) => metadata.getStyle().some(styleIsComment),
|
||||
block.findStyleRanges(
|
||||
(metadata) => metadata.getStyle().some(styleIsComment),
|
||||
(start, end) => {
|
||||
block.getInlineStyleAt(start).filter(styleIsComment).forEach(
|
||||
(style) => {
|
||||
block
|
||||
.getInlineStyleAt(start)
|
||||
.filter(styleIsComment)
|
||||
.forEach((style) => {
|
||||
// We have already filtered out any undefined styles, so cast here
|
||||
const id = getIdForCommentStyle(style as string);
|
||||
let existingPosition = commentPositions.get(id);
|
||||
|
@ -275,29 +311,30 @@ export function updateCommentPositions({ editorState, comments, commentApp }:
|
|||
existingPosition.push({
|
||||
key: key,
|
||||
start: start,
|
||||
end: end
|
||||
end: end,
|
||||
});
|
||||
commentPositions.set(id, existingPosition);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
comments.filter(comment => comment.annotation).forEach((comment) => {
|
||||
// if a comment has an annotation - ie the field has it inserted - update its position
|
||||
const newPosition = commentPositions.get(comment.localId);
|
||||
const serializedNewPosition = newPosition ? JSON.stringify(newPosition) : '[]';
|
||||
if (comment.position !== serializedNewPosition) {
|
||||
commentApp.store.dispatch(
|
||||
commentApp.actions.updateComment(
|
||||
comment.localId,
|
||||
{ position: serializedNewPosition }
|
||||
)
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
comments
|
||||
.filter((comment) => comment.annotation)
|
||||
.forEach((comment) => {
|
||||
// if a comment has an annotation - ie the field has it inserted - update its position
|
||||
const newPosition = commentPositions.get(comment.localId);
|
||||
const serializedNewPosition = newPosition
|
||||
? JSON.stringify(newPosition)
|
||||
: '[]';
|
||||
if (comment.position !== serializedNewPosition) {
|
||||
commentApp.store.dispatch(
|
||||
commentApp.actions.updateComment(comment.localId, {
|
||||
position: serializedNewPosition,
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -305,7 +342,9 @@ export function updateCommentPositions({ editorState, comments, commentApp }:
|
|||
* has the fewest style ranges within the block, or null if no comment exists at the offset
|
||||
*/
|
||||
export function findLeastCommonCommentId(block: ContentBlock, offset: number) {
|
||||
const styles = block.getInlineStyleAt(offset).filter(styleIsComment) as Immutable.OrderedSet<string>;
|
||||
const styles = block
|
||||
.getInlineStyleAt(offset)
|
||||
.filter(styleIsComment) as Immutable.OrderedSet<string>;
|
||||
let styleToUse: string;
|
||||
const styleCount = styles.count();
|
||||
if (styleCount === 0) {
|
||||
|
@ -322,15 +361,20 @@ export function findLeastCommonCommentId(block: ContentBlock, offset: number) {
|
|||
// this casting should be removed
|
||||
let styleFreq = styles.map((style) => {
|
||||
let counter = 0;
|
||||
findCommentStyleRanges(block,
|
||||
() => { counter = counter + 1; },
|
||||
(metadata) => metadata.getStyle().some(rangeStyle => rangeStyle === style)
|
||||
findCommentStyleRanges(
|
||||
block,
|
||||
() => {
|
||||
counter = counter + 1;
|
||||
},
|
||||
(metadata) =>
|
||||
metadata.getStyle().some((rangeStyle) => rangeStyle === style),
|
||||
);
|
||||
return [style, counter];
|
||||
}) as unknown as Immutable.OrderedSet<[string, number]>;
|
||||
|
||||
styleFreq = styleFreq.sort(
|
||||
(firstStyleCount, secondStyleCount) => firstStyleCount[1] - secondStyleCount[1]
|
||||
styleFreq = styleFreq.sort(
|
||||
(firstStyleCount, secondStyleCount) =>
|
||||
firstStyleCount[1] - secondStyleCount[1],
|
||||
) as Immutable.OrderedSet<[string, number]>;
|
||||
|
||||
styleToUse = styleFreq.first()[0];
|
||||
|
@ -341,8 +385,8 @@ export function findLeastCommonCommentId(block: ContentBlock, offset: number) {
|
|||
}
|
||||
|
||||
interface DecoratorProps {
|
||||
contentState: ContentState,
|
||||
children?: Array<DraftEditorLeaf>
|
||||
contentState: ContentState;
|
||||
children?: Array<DraftEditorLeaf>;
|
||||
}
|
||||
|
||||
function getCommentDecorator(commentApp: CommentApp) {
|
||||
|
@ -358,11 +402,10 @@ function getCommentDecorator(commentApp: CommentApp) {
|
|||
const blockKey: BlockKey = children[0].props.block.getKey();
|
||||
const start: number = children[0].props.start;
|
||||
|
||||
const commentId = useMemo(
|
||||
() => {
|
||||
const block = contentState.getBlockForKey(blockKey);
|
||||
return findLeastCommonCommentId(block, start);
|
||||
}, [blockKey, start]);
|
||||
const commentId = useMemo(() => {
|
||||
const block = contentState.getBlockForKey(blockKey);
|
||||
return findLeastCommonCommentId(block, start);
|
||||
}, [blockKey, start]);
|
||||
const annotationNode = useRef(null);
|
||||
useEffect(() => {
|
||||
// Add a ref to the annotation, allowing the comment to float alongside the attached text.
|
||||
|
@ -379,9 +422,7 @@ function getCommentDecorator(commentApp: CommentApp) {
|
|||
}, [commentId, annotationNode, blockKey]);
|
||||
|
||||
if (!enabled) {
|
||||
return <>
|
||||
{children}
|
||||
</>;
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
const onClick = () => {
|
||||
|
@ -390,7 +431,11 @@ function getCommentDecorator(commentApp: CommentApp) {
|
|||
return;
|
||||
}
|
||||
const annotation = commentApp.layout.commentAnnotations.get(commentId);
|
||||
if (annotation && annotation instanceof DraftailInlineAnnotation && annotationNode) {
|
||||
if (
|
||||
annotation &&
|
||||
annotation instanceof DraftailInlineAnnotation &&
|
||||
annotationNode
|
||||
) {
|
||||
annotation.setFocusedBlockKey(blockKey);
|
||||
}
|
||||
|
||||
|
@ -398,8 +443,8 @@ function getCommentDecorator(commentApp: CommentApp) {
|
|||
commentApp.store.dispatch(
|
||||
commentApp.actions.setFocusedComment(commentId, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: false
|
||||
})
|
||||
forceFocus: false,
|
||||
}),
|
||||
);
|
||||
};
|
||||
return (
|
||||
|
@ -417,7 +462,10 @@ function getCommentDecorator(commentApp: CommentApp) {
|
|||
return CommentDecorator;
|
||||
}
|
||||
|
||||
function forceResetEditorState(editorState: EditorState, replacementContent?: ContentState) {
|
||||
function forceResetEditorState(
|
||||
editorState: EditorState,
|
||||
replacementContent?: ContentState,
|
||||
) {
|
||||
const content = replacementContent || editorState.getCurrentContent();
|
||||
const state = EditorState.set(
|
||||
EditorState.createWithContent(content, editorState.getDecorator()),
|
||||
|
@ -425,8 +473,8 @@ function forceResetEditorState(editorState: EditorState, replacementContent?: Co
|
|||
selection: editorState.getSelection(),
|
||||
undoStack: editorState.getUndoStack(),
|
||||
redoStack: editorState.getRedoStack(),
|
||||
inlineStyleOverride: editorState.getInlineStyleOverride()
|
||||
}
|
||||
inlineStyleOverride: editorState.getInlineStyleOverride(),
|
||||
},
|
||||
);
|
||||
return EditorState.acceptSelection(state, state.getSelection());
|
||||
}
|
||||
|
@ -435,39 +483,43 @@ export function addCommentsToEditor(
|
|||
contentState: ContentState,
|
||||
comments: Comment[],
|
||||
commentApp: CommentApp,
|
||||
getAnnotation: () => Annotation
|
||||
getAnnotation: () => Annotation,
|
||||
) {
|
||||
let newContentState = contentState;
|
||||
comments.filter(comment => !comment.annotation).forEach((comment) => {
|
||||
commentApp.updateAnnotation(getAnnotation(), comment.localId);
|
||||
const style = `${COMMENT_STYLE_IDENTIFIER}${comment.localId}`;
|
||||
try {
|
||||
const positions = JSON.parse(comment.position);
|
||||
positions.forEach((position) => {
|
||||
newContentState = applyInlineStyleToRange({
|
||||
contentState: newContentState,
|
||||
blockKey: position.key,
|
||||
start: position.start,
|
||||
end: position.end,
|
||||
style
|
||||
comments
|
||||
.filter((comment) => !comment.annotation)
|
||||
.forEach((comment) => {
|
||||
commentApp.updateAnnotation(getAnnotation(), comment.localId);
|
||||
const style = `${COMMENT_STYLE_IDENTIFIER}${comment.localId}`;
|
||||
try {
|
||||
const positions = JSON.parse(comment.position);
|
||||
positions.forEach((position) => {
|
||||
newContentState = applyInlineStyleToRange({
|
||||
contentState: newContentState,
|
||||
blockKey: position.key,
|
||||
start: position.start,
|
||||
end: position.end,
|
||||
style,
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.error(`Error loading comment position for comment ${comment.localId}`);
|
||||
console.error(err);
|
||||
/* esline-enable no-console */
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
/* eslint-disable no-console */
|
||||
console.error(
|
||||
`Error loading comment position for comment ${comment.localId}`,
|
||||
);
|
||||
console.error(err);
|
||||
/* esline-enable no-console */
|
||||
}
|
||||
});
|
||||
return newContentState;
|
||||
}
|
||||
|
||||
type Direction = 'RTL' | 'LTR'
|
||||
type Direction = 'RTL' | 'LTR';
|
||||
|
||||
function handleArrowAtContentEnd(
|
||||
state: EditorState,
|
||||
setEditorState: (newState: EditorState) => void,
|
||||
direction: Direction
|
||||
direction: Direction,
|
||||
) {
|
||||
// If at the end of content and pressing in the same direction as the text, remove the comment style from
|
||||
// further typing
|
||||
|
@ -476,49 +528,53 @@ function handleArrowAtContentEnd(
|
|||
const lastBlock = newState.getCurrentContent().getLastBlock();
|
||||
const textDirection = newState.getDirectionMap().get(lastBlock.getKey());
|
||||
|
||||
if (!(
|
||||
textDirection === direction
|
||||
&& selection.isCollapsed()
|
||||
&& selection.getAnchorKey() === lastBlock.getKey()
|
||||
&& selection.getAnchorOffset() === lastBlock.getLength()
|
||||
)) {
|
||||
if (
|
||||
!(
|
||||
textDirection === direction &&
|
||||
selection.isCollapsed() &&
|
||||
selection.getAnchorKey() === lastBlock.getKey() &&
|
||||
selection.getAnchorOffset() === lastBlock.getLength()
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setEditorState(
|
||||
EditorState.setInlineStyleOverride(
|
||||
newState,
|
||||
newState.getCurrentInlineStyle().filter(style => !styleIsComment(style)) as DraftInlineStyle
|
||||
)
|
||||
newState
|
||||
.getCurrentInlineStyle()
|
||||
.filter((style) => !styleIsComment(style)) as DraftInlineStyle,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
interface InlineStyle {
|
||||
label?: string,
|
||||
description?: string,
|
||||
icon?: string | string[] | Node,
|
||||
type: string,
|
||||
style?: Record<string, string | number | ReactText | undefined >
|
||||
label?: string;
|
||||
description?: string;
|
||||
icon?: string | string[] | Node;
|
||||
type: string;
|
||||
style?: Record<string, string | number | ReactText | undefined>;
|
||||
}
|
||||
|
||||
interface ColorConfigProp {
|
||||
standardHighlight: string,
|
||||
overlappingHighlight: string,
|
||||
focusedHighlight: string
|
||||
standardHighlight: string;
|
||||
overlappingHighlight: string;
|
||||
focusedHighlight: string;
|
||||
}
|
||||
|
||||
interface CommentableEditorProps {
|
||||
commentApp: CommentApp,
|
||||
fieldNode: Element,
|
||||
contentPath: string,
|
||||
rawContentState: RawDraftContentState,
|
||||
onSave: (rawContent: RawDraftContentState) => void,
|
||||
inlineStyles: Array<InlineStyle>,
|
||||
editorRef: (editor: ReactNode) => void
|
||||
colorConfig: ColorConfigProp
|
||||
isCommentShortcut: (e: React.KeyboardEvent) => boolean
|
||||
commentApp: CommentApp;
|
||||
fieldNode: Element;
|
||||
contentPath: string;
|
||||
rawContentState: RawDraftContentState;
|
||||
onSave: (rawContent: RawDraftContentState) => void;
|
||||
inlineStyles: Array<InlineStyle>;
|
||||
editorRef: (editor: ReactNode) => void;
|
||||
colorConfig: ColorConfigProp;
|
||||
isCommentShortcut: (e: React.KeyboardEvent) => boolean;
|
||||
// Unfortunately the EditorPlugin type isn't exported in our version of 'draft-js-plugins-editor'
|
||||
plugins?: Record<string, unknown>[]
|
||||
controls?: Array<(props: ControlProps) => JSX.Element>
|
||||
plugins?: Record<string, unknown>[];
|
||||
controls?: Array<(props: ControlProps) => JSX.Element>;
|
||||
}
|
||||
|
||||
function CommentableEditor({
|
||||
|
@ -536,33 +592,35 @@ function CommentableEditor({
|
|||
...options
|
||||
}: CommentableEditorProps) {
|
||||
const [editorState, setEditorState] = useState(() =>
|
||||
createEditorStateFromRaw(rawContentState)
|
||||
createEditorStateFromRaw(rawContentState),
|
||||
);
|
||||
const CommentControl = useMemo(
|
||||
() => getCommentControl(commentApp, contentPath, fieldNode),
|
||||
[commentApp, contentPath, fieldNode]
|
||||
[commentApp, contentPath, fieldNode],
|
||||
);
|
||||
const commentsSelector = useMemo(
|
||||
() => commentApp.utils.selectCommentsForContentPathFactory(contentPath),
|
||||
[contentPath, commentApp]
|
||||
[contentPath, commentApp],
|
||||
);
|
||||
const CommentDecorator = useMemo(
|
||||
() => getCommentDecorator(commentApp),
|
||||
[commentApp],
|
||||
);
|
||||
const CommentDecorator = useMemo(() => getCommentDecorator(commentApp), [
|
||||
commentApp,
|
||||
]);
|
||||
const comments = useSelector(commentsSelector, shallowEqual);
|
||||
const enabled = useSelector(commentApp.selectors.selectEnabled);
|
||||
const focusedId = useSelector(commentApp.selectors.selectFocused);
|
||||
|
||||
const ids = useMemo(() => comments.map((comment) => comment.localId), [
|
||||
comments,
|
||||
]);
|
||||
const ids = useMemo(
|
||||
() => comments.map((comment) => comment.localId),
|
||||
[comments],
|
||||
);
|
||||
|
||||
const commentStyles: Array<InlineStyle> = useMemo(
|
||||
() =>
|
||||
ids.map((id) => ({
|
||||
type: `${COMMENT_STYLE_IDENTIFIER}${id}`
|
||||
type: `${COMMENT_STYLE_IDENTIFIER}${id}`,
|
||||
})),
|
||||
[ids]
|
||||
[ids],
|
||||
);
|
||||
|
||||
const [uniqueStyleId, setUniqueStyleId] = useState(0);
|
||||
|
@ -574,16 +632,17 @@ function CommentableEditor({
|
|||
// Only trigger a focus-related rerender if the current focused comment is inside the field, or the previous one was
|
||||
const validFocusChange =
|
||||
previousFocused !== focusedId &&
|
||||
((previousFocused && previousIds && previousIds.includes(previousFocused)) ||
|
||||
focusedId && ids.includes(focusedId));
|
||||
((previousFocused &&
|
||||
previousIds &&
|
||||
previousIds.includes(previousFocused)) ||
|
||||
(focusedId && ids.includes(focusedId)));
|
||||
|
||||
if (
|
||||
!validFocusChange &&
|
||||
previousEnabled === enabled &&
|
||||
(
|
||||
previousIds === ids ||
|
||||
(previousIds.length === ids.length && previousIds.every((value, index) => value === ids[index]))
|
||||
)
|
||||
(previousIds === ids ||
|
||||
(previousIds.length === ids.length &&
|
||||
previousIds.every((value, index) => value === ids[index])))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -593,7 +652,7 @@ function CommentableEditor({
|
|||
inlineStyles
|
||||
.map((style) => style.type)
|
||||
.concat(ids.map((id) => `${COMMENT_STYLE_IDENTIFIER}${id}`)),
|
||||
editorState.getCurrentContent()
|
||||
editorState.getCurrentContent(),
|
||||
);
|
||||
// Force reset the editor state to ensure redecoration, and apply a new (blank) inline style to force
|
||||
// inline style rerender. This must be entirely new for the rerender to trigger, hence the unique
|
||||
|
@ -606,9 +665,9 @@ function CommentableEditor({
|
|||
Modifier.applyInlineStyle(
|
||||
filteredContent,
|
||||
getFullSelectionState(filteredContent),
|
||||
`STYLE_RERENDER_${uniqueStyleId}`
|
||||
)
|
||||
)
|
||||
`STYLE_RERENDER_${uniqueStyleId}`,
|
||||
),
|
||||
),
|
||||
);
|
||||
setUniqueStyleId((id) => (id + 1) % 200);
|
||||
}, [focusedId, enabled, inlineStyles, ids, editorState]);
|
||||
|
@ -617,7 +676,10 @@ function CommentableEditor({
|
|||
// if there are any comments without annotations, we need to add them to the EditorState
|
||||
const contentState = editorState.getCurrentContent();
|
||||
const newContentState = addCommentsToEditor(
|
||||
contentState, comments, commentApp, () => new DraftailInlineAnnotation(fieldNode)
|
||||
contentState,
|
||||
comments,
|
||||
commentApp,
|
||||
() => new DraftailInlineAnnotation(fieldNode),
|
||||
);
|
||||
if (contentState !== newContentState) {
|
||||
setEditorState(forceResetEditorState(editorState, newContentState));
|
||||
|
@ -633,19 +695,16 @@ function CommentableEditor({
|
|||
editorState,
|
||||
filterInlineStyles(
|
||||
inlineStyles.map((style) => style.type),
|
||||
editorState.getCurrentContent()
|
||||
editorState.getCurrentContent(),
|
||||
),
|
||||
'change-inline-style'
|
||||
'change-inline-style',
|
||||
);
|
||||
timeoutRef.current = window.setTimeout(
|
||||
() => {
|
||||
onSave(serialiseEditorStateToRaw(filteredEditorState));
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
onSave(serialiseEditorStateToRaw(filteredEditorState));
|
||||
|
||||
// Next, update comment positions in the redux store
|
||||
updateCommentPositions({ editorState, comments, commentApp });
|
||||
},
|
||||
250
|
||||
);
|
||||
// Next, update comment positions in the redux store
|
||||
updateCommentPositions({ editorState, comments, commentApp });
|
||||
}, 250);
|
||||
return () => {
|
||||
window.clearTimeout(timeoutRef.current);
|
||||
};
|
||||
|
@ -659,21 +718,27 @@ function CommentableEditor({
|
|||
if (['undo', 'redo'].includes(state.getLastChangeType())) {
|
||||
const filteredContent = filterInlineStyles(
|
||||
inlineStyles
|
||||
.map(style => style.type)
|
||||
.concat(ids.map(id => `${COMMENT_STYLE_IDENTIFIER}${id}`)),
|
||||
state.getCurrentContent()
|
||||
.map((style) => style.type)
|
||||
.concat(ids.map((id) => `${COMMENT_STYLE_IDENTIFIER}${id}`)),
|
||||
state.getCurrentContent(),
|
||||
);
|
||||
newEditorState = forceResetEditorState(state, filteredContent);
|
||||
} else if (state.getLastChangeType() === 'split-block') {
|
||||
const content = newEditorState.getCurrentContent();
|
||||
const selection = newEditorState.getSelection();
|
||||
const style = content.getBlockForKey(selection.getAnchorKey()).getInlineStyleAt(selection.getAnchorOffset());
|
||||
const style = content
|
||||
.getBlockForKey(selection.getAnchorKey())
|
||||
.getInlineStyleAt(selection.getAnchorOffset());
|
||||
// If starting a new paragraph (and not splitting an existing comment)
|
||||
// ensure any new text entered doesn't get a comment style
|
||||
if (!style.some(styleName => styleIsComment(styleName))) {
|
||||
if (!style.some((styleName) => styleIsComment(styleName))) {
|
||||
newEditorState = EditorState.setInlineStyleOverride(
|
||||
newEditorState,
|
||||
newEditorState.getCurrentInlineStyle().filter(styleName => !styleIsComment(styleName)) as DraftInlineStyle
|
||||
newEditorState
|
||||
.getCurrentInlineStyle()
|
||||
.filter(
|
||||
(styleName) => !styleIsComment(styleName),
|
||||
) as DraftInlineStyle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -682,89 +747,101 @@ function CommentableEditor({
|
|||
editorState={editorState}
|
||||
controls={enabled ? controls.concat([CommentControl]) : controls}
|
||||
inlineStyles={inlineStyles.concat(commentStyles)}
|
||||
plugins={plugins.concat([{
|
||||
decorators: [{
|
||||
strategy: (
|
||||
block: ContentBlock, callback: (start: number, end: number) => void
|
||||
) => findCommentStyleRanges(block, callback),
|
||||
component: CommentDecorator,
|
||||
}],
|
||||
keyBindingFn: (e: React.KeyboardEvent) => {
|
||||
if (isCommentShortcut(e)) {
|
||||
return 'comment';
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
onRightArrow: (_: React.KeyboardEvent, { getEditorState }) => {
|
||||
// In later versions of draft-js, this is deprecated and can be handled via handleKeyCommand instead
|
||||
// when draftail upgrades, this logic can be moved there
|
||||
|
||||
handleArrowAtContentEnd(getEditorState(), setEditorState, 'LTR');
|
||||
},
|
||||
onLeftArrow: (_: React.KeyboardEvent, { getEditorState }) => {
|
||||
// In later versions of draft-js, this is deprecated and can be handled via handleKeyCommand instead
|
||||
// when draftail upgrades, this logic can be moved there
|
||||
|
||||
handleArrowAtContentEnd(getEditorState(), setEditorState, 'RTL');
|
||||
},
|
||||
handleKeyCommand: (command: string, state: EditorState) => {
|
||||
if (enabled && command === 'comment') {
|
||||
const selection = state.getSelection();
|
||||
const content = state.getCurrentContent();
|
||||
if (selection.isCollapsed()) {
|
||||
// We might be trying to focus an existing comment - check if we're in a comment range
|
||||
const id = findLeastCommonCommentId(
|
||||
content.getBlockForKey(selection.getAnchorKey()),
|
||||
selection.getAnchorOffset()
|
||||
);
|
||||
if (id) {
|
||||
// Focus the comment
|
||||
commentApp.store.dispatch(
|
||||
commentApp.actions.setFocusedComment(id, { updatePinnedComment: true, forceFocus: true })
|
||||
);
|
||||
return 'handled';
|
||||
}
|
||||
plugins={plugins.concat([
|
||||
{
|
||||
decorators: [
|
||||
{
|
||||
strategy: (
|
||||
block: ContentBlock,
|
||||
callback: (start: number, end: number) => void,
|
||||
) => findCommentStyleRanges(block, callback),
|
||||
component: CommentDecorator,
|
||||
},
|
||||
],
|
||||
keyBindingFn: (e: React.KeyboardEvent) => {
|
||||
if (isCommentShortcut(e)) {
|
||||
return 'comment';
|
||||
}
|
||||
// Otherwise, add a new comment
|
||||
setEditorState(addNewComment(state, fieldNode, commentApp, contentPath));
|
||||
return 'handled';
|
||||
}
|
||||
return 'not-handled';
|
||||
},
|
||||
customStyleFn: (styleSet: DraftInlineStyle) => {
|
||||
if (!enabled) {
|
||||
return undefined;
|
||||
}
|
||||
// Use of casting in this function is due to issue #1563 in immutable-js, which causes operations like
|
||||
// map and filter to lose type information on the results. It should be fixed in v4: when we upgrade,
|
||||
// this casting should be removed
|
||||
const localCommentStyles = styleSet.filter(styleIsComment) as Immutable.OrderedSet<string>;
|
||||
const numStyles = localCommentStyles.count();
|
||||
if (numStyles > 0) {
|
||||
// There is at least one comment in the range
|
||||
const commentIds = localCommentStyles.map(
|
||||
style => getIdForCommentStyle(style as string)
|
||||
) as unknown as Immutable.OrderedSet<number>;
|
||||
let background = standardHighlight;
|
||||
if (focusedId && commentIds.has(focusedId)) {
|
||||
// Use the focused colour if one of the comments is focused
|
||||
background = focusedHighlight;
|
||||
},
|
||||
onRightArrow: (_: React.KeyboardEvent, { getEditorState }) => {
|
||||
// In later versions of draft-js, this is deprecated and can be handled via handleKeyCommand instead
|
||||
// when draftail upgrades, this logic can be moved there
|
||||
|
||||
handleArrowAtContentEnd(getEditorState(), setEditorState, 'LTR');
|
||||
},
|
||||
onLeftArrow: (_: React.KeyboardEvent, { getEditorState }) => {
|
||||
// In later versions of draft-js, this is deprecated and can be handled via handleKeyCommand instead
|
||||
// when draftail upgrades, this logic can be moved there
|
||||
|
||||
handleArrowAtContentEnd(getEditorState(), setEditorState, 'RTL');
|
||||
},
|
||||
handleKeyCommand: (command: string, state: EditorState) => {
|
||||
if (enabled && command === 'comment') {
|
||||
const selection = state.getSelection();
|
||||
const content = state.getCurrentContent();
|
||||
if (selection.isCollapsed()) {
|
||||
// We might be trying to focus an existing comment - check if we're in a comment range
|
||||
const id = findLeastCommonCommentId(
|
||||
content.getBlockForKey(selection.getAnchorKey()),
|
||||
selection.getAnchorOffset(),
|
||||
);
|
||||
if (id) {
|
||||
// Focus the comment
|
||||
commentApp.store.dispatch(
|
||||
commentApp.actions.setFocusedComment(id, {
|
||||
updatePinnedComment: true,
|
||||
forceFocus: true,
|
||||
}),
|
||||
);
|
||||
return 'handled';
|
||||
}
|
||||
}
|
||||
// Otherwise, add a new comment
|
||||
setEditorState(
|
||||
addNewComment(state, fieldNode, commentApp, contentPath),
|
||||
);
|
||||
return 'handled';
|
||||
}
|
||||
return 'not-handled';
|
||||
},
|
||||
customStyleFn: (styleSet: DraftInlineStyle) => {
|
||||
if (!enabled) {
|
||||
return undefined;
|
||||
}
|
||||
// Use of casting in this function is due to issue #1563 in immutable-js, which causes operations like
|
||||
// map and filter to lose type information on the results. It should be fixed in v4: when we upgrade,
|
||||
// this casting should be removed
|
||||
const localCommentStyles = styleSet.filter(
|
||||
styleIsComment,
|
||||
) as Immutable.OrderedSet<string>;
|
||||
const numStyles = localCommentStyles.count();
|
||||
if (numStyles > 0) {
|
||||
// There is at least one comment in the range
|
||||
const commentIds = localCommentStyles.map((style) =>
|
||||
getIdForCommentStyle(style as string),
|
||||
) as unknown as Immutable.OrderedSet<number>;
|
||||
let background = standardHighlight;
|
||||
if (focusedId && commentIds.has(focusedId)) {
|
||||
// Use the focused colour if one of the comments is focused
|
||||
background = focusedHighlight;
|
||||
return {
|
||||
'background-color': background,
|
||||
'color': standardHighlight,
|
||||
};
|
||||
} else if (numStyles > 1) {
|
||||
// Otherwise if we're in a region with overlapping comments, use a slightly darker colour than usual
|
||||
// to indicate that
|
||||
background = overlappingHighlight;
|
||||
}
|
||||
return {
|
||||
'background-color': background,
|
||||
'color': standardHighlight
|
||||
};
|
||||
} else if (numStyles > 1) {
|
||||
// Otherwise if we're in a region with overlapping comments, use a slightly darker colour than usual
|
||||
// to indicate that
|
||||
background = overlappingHighlight;
|
||||
}
|
||||
return {
|
||||
'background-color': background
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}])}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
])}
|
||||
{...options}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
/**
|
||||
* Returns collection of currently selected blocks.
|
||||
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L19.
|
||||
*/
|
||||
* Returns collection of currently selected blocks.
|
||||
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L19.
|
||||
*/
|
||||
const getSelectedBlocksList = (editorState) => {
|
||||
const selectionState = editorState.getSelection();
|
||||
const content = editorState.getCurrentContent();
|
||||
const startKey = selectionState.getStartKey();
|
||||
const endKey = selectionState.getEndKey();
|
||||
const blockMap = content.getBlockMap();
|
||||
const blocks = blockMap
|
||||
const blocks = blockMap
|
||||
.toSeq()
|
||||
.skipUntil((_, k) => k === startKey)
|
||||
.takeUntil((_, k) => k === endKey)
|
||||
|
@ -18,9 +17,9 @@ const getSelectedBlocksList = (editorState) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Returns the currently selected text in the editor.
|
||||
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L106.
|
||||
*/
|
||||
* Returns the currently selected text in the editor.
|
||||
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L106.
|
||||
*/
|
||||
export const getSelectionText = (editorState) => {
|
||||
const selection = editorState.getSelection();
|
||||
let start = selection.getAnchorOffset();
|
||||
|
@ -36,7 +35,10 @@ export const getSelectionText = (editorState) => {
|
|||
let selectedText = '';
|
||||
for (let i = 0; i < selectedBlocks.size; i += 1) {
|
||||
const blockStart = i === 0 ? start : 0;
|
||||
const blockEnd = i === (selectedBlocks.size - 1) ? end : selectedBlocks.get(i).getText().length;
|
||||
const blockEnd =
|
||||
i === selectedBlocks.size - 1
|
||||
? end
|
||||
: selectedBlocks.get(i).getText().length;
|
||||
selectedText += selectedBlocks.get(i).getText().slice(blockStart, blockEnd);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue