reinit new repo
This commit is contained in:
commit
fc28e8bae8
280 changed files with 484202 additions and 0 deletions
52
.eslintrc.js
Normal file
52
.eslintrc.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
ignorePatterns: ['node_modules', 'docs/**'],
|
||||
extends: ['standard'],
|
||||
plugins: ['svelte3', '@typescript-eslint'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
extraFileExtensions: ['.svelte'],
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2022: true,
|
||||
},
|
||||
settings: {
|
||||
'svelte3/ignore-styles': () => true,
|
||||
'svelte3/typescript': require('typescript'),
|
||||
},
|
||||
rules: {
|
||||
indent: ['error', 2],
|
||||
semi: ['error', 'always'],
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
{
|
||||
arrays: 'always-multiline',
|
||||
objects: 'always-multiline',
|
||||
imports: 'never',
|
||||
exports: 'never',
|
||||
functions: 'never',
|
||||
},
|
||||
],
|
||||
'operator-linebreak': ['error', 'after'],
|
||||
'space-before-function-paren': ['error', 'never'],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
processor: 'svelte3/svelte3',
|
||||
rules: {
|
||||
'no-multiple-empty-lines': ['error', { max: 2, maxBOF: 2 }],
|
||||
'import/first': 'off',
|
||||
'import/no-duplicates': 'off',
|
||||
'import/no-mutable-exports': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
indent: ['error', 2],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* @hobbes7878 @fcage @MinamiFunakoshiTR
|
||||
7
.github/COMMIT_ERROR_ISSUE_TEMPLATE.md
vendored
Normal file
7
.github/COMMIT_ERROR_ISSUE_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
title: Build error on commit
|
||||
assignees: hobbes7878, MinamiFunakoshiTR
|
||||
labels: bug
|
||||
---
|
||||
|
||||
A commit caused docs to fail to build: {{ sha }}
|
||||
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
### What's in this pull request
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New component/feature
|
||||
- [ ] Documentation update
|
||||
- [ ] Other
|
||||
|
||||
### Description
|
||||
|
||||
Tell us what this PR does or link to any related issues that describe the goal here.
|
||||
|
||||
### Before submitting, please check that you've
|
||||
|
||||
- [ ] Read our [contributing guide](https://github.com/reuters-graphics/graphics-svelte-components/blob/master/CONTRIBUTING.md) at some point
|
||||
- [ ] Formatted you code correctly (i.e., prettier cleaned it up)
|
||||
- [ ] Documented any new components or features
|
||||
- [ ] Tagged an editor to review
|
||||
41
.github/docs.yaml
vendored
Normal file
41
.github/docs.yaml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
name: Build docs site
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build-app:
|
||||
name: Build site
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TESTING: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16.7.0'
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Config git
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
- name: Build docs
|
||||
run: yarn build:docs
|
||||
- name: Create issue on fail
|
||||
uses: JasonEtco/create-an-issue@v2
|
||||
if: ${{ failure() }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
filename: .github/COMMIT_ERROR_ISSUE_TEMPLATE.md
|
||||
- name: Commit docs
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
git add .
|
||||
git commit -m "build docs"
|
||||
git push origin
|
||||
182
.gitignore
vendored
Normal file
182
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,linux
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,linux
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
.env*.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
storybook-static
|
||||
|
||||
# rollup.js default build output
|
||||
dist/
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,macos,linux
|
||||
|
||||
.svelte/
|
||||
.svelte-kit/
|
||||
dist/
|
||||
graphics-pack/
|
||||
!dist/package.json
|
||||
project-files
|
||||
5
.markdownlint.jsonc
Normal file
5
.markdownlint.jsonc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MD013": false,
|
||||
"MD033": false,
|
||||
"MD041": false
|
||||
}
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
engine-strict=true
|
||||
15
.prettierrc
Normal file
15
.prettierrc
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"svelteStrictMode": true,
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"endOfLine": "lf",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
4
.storybook/ArticleWrapper.svelte
Normal file
4
.storybook/ArticleWrapper.svelte
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<!-- Wrapper around all stories -->
|
||||
<article class="container-fluid">
|
||||
<slot />
|
||||
</article>
|
||||
9
.storybook/Theme.js
Normal file
9
.storybook/Theme.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { create } from '@storybook/theming';
|
||||
|
||||
export default create({
|
||||
base: 'light',
|
||||
brandTitle: 'Graphics svelte components',
|
||||
brandUrl: 'https://reuters-graphics.github.io/graphics-svelte-components/',
|
||||
brandImage: 'https://graphics.thomsonreuters.com/style-assets/images/logos/reuters-graphics-logo/svg/graphics-logo-color-dark.svg',
|
||||
brandTarget: '_self',
|
||||
});
|
||||
41
.storybook/main.cjs
Normal file
41
.storybook/main.cjs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
const { mergeConfig } = require('vite');
|
||||
const preprocess = require('../bin/preprocess/index.cjs');
|
||||
|
||||
|
||||
module.exports = {
|
||||
"stories": [
|
||||
"../src/**/*.stories.mdx",
|
||||
"../src/**/*.stories.svelte"
|
||||
],
|
||||
"addons": [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
],
|
||||
"framework": "@storybook/svelte",
|
||||
"core": {
|
||||
"builder": "@storybook/builder-vite"
|
||||
},
|
||||
"features": {
|
||||
"storyStoreV7": false,
|
||||
"previewMdx2": true,
|
||||
},
|
||||
async viteFinal(config, { configType }) {
|
||||
return mergeConfig(config, {
|
||||
base: configType === 'PRODUCTION' ? 'https://reuters-graphics.github.io/graphics-svelte-components/' : '/',
|
||||
css: {
|
||||
preprocessorOptions: { scss: preprocess.scss },
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@reuters-graphics/svelte-charts': './src',
|
||||
'$lib': './src',
|
||||
'$docs': './src/docs',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
svelteOptions: {
|
||||
preprocess: preprocess.sveltePreprocess,
|
||||
},
|
||||
}
|
||||
26
.storybook/manager.js
Normal file
26
.storybook/manager.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { addons } from '@storybook/addons';
|
||||
import theme from './Theme';
|
||||
|
||||
addons.setConfig({
|
||||
isFullscreen: false,
|
||||
showNav: true,
|
||||
showPanel: true,
|
||||
panelPosition: 'bottom',
|
||||
enableShortcuts: true,
|
||||
showToolbar: true,
|
||||
theme: undefined,
|
||||
selectedPanel: undefined,
|
||||
initialActive: 'sidebar',
|
||||
sidebar: {
|
||||
showRoots: false,
|
||||
collapsedRoots: ['other'],
|
||||
},
|
||||
toolbar: {
|
||||
title: { hidden: false },
|
||||
zoom: { hidden: false },
|
||||
eject: { hidden: false },
|
||||
copy: { hidden: false },
|
||||
fullscreen: { hidden: false },
|
||||
},
|
||||
theme,
|
||||
});
|
||||
3
.storybook/preview-head.html
Normal file
3
.storybook/preview-head.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<script>
|
||||
window.global = window;
|
||||
</script>
|
||||
58
.storybook/preview.js
Normal file
58
.storybook/preview.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import '../src/scss/main.scss';
|
||||
import './preview.scss';
|
||||
|
||||
import Article from '../src/components/Article/Article.svelte';
|
||||
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import scss from 'react-syntax-highlighter/dist/esm/languages/prism/scss';
|
||||
import svelte from './svelte-highlighting.js';
|
||||
|
||||
SyntaxHighlighter.registerLanguage('scss', scss);
|
||||
SyntaxHighlighter.registerLanguage('svelte', svelte);
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
viewMode: 'docs',
|
||||
previewTabs: { 'storybook/docs/panel': { index: -1 } },
|
||||
controls: {
|
||||
expanded: true,
|
||||
sort: 'requiredFirst',
|
||||
matchers: {
|
||||
color: /(background|colour|Colour)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
layout: 'fullscreen',
|
||||
options: {
|
||||
// https://storybook.js.org/docs/svelte/writing-stories/naming-components-and-hierarchy#sorting-stories
|
||||
storySort: {
|
||||
includeNames: true,
|
||||
order: [
|
||||
'Intro',
|
||||
'Guides',
|
||||
[
|
||||
'Using these docs',
|
||||
'Using with the Graphics Kit',
|
||||
'Using with Google docs',
|
||||
'Customising components with SCSS',
|
||||
'*',
|
||||
],
|
||||
'*',
|
||||
'Actions',
|
||||
['*'],
|
||||
'Utilities',
|
||||
['*'],
|
||||
'Contributing',
|
||||
[
|
||||
'Quickstart', 'Component Basics', '*', 'Writing Stories',
|
||||
'Recipes: Basic story',
|
||||
'Recipes: Story with custom docs',
|
||||
'Recipes: Story with custom controls',
|
||||
'Recipes: Story with media',
|
||||
'Recipes: Story for a component with slots'
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export const decorators = [() => ({ Component: Article })];
|
||||
29
.storybook/preview.scss
Normal file
29
.storybook/preview.scss
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
table.docblock-argstable {
|
||||
p {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
div.sbdocs-content {
|
||||
& > h2, & > div > div > h2 {
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 2rem;
|
||||
&:first-of-type {
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.docblock-source {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
& > blockquote, & > div > div > blockquote, blockquote.sbdocs {
|
||||
background-color: #ededed;
|
||||
padding: 15px 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
147
.storybook/svelte-highlighting.js
Normal file
147
.storybook/svelte-highlighting.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
svelte.displayName = 'svelte'
|
||||
svelte.aliases = []
|
||||
|
||||
export default function svelte(Prism) {
|
||||
const blocks = '(if|else if|await|then|catch|each|html|debug)';
|
||||
|
||||
Prism.languages.svelte = Prism.languages.extend('markup', {
|
||||
each: {
|
||||
pattern: new RegExp(
|
||||
'{[#/]each' +
|
||||
'(?:(?:\\{(?:(?:\\{(?:[^{}])*\\})|(?:[^{}]))*\\})|(?:[^{}]))*}'
|
||||
),
|
||||
inside: {
|
||||
'language-javascript': [
|
||||
{
|
||||
pattern: /(as[\s\S]*)\([\s\S]*\)(?=\s*\})/,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
{
|
||||
pattern: /(as[\s]*)[\s\S]*(?=\s*)/,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
{
|
||||
pattern: /(#each[\s]*)[\s\S]*(?=as)/,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
],
|
||||
keyword: /[#/]each|as/,
|
||||
punctuation: /{|}/,
|
||||
},
|
||||
},
|
||||
block: {
|
||||
pattern: new RegExp(
|
||||
'{[#:/@]/s' +
|
||||
blocks +
|
||||
'(?:(?:\\{(?:(?:\\{(?:[^{}])*\\})|(?:[^{}]))*\\})|(?:[^{}]))*}'
|
||||
),
|
||||
inside: {
|
||||
punctuation: /^{|}$/,
|
||||
keyword: [new RegExp('[#:/@]' + blocks + '( )*'), /as/, /then/],
|
||||
'language-javascript': {
|
||||
pattern: /[\s\S]*/,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
},
|
||||
},
|
||||
tag: {
|
||||
pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?:"[^"]*"|'[^']*'|{[\s\S]+?}(?=[\s/>])))|(?=[\s/>])))+)?\s*\/?>/i,
|
||||
greedy: true,
|
||||
inside: {
|
||||
tag: {
|
||||
pattern: /^<\/?[^\s>\/]+/i,
|
||||
inside: {
|
||||
punctuation: /^<\/?/,
|
||||
namespace: /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
'language-javascript': {
|
||||
pattern: /\{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}/,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
'attr-value': {
|
||||
pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,
|
||||
inside: {
|
||||
punctuation: [
|
||||
/^=/,
|
||||
{
|
||||
pattern: /^(\s*)["']|["']$/,
|
||||
lookbehind: true,
|
||||
},
|
||||
],
|
||||
'language-javascript': {
|
||||
pattern: /{[\s\S]+}/,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
},
|
||||
},
|
||||
punctuation: /\/?>/,
|
||||
'attr-name': {
|
||||
pattern: /[^\s>\/]+/,
|
||||
inside: {
|
||||
namespace: /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'language-javascript': {
|
||||
pattern: /\{(?:(?:\{(?:(?:\{(?:[^{}])*\})|(?:[^{}]))*\})|(?:[^{}]))*\}/,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages['javascript'],
|
||||
},
|
||||
});
|
||||
|
||||
Prism.languages.svelte['tag'].inside['attr-value'].inside['entity'] =
|
||||
Prism.languages.svelte['entity'];
|
||||
|
||||
Prism.hooks.add('wrap', env => {
|
||||
if (env.type === 'entity') {
|
||||
env.attributes['title'] = env.content.replace(/&/, '&');
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(Prism.languages.svelte.tag, 'addInlined', {
|
||||
value: function addInlined(tagName, lang) {
|
||||
const includedCdataInside = {};
|
||||
includedCdataInside['language-' + lang] = {
|
||||
pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages[lang],
|
||||
};
|
||||
includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i;
|
||||
|
||||
const inside = {
|
||||
'included-cdata': {
|
||||
pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
|
||||
inside: includedCdataInside,
|
||||
},
|
||||
};
|
||||
inside['language-' + lang] = {
|
||||
pattern: /[\s\S]+/,
|
||||
inside: Prism.languages[lang],
|
||||
};
|
||||
|
||||
const def = {};
|
||||
def[tagName] = {
|
||||
pattern: RegExp(
|
||||
/(<__[\s\S]*?>)(?:<!\[CDATA\[[\s\S]*?\]\]>\s*|[\s\S])*?(?=<\/__>)/.source.replace(
|
||||
/__/g,
|
||||
tagName
|
||||
),
|
||||
'i'
|
||||
),
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
inside,
|
||||
};
|
||||
|
||||
Prism.languages.insertBefore('svelte', 'cdata', def);
|
||||
},
|
||||
});
|
||||
|
||||
Prism.languages.svelte.tag.addInlined('style', 'css');
|
||||
Prism.languages.svelte.tag.addInlined('script', 'javascript');
|
||||
}
|
||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["silvenon.mdx"]
|
||||
}
|
||||
20
.vscode/settings.json
vendored
Normal file
20
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"i18n-ally.localesPaths": [
|
||||
"locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"svelte"
|
||||
],
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"files.associations": {
|
||||
"*.svx": "mdx"
|
||||
},
|
||||
"[mdx]": {
|
||||
"editor.wordWrap": "on"
|
||||
}
|
||||
}
|
||||
43
.vscode/svelte.scripts.code-snippets
vendored
Normal file
43
.vscode/svelte.scripts.code-snippets
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"Static assets path": {
|
||||
"scope": "javascript",
|
||||
"prefix": "static",
|
||||
"body": [
|
||||
"import { getPath } from '$utils/statics';",
|
||||
"",
|
||||
"getPath('${1:filepath}')"
|
||||
],
|
||||
"description": "Add a static assets file path"
|
||||
},
|
||||
"SvelteKit $app/env": {
|
||||
"scope": "javascript",
|
||||
"prefix": "env",
|
||||
"body": [
|
||||
"import { browser, dev, prerendering } from '$app/env';"
|
||||
],
|
||||
"description": "SvelteKit $app/env stores"
|
||||
},
|
||||
"Fetch script module": {
|
||||
"scope": "svelte",
|
||||
"prefix": "fetch-module",
|
||||
"body": [
|
||||
"<script context=\"module\">",
|
||||
" export async function load({ page, fetch, session, context }) {",
|
||||
" const url = 'https://$1';",
|
||||
" const res = await fetch(url);",
|
||||
" if (res.ok) {",
|
||||
" return {",
|
||||
" props: {",
|
||||
" data: await res.json(),",
|
||||
" },",
|
||||
" };",
|
||||
" }",
|
||||
" return {",
|
||||
" status: res.status,",
|
||||
" error: new Error('Could not fetch!'),",
|
||||
" };",
|
||||
" }",
|
||||
"</script>"
|
||||
]
|
||||
}
|
||||
}
|
||||
49
.vscode/svelte.styles.code-snippets
vendored
Normal file
49
.vscode/svelte.styles.code-snippets
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"Svelte SCSS style": {
|
||||
"scope": "svelte",
|
||||
"prefix": "scss",
|
||||
"body": [
|
||||
"<style lang=\"scss\">",
|
||||
"$1",
|
||||
"</style>"
|
||||
],
|
||||
"description": "Add a Svelte SCSS style tag"
|
||||
},
|
||||
"Reuters Graphics styles theme": {
|
||||
"scope": "svelte",
|
||||
"prefix": "theme",
|
||||
"body": [
|
||||
"<style lang=\"scss\">",
|
||||
" @import '~@reuters-graphics/style-main/scss/fonts/font-faces';",
|
||||
" :global {",
|
||||
" @import '@reuters-graphics/style-theme-eisbaer/scss/main';",
|
||||
" }",
|
||||
"</style>"
|
||||
],
|
||||
"description": "Add Reuters Graphics theme to page"
|
||||
},
|
||||
"Reuters Graphics fonts mixins": {
|
||||
"scope": "scss",
|
||||
"prefix": "fonts-mixins",
|
||||
"body": [
|
||||
"@import \"~@reuters-graphics/style-main/scss/fonts/mixins\";",
|
||||
"",
|
||||
"$1 {",
|
||||
" @include font-sans;",
|
||||
"}"
|
||||
],
|
||||
"description": "Add Reuters Graphics fonts mixins"
|
||||
},
|
||||
"Reuters Graphics fonts variables": {
|
||||
"scope": "scss",
|
||||
"prefix": "fonts-variables",
|
||||
"body": [
|
||||
"@import \"~@reuters-graphics/style-main/scss/fonts/variables\";",
|
||||
"",
|
||||
"$1 {",
|
||||
" font-family: $font-family-sans-serif;",
|
||||
"}"
|
||||
],
|
||||
"description": "Add Reuters Graphics fonts variables"
|
||||
}
|
||||
}
|
||||
50
CONTRIBUTING.md
Normal file
50
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||

|
||||
|
||||
# Contributing Guide
|
||||
|
||||
## Why this?
|
||||
|
||||
Most Reuters Graphics repos don't include (or need) contributing guidelines. The ones that do represent critical infrastructure. They also are the ones we most want wider contributions from the team to improve and maintain.
|
||||
|
||||
This doc provides for a few simple guidelines to make sure changes are well considered and represent the best ideas for how to move our tools forward while opening up the opportunity for others to ship their next great idea.
|
||||
|
||||
## Who can contribute?
|
||||
|
||||
Contributions are always welcome from members of the Reuters Graphics team.
|
||||
|
||||
Anyone outside our team using these components is welcome to submit PRs or issues, **BUT** if they are designed solely to benefit a use case that isn't ours, they likely won't be merged.
|
||||
|
||||
## How should Reuters Graphics staff contribute?
|
||||
|
||||
### 🏷️ Make an issue
|
||||
|
||||
We recommend your first step is to create an issue on this repo describing what is missing, broken or could be added or improved. (We'll close that issue when we merge your PR.)
|
||||
|
||||
- It's helpful if that issue describes what changes you propose to make at a high level so we can agree on a general direction before you write code. That's especially true if code you're writing will change how others need to write theirs.
|
||||
- If needed, provide any links to best practice guidelines that support the change you want to make.
|
||||
- Tag others on the team who may have expertise or would contribute to any needed discussion.
|
||||
- **Always tag an editor.**
|
||||
|
||||
### 🧹 Follow code standards
|
||||
|
||||
Once you're ready to submit code, be sure it's properly formatted _before_ you ask for a review. The easiest way is to ensure the built-in code formatter (prettier) is working. (It should.)
|
||||
|
||||
Be sure to add comments around any tricky bits of logic you're adding. Even better, document your code using [JSDoc](https://devhints.io/jsdoc) comments. (Check out [JSDoc shortcuts](https://code.visualstudio.com/docs/languages/javascript#_jsdoc-support) in VS Code for a leg-up.)
|
||||
|
||||
### 📝 Write docs
|
||||
|
||||
All new components and component features should be reflected in a [docs page](https://reuters-graphics.github.io/graphics-svelte-components/) included with your PR. See the [README](https://github.com/reuters-graphics/graphics-svelte-components#developing-new-components) for instructions on how to add those docs.
|
||||
|
||||
### 🍺 Submit code
|
||||
|
||||
All code contributions should be made through the normal [GitHub Flow](https://www.w3schools.com/git/git_github_flow.asp#:~:text=The%20GitHub%20flow%20is%20a,Make%20changes%20and%20add%20Commits). Basically, make a branch and submit a pull request.
|
||||
|
||||
Generally, it's better to avoid bundling several new features or components in a single PR. Breaking them apart into smaller, individual contributions makes them easier to review and manage.
|
||||
|
||||
Once you've submitted your PR, tag an editor to review it.
|
||||
|
||||
An editor will approve your PR after addressing any issues they see. Once an editor approves and there are no code conflicts, you can merge your PR into master.
|
||||
|
||||
### ✉️ Publishing to the team
|
||||
|
||||
For now, only editors should publish new versions of the library to npm. We'll follow [semantic versioning](https://semver.org/) conventions. Most MINOR and all MAJOR version changes should be identified ahead of time during PR review.
|
||||
5
README.md
Normal file
5
README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||

|
||||
|
||||
# ⚙️ graphics-components
|
||||
|
||||
Home of the next generation.
|
||||
3
assetinfo.json
Normal file
3
assetinfo.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"assetid": ["205245"]
|
||||
}
|
||||
79
bin/buildPackage/index.js
Normal file
79
bin/buildPackage/index.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import { DIST, LIB, PACKAGE, TYPES } from './locations.js';
|
||||
|
||||
import { createRequire } from 'module';
|
||||
import { emitDts } from 'svelte2tsx';
|
||||
import fs from 'fs-extra';
|
||||
import glob from 'tiny-glob';
|
||||
import path from 'path';
|
||||
import picomatch from 'picomatch';
|
||||
import processOther from './process/other.js';
|
||||
import processSvelte from './process/svelte.js';
|
||||
import processTypescript from './process/typescript.js';
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const excludePatterns = [
|
||||
'**/stories/**/*',
|
||||
'**/docs/**/*',
|
||||
'**/statics/**/*',
|
||||
'**/*.exclude.*',
|
||||
'**/*.stories.svelte',
|
||||
'**/*.stories.svelte.d.ts',
|
||||
];
|
||||
|
||||
const excludedTypeDefs = [
|
||||
'**/stories/**/*',
|
||||
'**/docs/**/*',
|
||||
'**/*.stories.svelte.d.ts',
|
||||
];
|
||||
|
||||
/**
|
||||
* This is a basic port of sveltekit's own packaging method:
|
||||
* https://github.com/sveltejs/kit/tree/master/packages/kit/src/packaging
|
||||
*/
|
||||
const build = async () => {
|
||||
console.log('📦 Building your package');
|
||||
if (fs.existsSync(DIST)) rimraf.sync(DIST);
|
||||
|
||||
// Extract types
|
||||
await emitDts({
|
||||
libRoot: LIB,
|
||||
svelteShimsPath: require.resolve('svelte2tsx/svelte-shims.d.ts'),
|
||||
declarationDir: TYPES,
|
||||
});
|
||||
|
||||
// Cleanup unwanted types
|
||||
fs.rmSync(path.join(TYPES, 'docs'), { recursive: true, force: true });
|
||||
const types = await glob('**/*', { cwd: TYPES, filesOnly: true });
|
||||
for (const t of types) {
|
||||
if(picomatch.isMatch(t, excludedTypeDefs)) fs.unlinkSync(path.join(TYPES, t));
|
||||
}
|
||||
|
||||
const pkgExports = {
|
||||
'./package.json': './package.json'
|
||||
};
|
||||
|
||||
const files = await glob('**/*.{js,json,ts,svelte,css,scss}', { cwd: LIB, filesOnly: true });
|
||||
for (const file of files) {
|
||||
if(picomatch.isMatch(file, excludePatterns)) continue;
|
||||
if (file.endsWith('.svelte')) {
|
||||
await processSvelte(file);
|
||||
} else if(file.endsWith('.ts') && !file.endsWith('.d.ts')) {
|
||||
await processTypescript(file);
|
||||
} else {
|
||||
await processOther(file);
|
||||
}
|
||||
if (file === 'index.js') continue; // Always add root index last to exports...
|
||||
pkgExports[`./${file.replace(/\/index\.js$|(\/[^/]+)\.js$/, '$1')}`] = `./dist/${file}`;
|
||||
}
|
||||
pkgExports['.'] = './dist/index.js';
|
||||
const pkg = fs.readJSONSync(PACKAGE);
|
||||
pkg.type = 'module';
|
||||
pkg.files = ['dist'];
|
||||
pkg.private = false;
|
||||
pkg.exports = pkgExports;
|
||||
fs.writeFileSync(PACKAGE, JSON.stringify(pkg, null, 2));
|
||||
}
|
||||
|
||||
build();
|
||||
8
bin/buildPackage/locations.js
Normal file
8
bin/buildPackage/locations.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import path from 'path';
|
||||
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
export const ROOT = path.resolve(__dirname, '../../');
|
||||
export const PACKAGE = path.join(ROOT, 'package.json');
|
||||
export const LIB = path.join(ROOT, 'src');
|
||||
export const DIST = path.join(ROOT, 'dist');
|
||||
export const TYPES = path.join(DIST, '@types');
|
||||
11
bin/buildPackage/process/other.js
Normal file
11
bin/buildPackage/process/other.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { DIST, LIB } from './../locations.js';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
export default async (file) => {
|
||||
const filename = path.join(LIB, file);
|
||||
const writePath = path.join(DIST, file);
|
||||
fs.ensureDirSync(path.dirname(writePath));
|
||||
fs.copyFileSync(filename, writePath);
|
||||
}
|
||||
20
bin/buildPackage/process/svelte.js
Normal file
20
bin/buildPackage/process/svelte.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { DIST, LIB } from './../locations.js';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import preprocess from '../../preprocess/index.cjs';
|
||||
import { preprocess as svelte } from 'svelte/compiler';
|
||||
|
||||
const stripLangTags = (source) =>
|
||||
source
|
||||
.replace(/(<!--[^]*?-->)|(<script[^>]*?)\s(?:type|lang)=(["']).*?\3/g, '$1$2')
|
||||
.replace(/(<!--[^]*?-->)|(<style[^>]*?)\s(?:type|lang)=(["']).*?\3/g, '$1$2');
|
||||
|
||||
export default async (file) => {
|
||||
const filename = path.join(LIB, file);
|
||||
let source = fs.readFileSync(filename, 'utf8');
|
||||
source = (await svelte(source, preprocess.sveltePreprocess, { filename })).code
|
||||
const writePath = path.join(DIST, file);
|
||||
fs.ensureDirSync(path.dirname(writePath));
|
||||
fs.writeFileSync(writePath, stripLangTags(source));
|
||||
}
|
||||
22
bin/buildPackage/process/typescript.js
Normal file
22
bin/buildPackage/process/typescript.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { DIST, LIB, ROOT } from './../locations.js';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import ts from 'typescript';
|
||||
|
||||
async function transpileTypeScript(filename, source) {
|
||||
const { compilerOptions } = fs.readJSONSync(path.join(ROOT, 'tsconfig.json'));
|
||||
return ts.transpileModule(source, {
|
||||
compilerOptions,
|
||||
fileName: filename
|
||||
}).outputText;
|
||||
}
|
||||
|
||||
export default async (file) => {
|
||||
const filename = path.join(LIB, file);
|
||||
let source = fs.readFileSync(filename, 'utf8');
|
||||
source = await transpileTypeScript(filename, source);
|
||||
const writePath = path.join(DIST, file).replace(/\.ts$/, '.js');
|
||||
fs.ensureDirSync(path.dirname(writePath));
|
||||
fs.writeFileSync(writePath, source);
|
||||
}
|
||||
44
bin/newComponent/index.cjs
Normal file
44
bin/newComponent/index.cjs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
const prompts = require('prompts');
|
||||
const { pascalCase } = require('change-case');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const glob = require('tiny-glob');
|
||||
const { cyan, green, bold } = require('kleur');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '../../');
|
||||
const LIB = path.join(ROOT, 'src/components');
|
||||
const TEMPLATE = path.join(__dirname, 'template');
|
||||
|
||||
const makeNewComponent = async() => {
|
||||
const{ name } = await prompts({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'What should we call your new component, e.g., ImagePack?',
|
||||
});
|
||||
|
||||
if (!name) return;
|
||||
|
||||
const componentName = pascalCase(name);
|
||||
const componentDir = path.join(LIB, componentName);
|
||||
|
||||
if(fs.existsSync(componentDir)) {
|
||||
console.log('Oops! That component already exists. Try another name?');
|
||||
return;
|
||||
}
|
||||
|
||||
fs.mkdirSync(componentDir);
|
||||
|
||||
const files = await glob('**/*', { cwd: TEMPLATE, filesOnly: true });
|
||||
|
||||
for (const file of files) {
|
||||
const content = fs.readFileSync(path.join(TEMPLATE, file), 'utf8');
|
||||
const writeContent = content.replace(/YourComponent/g, componentName);
|
||||
const writePath = path.join(LIB, file.replace(/YourComponent/g, componentName));
|
||||
fs.ensureDirSync(path.dirname(writePath));
|
||||
fs.writeFileSync(writePath, writeContent);
|
||||
}
|
||||
|
||||
console.log(`${green('✔')} ${bold('Your component is ready at:')}\n📁 ${cyan(`src/component/${bold(componentName)}/${componentName}.svelte`)}`);
|
||||
};
|
||||
|
||||
makeNewComponent();
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// Don't lose the "?raw" in markdown imports!
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import YourComponent from './YourComponent.svelte';
|
||||
|
||||
import { withComponentDocs } from '$docs/utils/withParams.js';
|
||||
|
||||
// 🖼️ You can import images you need in stories directly in code!
|
||||
// @ts-ignore
|
||||
import SharkImg from './stories/shark.jpg';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/YourComponent',
|
||||
component: YourComponent,
|
||||
...withComponentDocs(componentDocs),
|
||||
// https://storybook.js.org/docs/svelte/essentials/controls
|
||||
argTypes: {
|
||||
width: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<YourComponent {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Basic"
|
||||
args="{{
|
||||
width: 'normal',
|
||||
src: SharkImg,
|
||||
altText: 'Duh dum! It\'s a shark!!',
|
||||
}}"
|
||||
/>
|
||||
51
bin/newComponent/template/YourComponent/YourComponent.svelte
Normal file
51
bin/newComponent/template/YourComponent/YourComponent.svelte
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<script lang="ts">
|
||||
/** ✏️ DOCUMENT your chart's props using TypeScript and JSDoc comments like below! */
|
||||
|
||||
/**
|
||||
* A source for the image.
|
||||
* @required
|
||||
*/
|
||||
export let src: string;
|
||||
|
||||
/**
|
||||
* AltText for the image.
|
||||
* @required
|
||||
*/
|
||||
export let altText: string;
|
||||
|
||||
/** Height of the image. */
|
||||
export let height: number = 500;
|
||||
|
||||
// You can declare custom types to help users implement your component.
|
||||
type ContainerWidth = 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
|
||||
|
||||
/** Width of the component within the text well. */
|
||||
export let width: ContainerWidth = 'normal';
|
||||
</script>
|
||||
|
||||
<section class="photo {width}">
|
||||
<div
|
||||
style:background-image={`url(${src})`}
|
||||
style:height={`${height}px`}
|
||||
/>
|
||||
<p class="visually-hidden">{altText}</p>
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
section.photo {
|
||||
div {
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
.visually-hidden {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
> **Welcome to your new component!** Use this template to build your component and customise its storybook.
|
||||
|
||||
- Build your component in `YourComponent/YourComponent.svelte`.
|
||||
- Write your component's storybook in `YourComponent/YourComponent.stories.svelte`.
|
||||
- Don't forget to add your component to `src/index.js`:
|
||||
|
||||
```javascript
|
||||
// ...
|
||||
export { default as YourComponent } from './components/YourComponent/YourComponent.svelte';
|
||||
```
|
||||
|
||||
- Commit your component to a new branch and push it to GitHub! 🏁
|
||||
|
||||
---
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { YourComponent } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
|
||||
<YourComponent />
|
||||
```
|
||||
BIN
bin/newComponent/template/YourComponent/stories/shark.jpg
Normal file
BIN
bin/newComponent/template/YourComponent/stories/shark.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 KiB |
29
bin/preprocess/index.cjs
Normal file
29
bin/preprocess/index.cjs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
const autoprefixer = require('autoprefixer');
|
||||
const preprocess = require('svelte-preprocess');
|
||||
|
||||
const scss = {
|
||||
includePaths: ['src/', 'node_modules/bootstrap/scss/'],
|
||||
importer: [
|
||||
(url) => {
|
||||
if (/^\$lib/.test(url)){ return { file: `src/${url.replace('$lib', '')}` }; }
|
||||
// Redirect tilde-prefixed imports to node_modules
|
||||
if (/^~/.test(url)) { return { file: `node_modules/${url.replace('~', '')}` }; }
|
||||
return null;
|
||||
},
|
||||
],
|
||||
quietDeps: true,
|
||||
};
|
||||
|
||||
const sveltePreprocess = preprocess({
|
||||
preserve: ['ld+json'],
|
||||
typescript: true,
|
||||
scss,
|
||||
postcss: {
|
||||
plugins: [autoprefixer],
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
scss,
|
||||
sveltePreprocess
|
||||
}
|
||||
8
lefthook.yml
Normal file
8
lefthook.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
pre-commit:
|
||||
commands:
|
||||
eslint:
|
||||
glob: 'src/**/*.js'
|
||||
run: npx eslint --cache --fix {staged_files}
|
||||
prettier:
|
||||
glob: 'src/**/*.{js,scss,md,svelte}'
|
||||
run: npx prettier --write {staged_files}
|
||||
162
package.json
Normal file
162
package.json
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
{
|
||||
"name": "@reuters-graphics/graphics-svelte-components",
|
||||
"version": "0.3.32",
|
||||
"type": "module",
|
||||
"private": false,
|
||||
"homepage": "https://reuters-graphics.github.io/graphics-svelte-components",
|
||||
"repository": "https://github.com/reuters-graphics/graphics-svelte-components",
|
||||
"scripts": {
|
||||
"start": "start-storybook -p 3000",
|
||||
"new": "node ./bin/newComponent/index.cjs",
|
||||
"build:package": "node ./bin/buildPackage/index.js",
|
||||
"build:docs": "build-storybook -o docs"
|
||||
},
|
||||
"license": "MIT",
|
||||
"types": "dist/@types/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.9",
|
||||
"@babel/eslint-parser": "^7.15.4",
|
||||
"@babel/eslint-plugin": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@evilmartians/lefthook": "^1.0.1",
|
||||
"@reuters-graphics/eslint-config": "^0.0.2",
|
||||
"@storybook/addon-actions": "6.5.9",
|
||||
"@storybook/addon-essentials": "6.5.9",
|
||||
"@storybook/addon-interactions": "6.5.9",
|
||||
"@storybook/addon-links": "6.5.9",
|
||||
"@storybook/addon-svelte-csf": "^2.0.6",
|
||||
"@storybook/builder-vite": "^0.2.1",
|
||||
"@storybook/mdx2-csf": "^0.0.3",
|
||||
"@storybook/svelte": "6.5.9",
|
||||
"@storybook/testing-library": "^0.0.13",
|
||||
"@storybook/theming": "6.5.9",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"@types/react-syntax-highlighter": "^15.5.4",
|
||||
"@typescript-eslint/parser": "^5.32.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"babel-loader": "^8.2.5",
|
||||
"change-case": "^4.1.2",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-storybook": "^0.6.1",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"kleur": "^4.1.5",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"prettier": "^2.3.2",
|
||||
"prettier-plugin-svelte": "^2.4.0",
|
||||
"prompts": "^2.4.2",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sass": "^1.54.0",
|
||||
"svelte": "^3.49.0",
|
||||
"svelte-loader": "^3.1.3",
|
||||
"svelte-preprocess": "^4.10.7",
|
||||
"svelte2tsx": "^0.5.13",
|
||||
"tiny-glob": "^0.2.9",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.15.3",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.3",
|
||||
"@reuters-graphics/style-theme-eisbaer": "^0.3.5",
|
||||
"@sveltejs/svelte-scroller": "^2.0.7",
|
||||
"bootstrap": "^5.2.0",
|
||||
"classnames": "^2.3.1",
|
||||
"dayjs": "^1.11.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lottie-web": "^5.7.13",
|
||||
"marked": "^4.0.8",
|
||||
"proper-url-join": "^2.1.1",
|
||||
"pym.js": "^1.3.2",
|
||||
"svelte-fa": "^2.4.0",
|
||||
"svelte-intersection-observer": "^0.10.0",
|
||||
"ua-parser-js": "^0.7.27"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./components/Ai2svelte/index.svelte": "./dist/components/Ai2svelte/index.svelte",
|
||||
"./components/BeforeAfter/BeforeAfter.svelte": "./dist/components/BeforeAfter/BeforeAfter.svelte",
|
||||
"./components/BodyText/BodyText.svelte": "./dist/components/BodyText/BodyText.svelte",
|
||||
"./components/DatawrapperChart/DatawrapperChart.svelte": "./dist/components/DatawrapperChart/DatawrapperChart.svelte",
|
||||
"./components/EmbedPreviewerLink/EmbedPreviewerLink.svelte": "./dist/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte",
|
||||
"./components/EndNotes/EndNotes.svelte": "./dist/components/EndNotes/EndNotes.svelte",
|
||||
"./components/FeaturePhoto/FeaturePhoto.svelte": "./dist/components/FeaturePhoto/FeaturePhoto.svelte",
|
||||
"./components/Framer/Framer.svelte": "./dist/components/Framer/Framer.svelte",
|
||||
"./components/Framer/Resizer/index.svelte": "./dist/components/Framer/Resizer/index.svelte",
|
||||
"./components/Framer/stores": "./dist/components/Framer/stores.js",
|
||||
"./components/Framer/uniqNames": "./dist/components/Framer/uniqNames.js",
|
||||
"./components/Headline/Headline.svelte": "./dist/components/Headline/Headline.svelte",
|
||||
"./components/Hero/Hero.svelte": "./dist/components/Hero/Hero.svelte",
|
||||
"./components/PymChild/PymChild.svelte": "./dist/components/PymChild/PymChild.svelte",
|
||||
"./components/ReutersLogo/ReutersLogo.svelte": "./dist/components/ReutersLogo/ReutersLogo.svelte",
|
||||
"./components/SEO/analytics": "./dist/components/SEO/analytics.js",
|
||||
"./components/SEO/index.svelte": "./dist/components/SEO/index.svelte",
|
||||
"./components/SEO/publisherTags": "./dist/components/SEO/publisherTags.js",
|
||||
"./components/Scroller/Background.svelte": "./dist/components/Scroller/Background.svelte",
|
||||
"./components/Scroller/Embedded/Background.svelte": "./dist/components/Scroller/Embedded/Background.svelte",
|
||||
"./components/Scroller/Embedded/Foreground.svelte": "./dist/components/Scroller/Embedded/Foreground.svelte",
|
||||
"./components/Scroller/Embedded/index.svelte": "./dist/components/Scroller/Embedded/index.svelte",
|
||||
"./components/Scroller/Foreground.svelte": "./dist/components/Scroller/Foreground.svelte",
|
||||
"./components/Scroller/demos/basic/_Clicker.svelte": "./dist/components/Scroller/demos/basic/_Clicker.svelte",
|
||||
"./components/Scroller/demos/basic/_Stacked.svelte": "./dist/components/Scroller/demos/basic/_Stacked.svelte",
|
||||
"./components/Scroller/demos/basic/_Step.svelte": "./dist/components/Scroller/demos/basic/_Step.svelte",
|
||||
"./components/Scroller/index.svelte": "./dist/components/Scroller/index.svelte",
|
||||
"./components/Sharer/Sharer.svelte": "./dist/components/Sharer/Sharer.svelte",
|
||||
"./components/Sharer/utils/copyToClipboard": "./dist/components/Sharer/utils/copyToClipboard.js",
|
||||
"./components/Sharer/utils/facebook": "./dist/components/Sharer/utils/facebook.js",
|
||||
"./components/Sharer/utils/meta": "./dist/components/Sharer/utils/meta.js",
|
||||
"./components/Sharer/utils/twitter": "./dist/components/Sharer/utils/twitter.js",
|
||||
"./components/SiteFooter/CompanyLinks.svelte": "./dist/components/SiteFooter/CompanyLinks.svelte",
|
||||
"./components/SiteFooter/LegalLinks.svelte": "./dist/components/SiteFooter/LegalLinks.svelte",
|
||||
"./components/SiteFooter/QuickLinks.svelte": "./dist/components/SiteFooter/QuickLinks.svelte",
|
||||
"./components/SiteFooter/Referrals/IntersectionObserver.svelte": "./dist/components/SiteFooter/Referrals/IntersectionObserver.svelte",
|
||||
"./components/SiteFooter/Referrals/Link.svelte": "./dist/components/SiteFooter/Referrals/Link.svelte",
|
||||
"./components/SiteFooter/Referrals/Referrals.svelte": "./dist/components/SiteFooter/Referrals/Referrals.svelte",
|
||||
"./components/SiteFooter/Referrals/index.svelte": "./dist/components/SiteFooter/Referrals/index.svelte",
|
||||
"./components/SiteFooter/SiteFooter.svelte": "./dist/components/SiteFooter/SiteFooter.svelte",
|
||||
"./components/SiteFooter/data.json": "./dist/components/SiteFooter/data.json",
|
||||
"./components/SiteFooter/svgs/Facebook.svelte": "./dist/components/SiteFooter/svgs/Facebook.svelte",
|
||||
"./components/SiteFooter/svgs/Graphics.svelte": "./dist/components/SiteFooter/svgs/Graphics.svelte",
|
||||
"./components/SiteFooter/svgs/Instagram.svelte": "./dist/components/SiteFooter/svgs/Instagram.svelte",
|
||||
"./components/SiteFooter/svgs/LinkedIn.svelte": "./dist/components/SiteFooter/svgs/LinkedIn.svelte",
|
||||
"./components/SiteFooter/svgs/Pictures.svelte": "./dist/components/SiteFooter/svgs/Pictures.svelte",
|
||||
"./components/SiteFooter/svgs/Twitter.svelte": "./dist/components/SiteFooter/svgs/Twitter.svelte",
|
||||
"./components/SiteFooter/svgs/Videos.svelte": "./dist/components/SiteFooter/svgs/Videos.svelte",
|
||||
"./components/SiteFooter/svgs/YouTube.svelte": "./dist/components/SiteFooter/svgs/YouTube.svelte",
|
||||
"./components/SiteHeader/MobileMenu/index.svelte": "./dist/components/SiteHeader/MobileMenu/index.svelte",
|
||||
"./components/SiteHeader/NavBar/DownArrow.svelte": "./dist/components/SiteHeader/NavBar/DownArrow.svelte",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/MoreDropdown.svelte": "./dist/components/SiteHeader/NavBar/NavDropdown/MoreDropdown.svelte",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/SectionDrowdown.svelte": "./dist/components/SiteHeader/NavBar/NavDropdown/SectionDrowdown.svelte",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/Spinner/index.svelte": "./dist/components/SiteHeader/NavBar/NavDropdown/Spinner/index.svelte",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/StoryCard/index.svelte": "./dist/components/SiteHeader/NavBar/NavDropdown/StoryCard/index.svelte",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/StoryCard/time": "./dist/components/SiteHeader/NavBar/NavDropdown/StoryCard/time.js",
|
||||
"./components/SiteHeader/NavBar/NavDropdown/index.svelte": "./dist/components/SiteHeader/NavBar/NavDropdown/index.svelte",
|
||||
"./components/SiteHeader/NavBar/index.svelte": "./dist/components/SiteHeader/NavBar/index.svelte",
|
||||
"./components/SiteHeader/NavBar/utils": "./dist/components/SiteHeader/NavBar/utils/index.js",
|
||||
"./components/SiteHeader/SiteHeader.svelte": "./dist/components/SiteHeader/SiteHeader.svelte",
|
||||
"./components/SiteHeader/data.json": "./dist/components/SiteHeader/data.json",
|
||||
"./components/SiteHeader/scss/_breakpoints.scss": "./dist/components/SiteHeader/scss/_breakpoints.scss",
|
||||
"./components/SiteHeader/scss/_colors.scss": "./dist/components/SiteHeader/scss/_colors.scss",
|
||||
"./components/SiteHeader/scss/_eases.scss": "./dist/components/SiteHeader/scss/_eases.scss",
|
||||
"./components/SiteHeader/scss/_grids.scss": "./dist/components/SiteHeader/scss/_grids.scss",
|
||||
"./components/SiteHeader/scss/_z-indexes.scss": "./dist/components/SiteHeader/scss/_z-indexes.scss",
|
||||
"./components/SiteHeader/svgs/Close.svelte": "./dist/components/SiteHeader/svgs/Close.svelte",
|
||||
"./components/SiteHeader/svgs/Menu.svelte": "./dist/components/SiteHeader/svgs/Menu.svelte",
|
||||
"./components/Spinner/Spinner.svelte": "./dist/components/Spinner/Spinner.svelte",
|
||||
"./components/Video/Controls.svelte": "./dist/components/Video/Controls.svelte",
|
||||
"./components/Video/index.svelte": "./dist/components/Video/index.svelte",
|
||||
"./components/Visible/Visible.svelte": "./dist/components/Visible/Visible.svelte",
|
||||
".": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
42
src/actions/cssVariables/cssVariables.stories.mdx
Normal file
42
src/actions/cssVariables/cssVariables.stories.mdx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Meta } from '@storybook/addon-docs';
|
||||
import { parameters } from '$docs/utils/docsPage.js';
|
||||
|
||||
<Meta title="Actions/cssVariables" parameters={{ ...parameters }} />
|
||||
|
||||

|
||||
|
||||
# `cssVariables`
|
||||
|
||||
A Svelte [action](https://svelte.dev/tutorial/actions) you can use to easily set [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) on HTML elements. Useful for passing JavaScript values to your component SCSS like this:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { cssVariables } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
export let height = 300;
|
||||
export let textColour = 'red';
|
||||
|
||||
// Create an object of variable names and CSS values...
|
||||
$: variables = {
|
||||
height: height + 'px',
|
||||
textColour: textColour,
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- Attach it to a parent element with the action -->
|
||||
<div use:cssVariables="{variables}">
|
||||
<p>My text...</p>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
/**
|
||||
* Now use your variables in your SCSS!
|
||||
*/
|
||||
div {
|
||||
height: var(--height);
|
||||
p {
|
||||
color: var(--textColour);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
20
src/actions/cssVariables/index.js
Normal file
20
src/actions/cssVariables/index.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Shamelessly stolen from: https://github.com/kaisermann/svelte-css-vars
|
||||
export default (node, props) => {
|
||||
Object.entries(props).forEach(([key, value]) => {
|
||||
node.style.setProperty(`--${key}`, value);
|
||||
});
|
||||
|
||||
return {
|
||||
update(new_props) {
|
||||
Object.entries(new_props).forEach(([key, value]) => {
|
||||
node.style.setProperty(`--${key}`, value);
|
||||
delete props[key];
|
||||
});
|
||||
|
||||
Object.keys(props).forEach(name =>
|
||||
node.style.removeProperty(`--${name}`),
|
||||
);
|
||||
props = new_props;
|
||||
},
|
||||
};
|
||||
};
|
||||
25
src/actions/resizeObserver/index.js
Normal file
25
src/actions/resizeObserver/index.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Shamelessly stolen from https://github.com/sveltejs/svelte/issues/7583#issue-1260717165
|
||||
let observer;
|
||||
let callbacks;
|
||||
|
||||
export default (element, onResize) => {
|
||||
if (!observer) {
|
||||
callbacks = new WeakMap();
|
||||
observer = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
const onResize = callbacks.get(entry.target);
|
||||
if (onResize) onResize(entry.target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
callbacks.set(element, onResize);
|
||||
observer.observe(element);
|
||||
|
||||
return {
|
||||
destroy: () => {
|
||||
callbacks.delete(element);
|
||||
observer.unobserve(element);
|
||||
},
|
||||
};
|
||||
}
|
||||
601
src/components/Ai2svelte/ai2svelte/ai-chart.exclude.svelte
Normal file
601
src/components/Ai2svelte/ai2svelte/ai-chart.exclude.svelte
Normal file
|
|
@ -0,0 +1,601 @@
|
|||
<script>
|
||||
import { assets } from '$app/paths';
|
||||
let width = null;
|
||||
</script>
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2021-09-29 12:37 -->
|
||||
|
||||
<div id="g-_ai-chart-box" bind:clientWidth="{width}">
|
||||
<!-- Artboard: xs -->
|
||||
{#if width && width >= 0 && width < 510}
|
||||
<div id="g-_ai-chart-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 91.7004% 0;"></div>
|
||||
<div
|
||||
id="g-_ai-chart-xs-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="{`background-image: url(${assets}/images/graphics/ai-chart-xs.png);`}"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai0-1"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:3.216%;margin-top:-7.7px;left:0.5952%;width:99px;"
|
||||
>
|
||||
<p class="g-pstyle0">Shake intensity</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-2"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:9.8251%;margin-top:-7.7px;left:4.9821%;width:47px;"
|
||||
>
|
||||
<p class="g-pstyle0">Light</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-3"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:15.7733%;margin-top:-7.7px;left:4.9821%;width:69px;"
|
||||
>
|
||||
<p class="g-pstyle0">Moderate</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-4"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:16.4343%;margin-top:-7.7px;left:79.0675%;width:82px;"
|
||||
>
|
||||
<p class="g-pstyle0">Cap-Haitien</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-5"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:21.7216%;margin-top:-7.7px;left:4.9821%;width:55px;"
|
||||
>
|
||||
<p class="g-pstyle0">Strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-6"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:28.0002%;margin-top:-7.7px;left:4.9821%;width:78px;"
|
||||
>
|
||||
<p class="g-pstyle0">Very strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-7"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:28.9916%;margin-top:-7.7px;left:62.2348%;width:68px;"
|
||||
>
|
||||
<p class="g-pstyle0">Gonaïves</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-8"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:39.9449%;margin-top:-14.9px;left:28.714%;margin-left:-36.5px;width:73px;"
|
||||
>
|
||||
<p class="g-pstyle1">Caribbean</p>
|
||||
<p class="g-pstyle1">Sea</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-9"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:42.6579%;margin-top:-10.1px;left:68.5061%;width:77px;"
|
||||
>
|
||||
<p class="g-pstyle2">HAITI</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-10"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:59.0632%;margin-top:-7.7px;left:11.2526%;width:63px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jeremie</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-11"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:61.1155%;margin-top:-8.9px;left:70.5455%;width:106px;"
|
||||
>
|
||||
<p class="g-pstyle3">Port-au-Prince</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-12"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:62.1069%;margin-top:-8.9px;left:32.6015%;width:77px;"
|
||||
>
|
||||
<p class="g-pstyle3">Epicenter</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-13"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:78.8906%;margin-top:-7.7px;left:63.9138%;width:58px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jacmel</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-14"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:80.2124%;margin-top:-7.7px;left:22.5649%;width:71px;"
|
||||
>
|
||||
<p class="g-pstyle0">Les Cayes</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-15"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:87.8129%;margin-top:-7.7px;left:0.6179%;width:49px;"
|
||||
>
|
||||
<p class="g-pstyle0">50 mi</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-16"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:91.0202%;margin-top:-11.4px;right:10.4418%;width:70px;"
|
||||
>
|
||||
<p class="g-pstyle4">Dominican</p>
|
||||
<p class="g-pstyle4">Republic</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai0-17"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:93.7611%;margin-top:-7.7px;left:0.6179%;width:52px;"
|
||||
>
|
||||
<p class="g-pstyle0">50 km</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: sm -->
|
||||
{#if width && width >= 510 && width < 660}
|
||||
<div id="g-_ai-chart-sm" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 82.703% 0;"></div>
|
||||
<div
|
||||
id="g-_ai-chart-sm-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="{`background-image: url(${assets}/images/graphics/ai-chart-sm.png);`}"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai1-1"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:3.8773%;margin-top:-9.4px;left:0.3278%;width:111px;"
|
||||
>
|
||||
<p class="g-pstyle0">Shake intensity</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-2"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:9.0933%;margin-top:-9.4px;left:3.0258%;width:52px;"
|
||||
>
|
||||
<p class="g-pstyle0">Light</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-3"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:13.5979%;margin-top:-9.4px;left:3.0259%;width:77px;"
|
||||
>
|
||||
<p class="g-pstyle0">Moderate</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-4"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:16.6801%;margin-top:-9.4px;left:70.3255%;width:92px;"
|
||||
>
|
||||
<p class="g-pstyle0">Cap-Haitien</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-5"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:18.3397%;margin-top:-9.4px;left:3.0258%;width:61px;"
|
||||
>
|
||||
<p class="g-pstyle0">Strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-6"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:22.6073%;margin-top:-9.4px;left:3.0258%;width:88px;"
|
||||
>
|
||||
<p class="g-pstyle0">Very strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-7"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:28.5344%;margin-top:-9.4px;left:55.9181%;width:76px;"
|
||||
>
|
||||
<p class="g-pstyle0">Gonaïves</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-8"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:38.8091%;margin-top:-17.7px;left:27.2818%;margin-left:-41px;width:82px;"
|
||||
>
|
||||
<p class="g-pstyle1">Caribbean</p>
|
||||
<p class="g-pstyle1">Sea</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-9"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:39.9724%;margin-top:-8.6px;left:61.2858%;width:67px;"
|
||||
>
|
||||
<p class="g-pstyle2">HAITI</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-10"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:56.985%;margin-top:-9.4px;left:12.2815%;width:69px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jeremie</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-11"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:59.1569%;margin-top:-9.5px;left:63.0314%;width:112px;"
|
||||
>
|
||||
<p class="g-pstyle3">Port-au-Prince</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-12"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:60.1053%;margin-top:-9.5px;left:30.5543%;width:81px;"
|
||||
>
|
||||
<p class="g-pstyle3">Epicenter</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-13"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:62.7194%;margin-top:-16.5px;left:91.2282%;margin-left:-57px;width:114px;"
|
||||
>
|
||||
<p class="g-pstyle4">Dominican</p>
|
||||
<p class="g-pstyle4">Republic</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-14"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:75.4778%;margin-top:-9.4px;left:57.3552%;width:64px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jacmel</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-15"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:76.6632%;margin-top:-9.4px;left:21.9639%;width:79px;"
|
||||
>
|
||||
<p class="g-pstyle0">Les Cayes</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-16"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:85.5251%;margin-top:-7.7px;left:0.1344%;width:49px;"
|
||||
>
|
||||
<p class="g-pstyle5">50 mi</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai1-17"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:90.0297%;margin-top:-7.7px;left:0.1344%;width:52px;"
|
||||
>
|
||||
<p class="g-pstyle5">50 km</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: md -->
|
||||
{#if width && width >= 660}
|
||||
<div id="g-_ai-chart-md" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 79.6009% 0;"></div>
|
||||
<div
|
||||
id="g-_ai-chart-md-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="{`background-image: url(${assets}/images/graphics/ai-chart-md.png);`}"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai2-1"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:2.3515%;margin-top:-9.4px;left:0.3608%;width:111px;"
|
||||
>
|
||||
<p class="g-pstyle0">Shake intensity</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-2"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:7.6811%;margin-top:-9.4px;left:2.6603%;width:52px;"
|
||||
>
|
||||
<p class="g-pstyle0">Light</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-3"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:12.2494%;margin-top:-9.4px;left:2.6604%;width:77px;"
|
||||
>
|
||||
<p class="g-pstyle0">Moderate</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-4"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:15.4852%;margin-top:-9.4px;left:70.3606%;width:92px;"
|
||||
>
|
||||
<p class="g-pstyle0">Cap-Haitien</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-5"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:17.1983%;margin-top:-9.4px;left:2.6603%;width:61px;"
|
||||
>
|
||||
<p class="g-pstyle0">Strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-6"
|
||||
class="g-legend g-aiAbs g-aiPointText"
|
||||
style="top:21.7666%;margin-top:-9.4px;left:2.6603%;width:88px;"
|
||||
>
|
||||
<p class="g-pstyle0">Very strong</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-7"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:27.6672%;margin-top:-9.4px;left:55.993%;width:76px;"
|
||||
>
|
||||
<p class="g-pstyle0">Gonaïves</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-8"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:38.0099%;margin-top:-17.7px;left:27.2388%;margin-left:-41px;width:82px;"
|
||||
>
|
||||
<p class="g-pstyle1">Caribbean</p>
|
||||
<p class="g-pstyle1">Sea</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-9"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:42.7626%;margin-top:-10.7px;left:62.8914%;width:80px;"
|
||||
>
|
||||
<p class="g-pstyle2">HAITI</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-10"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:50.0029%;margin-top:-17.7px;left:92.295%;margin-left:-60.5px;width:121px;"
|
||||
>
|
||||
<p class="g-pstyle3">Dominican</p>
|
||||
<p class="g-pstyle3">Republic</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-11"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:57.3608%;margin-top:-9.4px;left:12.2815%;width:69px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jeremie</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-12"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:60.2742%;margin-top:-10.7px;left:30.6995%;width:89px;"
|
||||
>
|
||||
<p class="g-pstyle4">Epicenter</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-13"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:62.5583%;margin-top:-10.7px;left:66.3403%;width:125px;"
|
||||
>
|
||||
<p class="g-pstyle4">Port-au-Prince</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-14"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:75.6338%;margin-top:-9.4px;left:57.8174%;width:64px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jacmel</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-15"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:77.3469%;margin-top:-9.4px;left:22.5239%;width:79px;"
|
||||
>
|
||||
<p class="g-pstyle0">Les Cayes</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-16"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:86.936%;margin-top:-7.7px;left:0.1678%;width:49px;"
|
||||
>
|
||||
<p class="g-pstyle5">50 mi</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-17"
|
||||
class="g-map-labels g-aiAbs g-aiPointText"
|
||||
style="top:91.5043%;margin-top:-7.7px;left:0.1678%;width:52px;"
|
||||
>
|
||||
<p class="g-pstyle5">50 km</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- End ai2html - 2021-09-29 12:37 -->
|
||||
|
||||
<!-- ai file: _ai-chart.ai -->
|
||||
<style lang="scss">
|
||||
#g-_ai-chart-box,
|
||||
#g-_ai-chart-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-_ai-chart-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-_ai-chart-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-_ai-chart-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-_ai-chart-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-_ai-chart-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-_ai-chart-xs p {
|
||||
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
color: rgb(51, 51, 51);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-_ai-chart-xs .g-pstyle0 {
|
||||
height: 14px;
|
||||
}
|
||||
#g-_ai-chart-xs .g-pstyle1 {
|
||||
font-style: italic;
|
||||
height: 14px;
|
||||
text-align: center;
|
||||
color: rgb(113, 115, 117);
|
||||
}
|
||||
#g-_ai-chart-xs .g-pstyle2 {
|
||||
font-weight: 700;
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
letter-spacing: 0.25em;
|
||||
font-size: 15px;
|
||||
}
|
||||
#g-_ai-chart-xs .g-pstyle3 {
|
||||
font-weight: 700;
|
||||
line-height: 16px;
|
||||
height: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
#g-_ai-chart-xs .g-pstyle4 {
|
||||
line-height: 11px;
|
||||
height: 11px;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
}
|
||||
#g-_ai-chart-sm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-_ai-chart-sm p {
|
||||
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: rgb(51, 51, 51);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle0 {
|
||||
height: 17px;
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle1 {
|
||||
font-style: italic;
|
||||
height: 17px;
|
||||
text-align: center;
|
||||
color: rgb(113, 115, 117);
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle2 {
|
||||
font-weight: 700;
|
||||
line-height: 15px;
|
||||
height: 15px;
|
||||
letter-spacing: 0.25em;
|
||||
font-size: 12px;
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle3 {
|
||||
font-weight: 700;
|
||||
height: 17px;
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle4 {
|
||||
font-weight: 300;
|
||||
line-height: 16px;
|
||||
height: 16px;
|
||||
letter-spacing: 0.25em;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: rgb(134, 136, 139);
|
||||
}
|
||||
#g-_ai-chart-sm .g-pstyle5 {
|
||||
line-height: 14px;
|
||||
height: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#g-_ai-chart-md {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-_ai-chart-md p {
|
||||
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 17px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
color: rgb(51, 51, 51);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle0 {
|
||||
height: 17px;
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle1 {
|
||||
font-style: italic;
|
||||
height: 17px;
|
||||
text-align: center;
|
||||
color: rgb(113, 115, 117);
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle2 {
|
||||
font-weight: 700;
|
||||
line-height: 19px;
|
||||
height: 19px;
|
||||
letter-spacing: 0.25em;
|
||||
font-size: 16px;
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle3 {
|
||||
font-weight: 300;
|
||||
height: 17px;
|
||||
letter-spacing: 0.25em;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: rgb(134, 136, 139);
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle4 {
|
||||
font-weight: 700;
|
||||
line-height: 19px;
|
||||
height: 19px;
|
||||
font-size: 16px;
|
||||
}
|
||||
#g-_ai-chart-md .g-pstyle5 {
|
||||
line-height: 14px;
|
||||
height: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Custom CSS */
|
||||
</style>
|
||||
230
src/components/Ai2svelte/ai2svelte/ai-linechart.exclude.svelte
Normal file
230
src/components/Ai2svelte/ai2svelte/ai-linechart.exclude.svelte
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
<script>
|
||||
import { assets } from '$app/paths';
|
||||
let width = null;
|
||||
</script>
|
||||
|
||||
<!-- Generated by ai2html v0.100.0 - 2022-02-14 14:47 -->
|
||||
|
||||
<div id="g-ai-linechart-box" bind:clientWidth="{width}">
|
||||
<!-- Artboard: xs -->
|
||||
{#if width && width >= 0 && width < 510}
|
||||
<div id="g-ai-linechart-xs" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 187.8788% 0;"></div>
|
||||
<div
|
||||
id="g-ai-linechart-xs-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="background-image: url({assets}/images/graphics/ai-linechart-xs.png);"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: sm -->
|
||||
{#if width && width >= 510 && width < 660}
|
||||
<div id="g-ai-linechart-sm" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 121.5686% 0;"></div>
|
||||
<div
|
||||
id="g-ai-linechart-sm-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="background-image: url({assets}/images/graphics/ai-linechart-sm.png);"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: md -->
|
||||
{#if width && width >= 660 && width < 930}
|
||||
<div id="g-ai-linechart-md" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 52.7353% 0;"></div>
|
||||
<div
|
||||
id="g-ai-linechart-md-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="background-image: url({assets}/images/graphics/ai-linechart-md.png);"
|
||||
></div>
|
||||
<div
|
||||
id="g-ai2-1"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:10.8355%;margin-top:-9.7px;left:10.3982%;width:41px;"
|
||||
>
|
||||
<p class="g-pstyle0">5%</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-2"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:29.5108%;margin-top:-9.7px;left:10.3982%;width:29px;"
|
||||
>
|
||||
<p class="g-pstyle0">0</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-3"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:35.0946%;margin-top:-11.1px;left:84.3799%;width:55px;"
|
||||
>
|
||||
<p class="g-pstyle1">Dow</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-4"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:39.9791%;margin-top:-11.1px;left:84.3801%;width:83px;"
|
||||
>
|
||||
<p class="g-pstyle2">S&P 500</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-5"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:48.3111%;margin-top:-11.1px;left:84.3801%;width:78px;"
|
||||
>
|
||||
<p class="g-pstyle3">Nasdaq</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-6"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:48.1861%;margin-top:-9.7px;left:10.3982%;width:36px;"
|
||||
>
|
||||
<p class="g-pstyle0">−5</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-7"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:67.1488%;margin-top:-9.7px;left:10.3982%;width:43px;"
|
||||
>
|
||||
<p class="g-pstyle0">−10</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-8"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:85.8241%;margin-top:-9.7px;left:10.3982%;width:43px;"
|
||||
>
|
||||
<p class="g-pstyle0">−15</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-9"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:93.0069%;margin-top:-9.7px;left:28.7755%;width:92px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jan. 2, 2022</p>
|
||||
</div>
|
||||
<div
|
||||
id="g-ai2-10"
|
||||
class="g-ai2html-settings g-aiAbs g-aiPointText"
|
||||
style="top:93.0069%;margin-top:-9.7px;left:73.7228%;width:57px;"
|
||||
>
|
||||
<p class="g-pstyle0">Jan. 9</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: lg -->
|
||||
{#if width && width >= 930 && width < 1200}
|
||||
<div id="g-ai-linechart-lg" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 66.6667% 0;"></div>
|
||||
<div
|
||||
id="g-ai-linechart-lg-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="background-image: url({assets}/images/graphics/ai-linechart-lg.png);"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
<!-- Artboard: xl -->
|
||||
{#if width && width >= 1200}
|
||||
<div id="g-ai-linechart-xl" class="g-artboard" style="">
|
||||
<div style="padding: 0 0 51.6667% 0;"></div>
|
||||
<div
|
||||
id="g-ai-linechart-xl-img"
|
||||
class="g-aiImg"
|
||||
alt=""
|
||||
style="background-image: url({assets}/images/graphics/ai-linechart-xl.png);"
|
||||
></div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<!-- End ai2html - 2022-02-14 14:47 -->
|
||||
|
||||
<!-- ai file: ai-linechart.ai -->
|
||||
<style lang="scss">
|
||||
#g-ai-linechart-box,
|
||||
#g-ai-linechart-box .g-artboard {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#g-ai-linechart-box p {
|
||||
margin: 0;
|
||||
}
|
||||
#g-ai-linechart-box .g-aiAbs {
|
||||
position: absolute;
|
||||
}
|
||||
#g-ai-linechart-box .g-aiImg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
#g-ai-linechart-box .g-aiPointText p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#g-ai-linechart-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-ai-linechart-sm {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-ai-linechart-md {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-ai-linechart-md p {
|
||||
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-weight: 300;
|
||||
line-height: 18px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
letter-spacing: 0em;
|
||||
font-size: 15px;
|
||||
text-align: left;
|
||||
color: rgb(0, 0, 0);
|
||||
text-transform: none;
|
||||
padding-bottom: 0;
|
||||
padding-top: 0;
|
||||
mix-blend-mode: normal;
|
||||
font-style: normal;
|
||||
position: static;
|
||||
}
|
||||
#g-ai-linechart-md .g-pstyle0 {
|
||||
height: 18px;
|
||||
}
|
||||
#g-ai-linechart-md .g-pstyle1 {
|
||||
font-weight: 700;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
font-size: 17px;
|
||||
color: rgb(116, 196, 118);
|
||||
}
|
||||
#g-ai-linechart-md .g-pstyle2 {
|
||||
font-weight: 700;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
font-size: 17px;
|
||||
color: rgb(49, 130, 189);
|
||||
}
|
||||
#g-ai-linechart-md .g-pstyle3 {
|
||||
font-weight: 700;
|
||||
line-height: 20px;
|
||||
height: 20px;
|
||||
font-size: 17px;
|
||||
color: rgb(244, 193, 0);
|
||||
}
|
||||
#g-ai-linechart-lg {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#g-ai-linechart-xl {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Custom CSS */
|
||||
</style>
|
||||
24
src/components/Ai2svelte/ai2svelte/data-table.exclude.svelte
Normal file
24
src/components/Ai2svelte/ai2svelte/data-table.exclude.svelte
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<table class="linechart-data">
|
||||
<thead
|
||||
><tr>
|
||||
<th data-column="Date" data-row="-1">Date </th><th
|
||||
data-column="S&P 500"
|
||||
data-row="-1"
|
||||
>S&P 500
|
||||
</th><th data-column="Dow" data-row="-1">Dow </th><th
|
||||
data-column="Nasdaq"
|
||||
data-row="-1"
|
||||
>Nasdaq
|
||||
</th>
|
||||
</tr></thead
|
||||
>
|
||||
<tbody
|
||||
><tr> <td>December 31, 2021</td><td>0%</td><td>0</td><td>0%</td></tr><tr>
|
||||
<td>January 3, 2022</td><td>1%</td><td>1</td><td>1%</td></tr
|
||||
><tr> <td>January 4, 2022</td><td>1%</td><td>1</td><td>−0%</td></tr><tr>
|
||||
<td>January 5, 2022</td><td>−1%</td><td>0</td><td>−3%</td></tr
|
||||
><tr> <td>January 6, 2022</td><td>−1%</td><td>−0</td><td>−4%</td></tr><tr>
|
||||
<td>January 7, 2022</td><td>−2%</td><td>−0</td><td>−5%</td></tr
|
||||
><tr> <td>January 10, 2022</td><td>−2%</td><td>−1</td><td>−4%</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
320
src/components/Ai2svelte/docs.svx
Normal file
320
src/components/Ai2svelte/docs.svx
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
---
|
||||
title: Ai2svelte
|
||||
description: A shortcut for ai2svelte graphics.
|
||||
slug: ai2svelte
|
||||
---
|
||||
|
||||
<script>
|
||||
import DemoContainer from '../_docs/DemoContainer/index.svelte';
|
||||
import Ai2svelte from './index.svelte';
|
||||
import AiChart from './ai2svelte/ai-chart.exclude.svelte';
|
||||
import DataTable from './ai2svelte/data-table.exclude.svelte'
|
||||
|
||||
const fetchComponent = async(componentName) => {
|
||||
return (await import(`./ai2svelte/${componentName}.exclude.svelte`)).default
|
||||
};
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
||||
## {title}
|
||||
|
||||
Pass a component created by [ai2svelte](https://github.com/reuters-graphics/ai2svelte) to this component, which will wrap it in a graphics section tag.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Ai2svelte } from '@reuters-graphics/graphics-svelte-components';
|
||||
import MyAiChart from './some-chart.svelte';
|
||||
</script>
|
||||
|
||||
<Ai2svelte
|
||||
AiGraphic="{MyAiChart}"
|
||||
ariaDescription="Description of your graphic for screen readers."
|
||||
/>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Ai2svelte AiGraphic={AiChart} ariaHidden = {true}
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country." />
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
## Accessibility props
|
||||
|
||||
`ariaHidden`
|
||||
|
||||
- Set to `true` by default, which means HTML text content in the
|
||||
ai2svelte component are visible on the page but not read aloud by screen readers.
|
||||
(Read more about aria-hidden elements [here](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden).)
|
||||
- Set it to `false` to make the screen reader read aloud HTML text content in ai2svelte.
|
||||
|
||||
`ariaDescription`
|
||||
|
||||
- If `ariaHidden` is `true`, make sure you add an `ariaDescription`, which is invisible on the page but is read aloud by screen readers.
|
||||
- This prop takes a string, like altText for an image.
|
||||
- It should describe what the chart is showing and why it's important. This is helpful for:
|
||||
- Blind readers using screen-reading or Braille conversion software
|
||||
- All readers if the image is unavailable or takes too long to load
|
||||
- Read about writing aria description, also known as alt text, [here](https://medium.com/nightingale/writing-alt-text-for-data-visualization-2a218ef43f81).
|
||||
|
||||
<section class='note'>
|
||||
|
||||
🚨 **Important:** If `ariaHidden` is `true` but you are missing `ariaDescription` or some other special alternative for screen readers (more on this later), your graphic will be hidden from the page and you will see a warning in your console.
|
||||
|
||||
</section>
|
||||
|
||||
Graphics kit users can set your `ariaHidden` prop and write your `ariaDescription` in the google doc like this:
|
||||
|
||||
</section>
|
||||
|
||||
```bash
|
||||
# In your Google doc...
|
||||
[blocks]
|
||||
|
||||
# ...
|
||||
Type: ai2svelte
|
||||
ComponentName: my-chart
|
||||
AriaHidden: true // or false
|
||||
AltText: Add a descriptive line on the graphic for screen readers.
|
||||
|
||||
[]
|
||||
```
|
||||
|
||||
<section>
|
||||
|
||||
## Graphic width
|
||||
|
||||
Adjust the size of the graphic by passing a class name corresponding to one of our well widths: `wide`, `wider`, `widest` or `fluid`.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Ai2svelte } from '@reuters-graphics/graphics-svelte-components';
|
||||
import MyAiChart from './some-chart.svelte';
|
||||
</script>
|
||||
|
||||
<Ai2svelte
|
||||
AiGraphic="{MyAiChart}"
|
||||
size="wide"
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country."
|
||||
/>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Ai2svelte AiGraphic={AiChart} size='wide'
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country." />
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
## Chart title, description, source and note
|
||||
|
||||
Add additional chart chatter through slots.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Ai2svelte } from '@reuters-graphics/graphics-svelte-components';
|
||||
import MyAiChart from './some-chart.svelte';
|
||||
</script>
|
||||
|
||||
<!-- Add an ID and change the default width of your graphic -->
|
||||
<Ai2svelte
|
||||
AiGraphic="{MyAiChart}"
|
||||
id="ai-map"
|
||||
size="wide"
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country."
|
||||
>
|
||||
<!-- Add a title and/or notes with slots -->
|
||||
<div slot="title" class="title">
|
||||
<h4>Earthquake in Haiti</h4>
|
||||
<p>A 7.0 magnitude earthquake struck the island on Tuesday.</p>
|
||||
</div>
|
||||
<aside slot="notes">
|
||||
<p class="note">Note: Data current as of Wednesday.</p>
|
||||
<p class="source">Source: USGIS</p>
|
||||
</aside>
|
||||
</Ai2svelte>
|
||||
|
||||
<style lang="scss">
|
||||
// You can now style those elements!
|
||||
h4 {
|
||||
color: darkred;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Ai2svelte AiGraphic="{AiChart}" id="ai-map"
|
||||
ariaHidden = {true}
|
||||
size="wide"
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country." >
|
||||
<!-- Add a title and/or notes with slots -->
|
||||
<div slot="title" class="title">
|
||||
<h4>Earthquake in Haiti</h4>
|
||||
<p>A 7.0 magnitude earthquake struck the island on Tuesday.</p>
|
||||
</div>
|
||||
<aside slot="notes">
|
||||
<p class="note">Note: Data current as of Wednesday.</p>
|
||||
<p class="source">Source: USGIS</p>
|
||||
</aside>
|
||||
</Ai2svelte>
|
||||
</DemoContainer>
|
||||
|
||||
<style>
|
||||
div.title h4 {
|
||||
color: darkred;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section>
|
||||
|
||||
## Using ArchieML google doc
|
||||
|
||||
You can use this component to layout AI graphics via an [ArchieML](http://archieml.org/)-formatted Google doc by using the following pattern to dynamically import an ai2svelte component:
|
||||
|
||||
</section>
|
||||
|
||||
```bash
|
||||
# In your Google doc...
|
||||
[blocks]
|
||||
|
||||
# ...
|
||||
Type: ai2svelte
|
||||
ComponentName: my-chart
|
||||
|
||||
[]
|
||||
```
|
||||
|
||||
```svelte
|
||||
<!-- src/lib/Page.svelte -->
|
||||
<script>
|
||||
import content from '$locales/en/content.json';
|
||||
import { Ai2svelte } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
import { truthyString } from '$utils/truthyString';
|
||||
import { fetchComponent } from '$utils/dynamicComponents';
|
||||
</script>
|
||||
|
||||
{#each content.blocks as block}
|
||||
{#if block.Type === 'ai2svelte'}
|
||||
{#await fetchComponent(block.ComponentName)}
|
||||
<div></div>
|
||||
{:then component}
|
||||
<Ai2svelte
|
||||
AiGraphic="{component}"
|
||||
id="{block.ComponentName}"
|
||||
size="{block.Size}"
|
||||
ariaHidden="{truthyString(block.AriaHidden)}"
|
||||
ariaDescription="{block.AltText}"
|
||||
>
|
||||
<!-- Code below is optional. Can delete if you have no dek, source, note, etc. -->
|
||||
<div slot="title" class="title">
|
||||
{#if block.Title}<h4>{block.Title}</h4>{/if}
|
||||
{#if block.Chatter}<p>{block.Chatter}</p>{/if}
|
||||
</div>
|
||||
<aside slot="notes">
|
||||
{#if block.Note}<p class="note">Note: {block.Note}</p>{/if}
|
||||
{#if block.Source}<p class="source">Source: {block.Source}</p>{/if}
|
||||
</aside>
|
||||
<!-- End of optional code -->
|
||||
</Ai2svelte>
|
||||
{:catch error}
|
||||
{console.error(
|
||||
`Error fetching component: ./ai2svelte/${block.ComponentName}.svelte`,
|
||||
error
|
||||
)}
|
||||
{/await}
|
||||
{/if}
|
||||
{/each}
|
||||
```
|
||||
|
||||
<section>
|
||||
|
||||
This comes with some restrictions, though. Be sure your `fetchComponent` function follows [the limits on dynamic imports](https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations).
|
||||
|
||||
**NOTE**: Make sure you wrap the function `truthyString()` around `block.StackBackground`. `truthyString()` converts the string pulled from Google docs ('true', 'false', etc.)
|
||||
into a Boolean.
|
||||
|
||||
</section>
|
||||
|
||||
<section class='pt-5'>
|
||||
|
||||
## Customising data for screen readers
|
||||
|
||||
Sometimes, in addition to or instead of adding an aria description for graphics,
|
||||
we want to also provide a data table, a lengthier description with more complex element structure or something else.
|
||||
|
||||
You can add custom information for screen readers only by using the `hidden` slot.
|
||||
|
||||
For example, for the line chart below, we can add a **data table** that helps screen-reader users navigate the data.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import SRDataTable from './SRDataTable.svelte';
|
||||
</script>
|
||||
<Ai2svelte
|
||||
AiGraphic="{MyAiChart}"
|
||||
ariaDescription="A line chart showing daily closing prices for S&P 500, Dow, and Nasdaq from Dec. 31, 2021 to Jan. 10, 2022"
|
||||
>
|
||||
<slot slot="hidden">
|
||||
<SRDataTable/>
|
||||
</slot>
|
||||
<Ai2svelte/>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
{#await fetchComponent('ai-linechart') then component}
|
||||
<Ai2svelte AiGraphic="{component}"
|
||||
ariaDescription="A map of Haiti shows the epicenter of an earthquake in the southwest of the country.">
|
||||
<slot slot="hidden"><DataTable/></slot>
|
||||
</Ai2svelte>
|
||||
{/await}
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
This is what `<SRDataTable/>` looks like. (You can make an HTML table using DataWrapper.)
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<table class="line-chart-data">
|
||||
<thead
|
||||
><tr>
|
||||
<th data-column="Date" data-row="-1">Date </th><th
|
||||
data-column="S&P 500"
|
||||
data-row="-1"
|
||||
>S&P 500
|
||||
</th><th data-column="Dow" data-row="-1">Dow </th><th
|
||||
data-column="Nasdaq"
|
||||
data-row="-1"
|
||||
>Nasdaq
|
||||
</th>
|
||||
</tr></thead
|
||||
>
|
||||
<tbody
|
||||
><tr> <td>December 31, 2021</td><td>0%</td><td>0</td><td>0%</td></tr><tr>
|
||||
<td>January 3, 2022</td><td>1%</td><td>1</td><td>1%</td></tr
|
||||
><tr> <td>January 4, 2022</td><td>1%</td><td>1</td><td>−0%</td></tr><tr>
|
||||
<td>January 5, 2022</td><td>−1%</td><td>0</td><td>−3%</td></tr
|
||||
><tr> <td>January 6, 2022</td><td>−1%</td><td>−0</td><td>−4%</td></tr><tr>
|
||||
<td>January 7, 2022</td><td>−2%</td><td>−0</td><td>−5%</td></tr
|
||||
><tr> <td>January 10, 2022</td><td>−2%</td><td>−1</td><td>−4%</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
```
|
||||
|
||||
<section>
|
||||
|
||||
[Read this](https://accessibility.psu.edu/images/charts/) for more information on screen-reader data tables for charts.
|
||||
|
||||
</section>
|
||||
63
src/components/Ai2svelte/index.svelte
Normal file
63
src/components/Ai2svelte/index.svelte
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<script>
|
||||
/* This component wraps ai2svelte graphics. */
|
||||
export let AiGraphic;
|
||||
export let id = '';
|
||||
export let ariaHidden = true;
|
||||
|
||||
export let ariaDescription = null;
|
||||
|
||||
// normal, wide, wider, widest or fluid
|
||||
export let size = 'normal';
|
||||
export let onAiMounted = () => {};
|
||||
|
||||
if (ariaHidden && !ariaDescription) {
|
||||
console.warn(
|
||||
'Must provide aria description for ai2svelte components if ariaHidden is true.'
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="ai2svelte-container graphic {size}" id="{id}">
|
||||
{#if (ariaHidden && (ariaDescription || $$slots.hidden)) || !ariaHidden}
|
||||
{#if $$slots.title}
|
||||
<div class="chatter-container">
|
||||
<slot name="title" />
|
||||
</div>
|
||||
{/if}
|
||||
{#if ariaDescription}
|
||||
<p class="visually-hidden">{ariaDescription}</p>
|
||||
{/if}
|
||||
{#if $$slots.hidden}
|
||||
<div class="visually-hidden custom">
|
||||
<slot name="hidden" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ai-wrapper" aria-hidden="{ariaHidden}">
|
||||
<svelte:component this="{AiGraphic}" onAiMounted="{onAiMounted}" />
|
||||
</div>
|
||||
{#if $$slots.notes}
|
||||
<div class="chatter-container">
|
||||
<slot name="notes" />
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~@reuters-graphics/style-theme-eisbaer/scss/components/containers/widths';
|
||||
.chatter-container {
|
||||
@extend .well;
|
||||
}
|
||||
.visually-hidden {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important; // Fix for https://github.com/twbs/bootstrap/issues/25686
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
</style>
|
||||
126
src/components/Article/Article.stories.svelte
Normal file
126
src/components/Article/Article.stories.svelte
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// Don't lose the "?raw" in markdown imports!
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore
|
||||
import customWellWidthsDocs from './stories/docs/customWellWidths.md?raw';
|
||||
|
||||
import Article from './Article.svelte';
|
||||
import Section from '../Section/Section.svelte';
|
||||
|
||||
import { withComponentDocs, withStoryDocs } from '$docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Layout/Article',
|
||||
component: Article,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<Article {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Basic"
|
||||
args="{{
|
||||
embedded: false,
|
||||
id: '',
|
||||
}}"
|
||||
>
|
||||
<Article id="article-story-basic">
|
||||
<div class="demo-container">
|
||||
<div class="background-label">Article container</div>
|
||||
<div class="padding-label"><span>⇤</span>15px padding</div>
|
||||
</div>
|
||||
</Article>
|
||||
</Story>
|
||||
|
||||
<Story
|
||||
name="Custom columns"
|
||||
{...withStoryDocs(customWellWidthsDocs)}
|
||||
>
|
||||
<Article id="article-column-widths-demo">
|
||||
<div class="article-boundaries">
|
||||
<Section id="section-demo" width="narrower">narrower</Section>
|
||||
<Section id="section-demo" width="narrow">narrow</Section>
|
||||
<Section id="section-demo">normal</Section>
|
||||
<Section id="section-demo" width="wide">wide</Section>
|
||||
<Section id="section-demo" width="wider">wider</Section>
|
||||
<Section id="section-demo" width="widest">widest</Section>
|
||||
<Section id="section-demo" width="fluid">fluid</Section>
|
||||
</div>
|
||||
</Article>
|
||||
<Article id="article-column-widths-demo" columnWidths={{ narrower: 310, narrow: 450, normal: 550, wide: 675, wider: 1400 }}>
|
||||
<div class="article-boundaries custom">
|
||||
<Section id="section-demo" width="narrower">narrower*</Section>
|
||||
<Section id="section-demo" width="narrow">narrow*</Section>
|
||||
<Section id="section-demo">normal*</Section>
|
||||
<Section id="section-demo" width="wide">wide*</Section>
|
||||
<Section id="section-demo" width="wider">wider*</Section>
|
||||
<Section id="section-demo" width="widest">widest</Section>
|
||||
<Section id="section-demo" width="fluid">fluid</Section>
|
||||
</div>
|
||||
</Article>
|
||||
</Story>
|
||||
|
||||
<style lang="scss">
|
||||
:global {
|
||||
#article-story-basic,
|
||||
#article-column-widths-demo {
|
||||
width: calc(100% + 30px);
|
||||
margin-left: -15px;
|
||||
}
|
||||
#article-column-widths-demo {
|
||||
background-color: #ddd;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.article-boundaries {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #bbb;
|
||||
&.custom {
|
||||
section {
|
||||
background: rgb(211, 132, 123);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
height: 300px;
|
||||
background: #81a1c1;
|
||||
margin-bottom: 2px;
|
||||
height: 50px;
|
||||
padding-left: 3px;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.demo-container {
|
||||
height: 300px;
|
||||
background: #ccc;
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
.background-label {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 5px;
|
||||
}
|
||||
.padding-label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -15px;
|
||||
span {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
59
src/components/Article/Article.svelte
Normal file
59
src/components/Article/Article.svelte
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<script lang="ts">
|
||||
/** Set to true for embeddables. */
|
||||
export let embedded: boolean = false;
|
||||
|
||||
/** Add an id to the article tag to target it with custom CSS. */
|
||||
export let id: string = '';
|
||||
/** ARIA role of the article, usually ["main"](https://w3c.github.io/aria/#main) if enclosing the story. */
|
||||
export let role: string = 'main';
|
||||
|
||||
interface ColumnWidths {
|
||||
/** Narrower column width */
|
||||
narrower: number;
|
||||
/** Narrow column width */
|
||||
narrow: number;
|
||||
/** Normal column width */
|
||||
normal: number;
|
||||
/** Wide column width */
|
||||
wide: number;
|
||||
/** Wider column width */
|
||||
wider: number;
|
||||
}
|
||||
|
||||
/** Set custom widths for the normal, wide and wider column dimensions */
|
||||
export let columnWidths: ColumnWidths = {
|
||||
narrower: 330,
|
||||
narrow: 510,
|
||||
normal: 660,
|
||||
wide: 930,
|
||||
wider: 1200,
|
||||
};
|
||||
|
||||
import cssVariables from '$lib/actions/cssVariables/index.js';
|
||||
|
||||
$: columnWidthVars = {
|
||||
'narrower-column-width': columnWidths.narrower + 'px',
|
||||
'narrow-column-width': columnWidths.narrow + 'px',
|
||||
'normal-column-width': columnWidths.normal + 'px',
|
||||
'wide-column-width': columnWidths.wide + 'px',
|
||||
'wider-column-width': columnWidths.wider + 'px',
|
||||
};
|
||||
</script>
|
||||
|
||||
<article {id} class:embedded={embedded} role={role} use:cssVariables={columnWidthVars}>
|
||||
<!-- Article content -->
|
||||
<slot></slot>
|
||||
</article>
|
||||
|
||||
<style lang="scss">
|
||||
article {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0 15px;
|
||||
overflow-x: hidden;
|
||||
&.embedded {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
13
src/components/Article/stories/docs/component.md
Normal file
13
src/components/Article/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
The `Article` component contains all the content of our story and also establishes the dimensions of our article well.
|
||||
|
||||
> 📌 In most cases, you won't need to mess with the `Article` component because it's already included in our rigs for you!
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Article } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Article>
|
||||
<!-- The story stuff goes in here! -->
|
||||
</Article>
|
||||
```
|
||||
83
src/components/Article/stories/docs/customWellWidths.md
Normal file
83
src/components/Article/stories/docs/customWellWidths.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
The `Article` component also creates several column dimensions inside our article well. The standard widths of columns follow a basic class scheme:
|
||||
|
||||
- `narrower` A bit narrower than narrow...
|
||||
- `narrow` A bit narrower than the text column
|
||||
- `normal` **The main width of the body text column**
|
||||
- `wide` A bit wider than the text column
|
||||
- `wider` A bit wider than wide...
|
||||
- `widest` Edge-to-edge, but _excluding_ the left and right padding on `Article`
|
||||
- `fluid` Fully edge-to-edge
|
||||
|
||||
(Check out the below demo in the "Canvas" tab to better see the wider differences.)
|
||||
|
||||
When combined with the `Section` component, you can set custom column widths by passing an object to the `columnWidths` prop with pixel values for the `narrower`, `narrow`, `normal`, `wide` and `wider` column widths.
|
||||
|
||||
```svelte
|
||||
<Article
|
||||
columnWidths={{ narrower: 310, narrow: 450, normal: 550, wide: 675, wider: 1400 }}
|
||||
>
|
||||
<Section width='narrower' />
|
||||
<Section width='narrow' />
|
||||
<Section width='normal' />
|
||||
<Section width='wide' />
|
||||
<Section width='wider' />
|
||||
<Section width='widest' />
|
||||
<Section width='fluid' />
|
||||
</Article>
|
||||
```
|
||||
|
||||
> Keep in mind, other tools, like our AI templates, use our default column widths, so customising those widths here may have downstream consequences for graphics made outside your code.
|
||||
|
||||
If you're not using our `Section` component, you can still inherit the column widths from `Article` to create your own custom container using the article well dimensions by using [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) like this:
|
||||
|
||||
```svelte
|
||||
<div class="my-special-container">
|
||||
<!-- Stuffs... -->
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.my-special-container {
|
||||
max-width: var(--wide-column-width);
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
... or you can make your component entirely configurable within the article well doing something like this:
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
export let width = 'normal';
|
||||
</script>
|
||||
|
||||
<div class="my-special-container {width}">
|
||||
<!-- Stuffs... -->
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
div.my-special-container {
|
||||
max-width: var(--normal-column-width);
|
||||
&.narrower {
|
||||
max-width: var(--narrower-column-width);
|
||||
}
|
||||
&.narrow {
|
||||
max-width: var(--narrow-column-width);
|
||||
}
|
||||
&.wide {
|
||||
max-width: var(--wide-column-width);
|
||||
}
|
||||
&.wider {
|
||||
max-width: var(--wider-column-width);
|
||||
}
|
||||
&.widest {
|
||||
max-width: 100%;
|
||||
}
|
||||
&.fluid {
|
||||
width: calc(100% + 30px);
|
||||
margin-left: -15px;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
Here's an example of how custom* `columnWidths` can be used to change the article well columns:
|
||||
123
src/components/BeforeAfter/BeforeAfter.stories.svelte
Normal file
123
src/components/BeforeAfter/BeforeAfter.stories.svelte
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore
|
||||
import withOverlaysDocs from './stories/docs/withOverlays.md?raw';
|
||||
// @ts-ignore
|
||||
import ariaDescriptionsDocs from './stories/docs/ariaDescriptions.md?raw';
|
||||
|
||||
import BeforeAfter from './BeforeAfter.svelte';
|
||||
|
||||
// @ts-ignore
|
||||
import beforeImg from './stories/myrne-before.jpg';
|
||||
// @ts-ignore
|
||||
import afterImg from './stories/myrne-after.jpg';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/BeforeAfter',
|
||||
component: BeforeAfter,
|
||||
...withComponentDocs(componentDocs),
|
||||
argTypes: {
|
||||
handleColour: { control: 'color' },
|
||||
width: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<BeforeAfter {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
beforeSrc: beforeImg,
|
||||
beforeAlt: 'Satellite image of Russian base at Myrne taken on July 7, 2020.',
|
||||
afterSrc: afterImg,
|
||||
afterAlt: 'Satellite image of Russian base at Myrne taken on Oct. 20, 2020.',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story name="With overlays" {...withStoryDocs(withOverlaysDocs)}>
|
||||
<BeforeAfter
|
||||
beforeSrc="{beforeImg}"
|
||||
beforeAlt="Satellite image of Russian base at Myrne taken on July 7, 2020."
|
||||
afterSrc="{afterImg}"
|
||||
afterAlt="Satellite image of Russian base at Myrne taken on Oct. 20, 2020."
|
||||
>
|
||||
<div slot="beforeOverlay" class="overlay before">
|
||||
<h6>July 7, 2020</h6>
|
||||
<p>Initially, this site was far smaller.</p>
|
||||
</div>
|
||||
<div slot="afterOverlay" class="overlay after">
|
||||
<h6>Oct. 20, 2020</h6>
|
||||
<p>But then forces built up.</p>
|
||||
</div>
|
||||
<aside slot="caption">
|
||||
<p>Photos by MAXAR Technologies, 2021.</p>
|
||||
</aside>
|
||||
</BeforeAfter>
|
||||
|
||||
<style lang="scss">
|
||||
.overlay {
|
||||
color: white;
|
||||
padding: 15px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
&.after {
|
||||
text-align: right;
|
||||
}
|
||||
h6, p {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</Story>
|
||||
|
||||
<Story name="ARIA descriptions" {...withStoryDocs(ariaDescriptionsDocs)}>
|
||||
<BeforeAfter
|
||||
beforeSrc="{beforeImg}"
|
||||
beforeAlt="Satellite image of Russian base at Myrne taken on July 7, 2020."
|
||||
afterSrc="{afterImg}"
|
||||
afterAlt="Satellite image of Russian base at Myrne taken on Oct. 20, 2020."
|
||||
>
|
||||
<div let:description="{id}" slot="beforeOverlay" class="overlay">
|
||||
<p id="{id}">
|
||||
On July 7, 2020, the base contained only a few transport vehicles.
|
||||
</p>
|
||||
</div>
|
||||
<div let:description="{id}" slot="afterOverlay" class="overlay">
|
||||
<!-- 👇 id can also be used on an element containing multiple text elements -->
|
||||
<div id="{id}">
|
||||
<p>But by October, tanks and artillery could be seen.</p>
|
||||
<p>
|
||||
In total, over 50 pieces of heavy machinery and 200 personnel are now
|
||||
based here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</BeforeAfter>
|
||||
<style lang="scss">
|
||||
div.overlay {
|
||||
color: white;
|
||||
padding: 15px;
|
||||
max-width: 250px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
|
||||
}
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</Story>
|
||||
322
src/components/BeforeAfter/BeforeAfter.svelte
Normal file
322
src/components/BeforeAfter/BeforeAfter.svelte
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
<script lang="ts">
|
||||
import { throttle } from 'lodash-es';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
type ContainerWidth = 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
|
||||
/** Width of the chart within the text well. */
|
||||
export let width: ContainerWidth = 'normal'; // options: wide, wider, widest, fluid
|
||||
/** Height of the component */
|
||||
export let height = 600;
|
||||
|
||||
/**
|
||||
* If set, makes the height a ratio of the component's width.
|
||||
* @type {number}
|
||||
*/
|
||||
export let heightRatio: number | null = null;
|
||||
|
||||
/**
|
||||
* Before image src
|
||||
* @required
|
||||
*/
|
||||
export let beforeSrc: string | null = null;
|
||||
/**
|
||||
* Before image altText
|
||||
* @required
|
||||
*/
|
||||
export let beforeAlt: string | null = null;
|
||||
/**
|
||||
* After image src
|
||||
* @required
|
||||
*/
|
||||
export let afterSrc: string | null = null;
|
||||
/**
|
||||
* After image altText
|
||||
* @required
|
||||
*/
|
||||
export let afterAlt: string | null = null;
|
||||
|
||||
/** Drag handle colour */
|
||||
export let handleColour = 'white';
|
||||
/** Drag handle opacity */
|
||||
export let handleInactiveOpacity = 0.4;
|
||||
/** Margin at the edge of the image to stop dragging */
|
||||
export let handleMargin = 20;
|
||||
/** Percentage of the component width the handle will travel ona key press */
|
||||
export let keyPressStep = 0.05;
|
||||
|
||||
/** Initial offset of the handle, between 0 and 1.*/
|
||||
export let offset = 0.5;
|
||||
|
||||
const random4 = () =>
|
||||
Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
|
||||
const id = 'before-after-' + random4() + random4();
|
||||
|
||||
let img;
|
||||
let imgOffset = null;
|
||||
let sliding = false;
|
||||
let figure;
|
||||
let beforeOverlayWidth = 0;
|
||||
let isFocused = false;
|
||||
let containerWidth;
|
||||
|
||||
$: containerHeight = (containerWidth && heightRatio) ? containerWidth * heightRatio : height;
|
||||
|
||||
const onFocus = () => (isFocused = true);
|
||||
const onBlur = () => (isFocused = false);
|
||||
const handleKeyDown = (e) => {
|
||||
if (!isFocused) return;
|
||||
const { keyCode } = e;
|
||||
const margin = handleMargin / w;
|
||||
if (keyCode === 39) {
|
||||
offset = Math.min(1 - margin, offset + keyPressStep);
|
||||
} else if (keyCode === 37) {
|
||||
offset = Math.max(0 + margin, offset - keyPressStep);
|
||||
}
|
||||
};
|
||||
|
||||
const measureImage = () => {
|
||||
if (img && img.complete) imgOffset = img.getBoundingClientRect();
|
||||
};
|
||||
|
||||
const resize = () => {
|
||||
measureImage();
|
||||
};
|
||||
|
||||
const measureLoadedImage = (e) => {
|
||||
if (e.type === 'load') {
|
||||
imgOffset = e.target.getBoundingClientRect();
|
||||
}
|
||||
};
|
||||
|
||||
const move = (e) => {
|
||||
if (sliding && imgOffset) {
|
||||
const el = e.touches ? e.touches[0] : e;
|
||||
const figureOffset = figure
|
||||
? parseInt(window.getComputedStyle(figure).marginLeft.slice(0, -2))
|
||||
: 0;
|
||||
let x = el.pageX - figureOffset - imgOffset.left;
|
||||
x =
|
||||
x < handleMargin
|
||||
? handleMargin
|
||||
: x > w - handleMargin
|
||||
? w - handleMargin
|
||||
: x;
|
||||
offset = x / w;
|
||||
}
|
||||
};
|
||||
const start = (e) => {
|
||||
sliding = true;
|
||||
move(e);
|
||||
};
|
||||
const end = () => {
|
||||
sliding = false;
|
||||
};
|
||||
|
||||
$: w = (imgOffset && imgOffset.width) || 0;
|
||||
$: x = w * offset;
|
||||
$: figStyle = `width:100%;height:${containerHeight}px;`;
|
||||
$: imgStyle = 'width:100%;height:100%;';
|
||||
$: beforeOverlayClip =
|
||||
x < beforeOverlayWidth ? Math.abs(x - beforeOverlayWidth) : 0;
|
||||
|
||||
if (!(beforeSrc && beforeAlt && afterSrc && afterAlt)) {
|
||||
console.warn('Missing required src or alt props for BeforeAfter component');
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
// This is necessary b/c on:load doesn't reliably fire on the image...
|
||||
const interval = setInterval(() => {
|
||||
if (imgOffset) clearInterval(interval);
|
||||
if (img && img.complete && !imgOffset) measureImage();
|
||||
}, 50);
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:touchmove="{move}"
|
||||
on:touchend="{end}"
|
||||
on:mousemove="{move}"
|
||||
on:mouseup="{end}"
|
||||
on:resize="{throttle(resize, 100)}"
|
||||
on:keydown="{handleKeyDown}"
|
||||
/>
|
||||
|
||||
{#if beforeSrc && beforeAlt && afterSrc && afterAlt}
|
||||
<section
|
||||
class="photo before-after {width}"
|
||||
style="height: {containerHeight}px;"
|
||||
bind:clientWidth="{containerWidth}"
|
||||
>
|
||||
<figure
|
||||
style="{figStyle}"
|
||||
class="before-after-container"
|
||||
on:touchstart="{start}"
|
||||
on:mousedown="{start}"
|
||||
bind:this="{figure}"
|
||||
aria-labelledby="{$$slots.caption && `${id}-caption`}"
|
||||
>
|
||||
<img
|
||||
bind:this="{img}"
|
||||
src="{afterSrc}"
|
||||
alt="{afterAlt}"
|
||||
on:load="{measureLoadedImage}"
|
||||
on:mousedown|preventDefault
|
||||
style="{imgStyle}"
|
||||
class="after"
|
||||
aria-describedby="{$$slots.beforeOverlay && `${id}-before`}"
|
||||
/>
|
||||
|
||||
<img
|
||||
src="{beforeSrc}"
|
||||
alt="{beforeAlt}"
|
||||
on:mousedown|preventDefault
|
||||
style="clip: rect(0 {x}px {containerHeight}px 0);{imgStyle}"
|
||||
class="before"
|
||||
aria-describedby="{$$slots.afterOverlay && `${id}-after`}"
|
||||
/>
|
||||
{#if $$slots.beforeOverlay}
|
||||
<div
|
||||
id="image-before-label"
|
||||
class="overlay-container before"
|
||||
bind:clientWidth="{beforeOverlayWidth}"
|
||||
style="clip-path: inset(0 {beforeOverlayClip}px 0 0);"
|
||||
>
|
||||
<!-- Overlay for before image -->
|
||||
<slot
|
||||
name="beforeOverlay"
|
||||
description="{`${id}-before-description`}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots.afterOverlay}
|
||||
<div id="image-after-label" class="overlay-container after">
|
||||
<!-- Overlay for after image -->
|
||||
<slot name="afterOverlay" description="{`${id}-after-description`}" />
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
tabindex="0"
|
||||
class="handle"
|
||||
style="left: calc({offset *
|
||||
100}% - 20px); --before-after-handle-colour: {handleColour}; --before-after-handle-inactive-opacity: {handleInactiveOpacity};"
|
||||
on:focus="{onFocus}"
|
||||
on:blur="{onBlur}"
|
||||
>
|
||||
<div class="arrow-left"></div>
|
||||
<div class="arrow-right"></div>
|
||||
</div>
|
||||
</figure>
|
||||
</section>
|
||||
{#if $$slots.caption}
|
||||
<section class="graphic caption {width}" id="{`${id}-caption`}">
|
||||
<!-- Caption for image credits -->
|
||||
<slot name="caption" />
|
||||
</section>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
@import "@reuters-graphics/style-main/scss/fonts/mixins";
|
||||
figure.before-after-container {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
box-sizing: content-box;
|
||||
margin: 0 auto;
|
||||
|
||||
img {
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 20;
|
||||
&.after {
|
||||
z-index: 21;
|
||||
}
|
||||
&.before {
|
||||
z-index: 22;
|
||||
}
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
user-select: none;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
}
|
||||
.overlay-container {
|
||||
position: absolute;
|
||||
:global {
|
||||
p {
|
||||
@include font-display;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2rem;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.before {
|
||||
left: 0;
|
||||
z-index: 23;
|
||||
}
|
||||
&.after {
|
||||
right: 0;
|
||||
z-index: 21;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
z-index: 30;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: move;
|
||||
background: none;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
border-radius: 50px;
|
||||
top: calc(50% - 20px);
|
||||
border: 4px solid var(--before-after-handle-colour);
|
||||
opacity: var(--before-after-handle-inactive-opacity, 0.6);
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
height: 9999px;
|
||||
position: absolute;
|
||||
left: calc(50% - 2px);
|
||||
border: 2px solid var(--before-after-handle-colour);
|
||||
}
|
||||
&:before {
|
||||
top: 40px;
|
||||
}
|
||||
&:after {
|
||||
bottom: 40px;
|
||||
}
|
||||
.arrow-right,
|
||||
.arrow-left {
|
||||
width: 0;
|
||||
height: 0;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
border-top: 10px solid transparent;
|
||||
border-bottom: 10px solid transparent;
|
||||
}
|
||||
.arrow-right {
|
||||
left: 19px;
|
||||
bottom: 14px;
|
||||
border-left: 10px solid var(--before-after-handle-colour);
|
||||
}
|
||||
.arrow-left {
|
||||
left: 3px;
|
||||
top: 6px;
|
||||
border-right: 10px solid var(--before-after-handle-colour);
|
||||
}
|
||||
}
|
||||
section.graphic.caption {
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
29
src/components/BeforeAfter/stories/docs/ariaDescriptions.md
Normal file
29
src/components/BeforeAfter/stories/docs/ariaDescriptions.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
Use text elements in your overlays as [ARIA descriptions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby) for your images by setting an ID on text elements within each overly with the `description` [slot prop](https://svelte.dev/tutorial/slot-props).
|
||||
|
||||
> **💡 TIP:** You must always use the `beforeAlt` / `afterAlt` props to label your image for visually impaired readers, but you can use these descriptions to provide more information or context that the reader might also need.
|
||||
|
||||
```svelte
|
||||
<BeforeAfter
|
||||
beforeSrc="{`${assets}/images/before-after/myrne-before.jpg`}"
|
||||
beforeAlt="Satellite image of Russian base at Myrne taken on July 7, 2020."
|
||||
afterSrc="{`${assets}/images/before-after/myrne-after.jpg`}"
|
||||
afterAlt="Satellite image of Russian base at Myrne taken on Oct. 20, 2020."
|
||||
>
|
||||
<!-- 👇 Define the prop on each slot... -->
|
||||
<div let:description="{id}" slot="beforeOverlay">
|
||||
<p id="{id}">
|
||||
On July 7, 2020, the base contained only a few transport vehicles.
|
||||
</p>
|
||||
</div>
|
||||
<div let:description="{id}" slot="afterOverlay">
|
||||
<!-- 👇 id can also be used on an element containing multiple text elements -->
|
||||
<div id="{id}">
|
||||
<p>But by October, tanks and artillery could be seen.</p>
|
||||
<p>
|
||||
In total, over 50 pieces of heavy machinery and 200 personnel are now
|
||||
based here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</BeforeAfter>
|
||||
```
|
||||
15
src/components/BeforeAfter/stories/docs/component.md
Normal file
15
src/components/BeforeAfter/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
A before and after image comparison component.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { BeforeAfter } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths'; // If using in the Graphics Kit
|
||||
</script>
|
||||
|
||||
<BeforeAfter
|
||||
beforeSrc="{`${assets}/images/before-after/myrne-before.jpg`}"
|
||||
beforeAlt="Satellite image of Russian base at Myrne taken on July 7, 2020."
|
||||
afterSrc="{`${assets}/images/before-after/myrne-after.jpg`}"
|
||||
afterAlt="Satellite image of Russian base at Myrne taken on Oct. 20, 2020."
|
||||
/>
|
||||
```
|
||||
34
src/components/BeforeAfter/stories/docs/withOverlays.md
Normal file
34
src/components/BeforeAfter/stories/docs/withOverlays.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
Add overlays with the `beforeOverlay` and `afterOverlay` slots and a caption to the `caption` slot, then style these elements to match your page design as below.
|
||||
|
||||
```svelte
|
||||
<BeforeAfter
|
||||
beforeSrc="{`${assets}/images/before-after/myrne-before.jpg`}"
|
||||
beforeAlt="Satellite image of Russian base at Myrne taken on July 7, 2020."
|
||||
afterSrc="{`${assets}/images/before-after/myrne-after.jpg`}"
|
||||
afterAlt="Satellite image of Russian base at Myrne taken on Oct. 20, 2020."
|
||||
>
|
||||
<div slot="beforeOverlay" class="overlay before">
|
||||
<h6>July 7, 2020</h6>
|
||||
<p>Initially, this site was far smaller.</p>
|
||||
</div>
|
||||
<div slot="afterOverlay" class="overlay after">
|
||||
<h6>Oct. 20, 2020</h6>
|
||||
<p>But then forces built up.</p>
|
||||
</div>
|
||||
</BeforeAfter>
|
||||
|
||||
<style lang="scss">
|
||||
.overlay {
|
||||
color: white;
|
||||
padding: 15px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
h6, p {
|
||||
color: white;
|
||||
margin: 0;
|
||||
}
|
||||
&.after {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
BIN
src/components/BeforeAfter/stories/myrne-after.jpg
Normal file
BIN
src/components/BeforeAfter/stories/myrne-after.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 715 KiB |
BIN
src/components/BeforeAfter/stories/myrne-before.jpg
Normal file
BIN
src/components/BeforeAfter/stories/myrne-before.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 472 KiB |
35
src/components/BodyText/BodyText.stories.svelte
Normal file
35
src/components/BodyText/BodyText.stories.svelte
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import BodyText from './BodyText.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/BodyText',
|
||||
component: BodyText,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<BodyText {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
text: `Bacon ipsum **dolor amet** cow tongue tri-tip.
|
||||
|
||||
Biltong turducken ground round kevin [hamburger turkey](https://reuters.com) pig.
|
||||
|
||||
Venison shoulder *ham hock ham leberkas*. Flank beef ribs fatback, jerky meatball ham hock.`
|
||||
}}
|
||||
/>
|
||||
17
src/components/BodyText/BodyText.svelte
Normal file
17
src/components/BodyText/BodyText.svelte
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* A markdown text string.
|
||||
* @type {string}
|
||||
* @required
|
||||
*/
|
||||
export let text: string;
|
||||
|
||||
import { marked } from 'marked';
|
||||
import Section from '../Section/Section.svelte';
|
||||
</script>
|
||||
|
||||
<Section cls="body-text">
|
||||
{#if text}
|
||||
{@html marked.parse(text)}
|
||||
{/if}
|
||||
</Section>
|
||||
15
src/components/BodyText/stories/docs/component.md
Normal file
15
src/components/BodyText/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
Parse mardown-formatted text.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { BodyText } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
const markdownText = `Bacon ipsum **dolor amet** cow tongue tri-tip.
|
||||
|
||||
Biltong turducken ground round kevin [hamburger turkey](https://reuters.com) pig.
|
||||
|
||||
Venison shoulder *ham hock ham leberkas*. Flank beef ribs fatback, jerky meatball ham hock.`;
|
||||
</script>
|
||||
|
||||
<BodyText text="{markdownText}" />
|
||||
```
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore
|
||||
import withChatterDocs from './stories/docs/withChatter.md?raw';
|
||||
|
||||
import DatawrapperChart from './DatawrapperChart.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/DatawrapperChart',
|
||||
component: DatawrapperChart,
|
||||
...withComponentDocs(componentDocs),
|
||||
argTypes: {
|
||||
width: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<DatawrapperChart {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
src: 'https://graphics.reuters.com/USA-ABORTION/lgpdwggnwvo/media-embed.html',
|
||||
id: 'abortion-rights-map',
|
||||
ariaLabel: 'map',
|
||||
title: 'Global abortion access',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story name="With chatter" {...withStoryDocs(withChatterDocs)}>
|
||||
<DatawrapperChart
|
||||
title='Global abortion access'
|
||||
ariaLabel='map'
|
||||
id='abortion-rights-map'
|
||||
src='https://graphics.reuters.com/USA-ABORTION/lgvdwemlbpo/media-embed.html'
|
||||
>
|
||||
<div slot="title" class="title">
|
||||
<h3>Global abortion access</h3>
|
||||
<p>A map of worldwide access to abortion.</p>
|
||||
</div>
|
||||
<aside slot="notes">
|
||||
<p class="note">Note: Different indicators and additional restrictions, including different gestational limits, apply in some countries. Refer to source for full classification. Current as of May 4, 2022.</p>
|
||||
<p class="source">Source: Center for Reproductive Rights</p>
|
||||
</aside>
|
||||
</DatawrapperChart>
|
||||
</Story>
|
||||
83
src/components/DatawrapperChart/DatawrapperChart.svelte
Normal file
83
src/components/DatawrapperChart/DatawrapperChart.svelte
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
/**
|
||||
* iFrame title
|
||||
* @required
|
||||
*/
|
||||
export let title: string = '';
|
||||
/**
|
||||
* iFrame aria label
|
||||
* @required
|
||||
*/
|
||||
export let ariaLabel: string = '';
|
||||
/** iFrame id */
|
||||
export let id: string = '';
|
||||
/**
|
||||
* Datawrapper embed URL
|
||||
* @required
|
||||
*/
|
||||
export let src: string;
|
||||
|
||||
type ScrollingOption = 'auto' | 'yes' | 'no';
|
||||
|
||||
/** iFrame scrolling option */
|
||||
export let scrolling: ScrollingOption = 'no';
|
||||
|
||||
type ContainerWidth = 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
|
||||
/** Width of the chart within the text well. */
|
||||
export let width: ContainerWidth = 'normal'; // options: wide, wider, widest, fluid
|
||||
|
||||
onMount(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('message', function (e) {
|
||||
if (void 0 !== e.data['datawrapper-height']) {
|
||||
const t = document.querySelectorAll('iframe');
|
||||
for (const a in e.data['datawrapper-height']) {
|
||||
for (let r = 0; r < t.length; r++) {
|
||||
if (t[r].contentWindow === e.source) {
|
||||
t[r].style.height = e.data['datawrapper-height'][a] + 'px';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<section class="graphic {width}">
|
||||
{#if $$slots.title}
|
||||
<div class="chatter-container">
|
||||
<!-- Custom headline and chatter slot -->
|
||||
<slot name="title" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="datawrapper-chart">
|
||||
<iframe
|
||||
title="{title}"
|
||||
aria-label="{ariaLabel}"
|
||||
id="{id}"
|
||||
src="{src}"
|
||||
scrolling="{scrolling}"
|
||||
frameborder="0"
|
||||
style="width: 0; min-width: 100% !important; border: none;"></iframe>
|
||||
</div>
|
||||
|
||||
{#if $$slots.notes}
|
||||
<div class="chatter-container">
|
||||
<!-- Custom notes and source slot -->
|
||||
<slot name="notes" />
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@reuters-graphics/style-theme-eisbaer/scss/components/containers/widths';
|
||||
.chatter-container {
|
||||
@extend .well;
|
||||
}
|
||||
.datawrapper-chart {
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
25
src/components/DatawrapperChart/stories/docs/component.md
Normal file
25
src/components/DatawrapperChart/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Easily add a responsive Datawrapper embed on your page.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { DatawrapperChart } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<DatawrapperChart
|
||||
title='Global abortion access'
|
||||
ariaLabel='map'
|
||||
id='abortion-rights-map'
|
||||
src='https://graphics.reuters.com/USA-ABORTION/lgpdwggnwvo/media-embed.html'
|
||||
/>
|
||||
```
|
||||
|
||||
##### Getting the chart URL for `src`
|
||||
|
||||
Copy the source url for the Datawrapper chart in the `src` prop.
|
||||
You can get this from the published url on Reuters Graphics.
|
||||
|
||||
- Publish the chart on Datawrapper.
|
||||
- Go to the **Datawrapper charts** Teams channel, wait for the graphic to finish publishing.
|
||||
- Inside **Embed code (for developers only)**, find and copy the url inside the `src` prop. (It ends in `media-embed.html`.)
|
||||
|
||||
**Note:** There is no need to update the url if you update the chart inside Datawrapper. Any changes will be automatically reflected.
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
By default, Datawrapper will export your chart with the title, chatter and notes. At the moment, these
|
||||
don't match our styles, can't be made to fit into the well and will necessarily stretch to fit the graph width and
|
||||
can't be modified. You can do all of these things by removing all the text from your Datawrapper chart before
|
||||
publishing it and instead adding whatever you need inside the component via slots as below.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import EmbedPreviewerLink from './EmbedPreviewerLink.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Utilities/EmbedPreviewerLink',
|
||||
component: EmbedPreviewerLink,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<EmbedPreviewerLink {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
dev: true
|
||||
}}
|
||||
/>
|
||||
30
src/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte
Normal file
30
src/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<script lang="ts">
|
||||
export let dev: boolean = false;
|
||||
|
||||
import Fa from 'svelte-fa/src/fa.svelte';
|
||||
import { faWindowRestore } from '@fortawesome/free-regular-svg-icons';
|
||||
</script>
|
||||
|
||||
{#if dev}
|
||||
<div>
|
||||
<a rel="external" href="/embed-previewer">
|
||||
<Fa icon={faWindowRestore} />
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
position: fixed;
|
||||
bottom: 5px;
|
||||
left: 10px;
|
||||
font-size: 18px;
|
||||
a {
|
||||
color: #ccc;
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
11
src/components/EmbedPreviewerLink/stories/docs/component.md
Normal file
11
src/components/EmbedPreviewerLink/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
An embed tool for development in graphics kit.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { EmbedPreviewerLink } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
import { dev } from '$app/env';
|
||||
</script>
|
||||
|
||||
<EmbedPreviewerLink dev={dev} />
|
||||
```
|
||||
37
src/components/EndNotes/EndNotes.stories.svelte
Normal file
37
src/components/EndNotes/EndNotes.stories.svelte
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import EndNotes from './EndNotes.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/EndNotes',
|
||||
component: EndNotes,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<EndNotes {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
text: `### Source
|
||||
|
||||
Reuters research.
|
||||
|
||||
### Credits
|
||||
|
||||
People.`
|
||||
}}
|
||||
/>
|
||||
16
src/components/EndNotes/EndNotes.svelte
Normal file
16
src/components/EndNotes/EndNotes.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* A markdown text string.
|
||||
* @type {string}
|
||||
* @required
|
||||
*/
|
||||
export let text: string;
|
||||
|
||||
import { marked } from 'marked';
|
||||
</script>
|
||||
|
||||
<section class="end-notes">
|
||||
{#if text}
|
||||
{@html marked.parse(text)}
|
||||
{/if}
|
||||
</section>
|
||||
17
src/components/EndNotes/stories/docs/component.md
Normal file
17
src/components/EndNotes/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
End notes section.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { EndNotes } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
const markdownText = `### Source
|
||||
|
||||
Reuters research.
|
||||
|
||||
### Credits
|
||||
|
||||
People.`;
|
||||
</script>
|
||||
|
||||
<EndNotes text="{markdownText}" />
|
||||
```
|
||||
56
src/components/FeaturePhoto/FeaturePhoto.stories.svelte
Normal file
56
src/components/FeaturePhoto/FeaturePhoto.stories.svelte
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore
|
||||
import missingAltTextDocs from './stories/docs/missingAltText.md?raw';
|
||||
|
||||
import FeaturePhoto from './FeaturePhoto.svelte';
|
||||
|
||||
// @ts-ignore
|
||||
import sharkSrc from './stories/shark.jpg';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/FeaturePhoto',
|
||||
component: FeaturePhoto,
|
||||
...withComponentDocs(componentDocs),
|
||||
argTypes: {
|
||||
width: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<FeaturePhoto {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
src: sharkSrc,
|
||||
altText: 'A shark!',
|
||||
width: 'normal',
|
||||
caption: 'Carcharodon carcharias - REUTERS'
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story
|
||||
name="Missing altText"
|
||||
args={{
|
||||
src: sharkSrc,
|
||||
width: 'normal',
|
||||
caption: 'Carcharodon carcharias - REUTERS'
|
||||
}}
|
||||
{...withStoryDocs(missingAltTextDocs)}
|
||||
/>
|
||||
115
src/components/FeaturePhoto/FeaturePhoto.svelte
Normal file
115
src/components/FeaturePhoto/FeaturePhoto.svelte
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
/**
|
||||
* Photo src
|
||||
* @type {string}
|
||||
* @required
|
||||
*/
|
||||
export let src: string;
|
||||
/**
|
||||
* Photo altText
|
||||
* @type {string}
|
||||
* @required
|
||||
*/
|
||||
export let altText: string;
|
||||
/**
|
||||
* Caption below the photo
|
||||
* @type {string}
|
||||
*/
|
||||
export let caption: string;
|
||||
/**
|
||||
* Height of the photo placeholder when lazy-loading
|
||||
*/
|
||||
export let height: number = 100;
|
||||
|
||||
type ContainerWidth = 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
|
||||
|
||||
/**
|
||||
* Width of the container, one of: normal, wide, wider, widest or fluid
|
||||
*/
|
||||
export let width: ContainerWidth = 'normal';
|
||||
|
||||
/**
|
||||
* Whether to lazy load the photo using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
|
||||
*/
|
||||
export let lazy: boolean = false;
|
||||
|
||||
/** Set Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin) `top` when lazy loading. */
|
||||
export let top = 0;
|
||||
/** Set Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin) `bottom` when lazy loading. */
|
||||
export let bottom = 0;
|
||||
/** Set Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin) `left` when lazy loading. */
|
||||
export let left = 0;
|
||||
/** Set Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin) `right` when lazy loading. */
|
||||
export let right = 0;
|
||||
|
||||
let intersecting = false;
|
||||
let container;
|
||||
const intersectable = typeof IntersectionObserver !== 'undefined';
|
||||
|
||||
onMount(() => {
|
||||
if (!lazy) return;
|
||||
if (intersectable) {
|
||||
const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
intersecting = entries[0].isIntersecting;
|
||||
if (intersecting) {
|
||||
observer.unobserve(container);
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin,
|
||||
}
|
||||
);
|
||||
|
||||
observer.observe(container);
|
||||
return () => observer.unobserve(container);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<figure
|
||||
class="photo { width }"
|
||||
bind:this="{container}"
|
||||
>
|
||||
{#if !lazy || (intersectable && intersecting)}
|
||||
<img src="{src}" alt="{altText}" />
|
||||
{:else}
|
||||
<div class="placeholder" height="{`${height}px`}"></div>
|
||||
{/if}
|
||||
{#if caption}
|
||||
<figcaption>{caption}</figcaption>
|
||||
{/if}
|
||||
{#if (!altText)}
|
||||
<div class='alt-warning'>Missing altText</div>
|
||||
{/if}
|
||||
</figure>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@reuters-graphics/style-main/scss/fonts/mixins";
|
||||
figure {
|
||||
position: relative;
|
||||
div.alt-warning {
|
||||
@include font-display;
|
||||
padding: 5px 10px;
|
||||
background-color: red;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
.placeholder {
|
||||
background-color: #ccc;
|
||||
width: 100%;
|
||||
}
|
||||
figcaption {
|
||||
@include font-display;
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
16
src/components/FeaturePhoto/stories/docs/component.md
Normal file
16
src/components/FeaturePhoto/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
A full-width photo inside the text well.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { FeaturePhoto } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths'; // 👈 If using in the Graphics Kit...
|
||||
</script>
|
||||
|
||||
<FeaturePhoto
|
||||
src="{`${assets}/images/myImage.jpg`}"
|
||||
alt="Some alt text"
|
||||
caption="A caption"
|
||||
lazy="{false}"
|
||||
width="normal"
|
||||
/>
|
||||
```
|
||||
|
|
@ -0,0 +1 @@
|
|||
If your photo is missing `altText` a small warning will overlay the image.
|
||||
BIN
src/components/FeaturePhoto/stories/shark.jpg
Normal file
BIN
src/components/FeaturePhoto/stories/shark.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
33
src/components/Framer/Framer.stories.svelte
Normal file
33
src/components/Framer/Framer.stories.svelte
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import Framer from './Framer.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Utilities/Framer',
|
||||
component: Framer,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<Framer {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
embeds: [
|
||||
'https://graphics.reuters.com/USA-CONGRESS/FUNDRAISING/zjvqkawjlvx/embeds/en/embed/?zzz',
|
||||
],
|
||||
}}
|
||||
/>
|
||||
142
src/components/Framer/Framer.svelte
Normal file
142
src/components/Framer/Framer.svelte
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
<script>
|
||||
import Fa from 'svelte-fa/src/fa.svelte';
|
||||
import { faDesktop, faLink } from '@fortawesome/free-solid-svg-icons';
|
||||
import { onMount, afterUpdate } from 'svelte';
|
||||
import pym from 'pym.js';
|
||||
import urljoin from 'proper-url-join';
|
||||
import Resizer from './Resizer/index.svelte';
|
||||
import { width } from './stores.js';
|
||||
import getUniqNames from './uniqNames.js';
|
||||
|
||||
export let embeds;
|
||||
export let breakpoints = [330, 510, 660, 930, 1200];
|
||||
export let minFrameWidth = 320;
|
||||
export let maxFrameWidth = 1200;
|
||||
|
||||
let activeEmbed = embeds[0];
|
||||
|
||||
$: embedTitles = getUniqNames(embeds);
|
||||
|
||||
// @ts-ignore
|
||||
let pymParent;
|
||||
|
||||
const reframe = (embed) => {
|
||||
pymParent = new pym.Parent(
|
||||
'frame-parent',
|
||||
/^http/.test(embed)
|
||||
? embed
|
||||
: urljoin(window.location.origin, embed, { trailingSlash: true })
|
||||
);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
reframe(activeEmbed);
|
||||
});
|
||||
|
||||
afterUpdate(() => {
|
||||
reframe(activeEmbed);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<header>
|
||||
<img
|
||||
src="https://graphics.thomsonreuters.com/style-assets/images/logos/reuters-graphics-logo/svg/graphics-logo-dark.svg"
|
||||
alt=""
|
||||
/>
|
||||
</header>
|
||||
|
||||
<nav>
|
||||
{#each embeds as embed, i}
|
||||
<button
|
||||
on:click="{() => {
|
||||
activeEmbed = embed;
|
||||
}}"
|
||||
class:active="{activeEmbed === embed}"
|
||||
>
|
||||
{embedTitles[i]}
|
||||
<a rel="external" target="_blank" href="{embed}" title="{embed}">
|
||||
<Fa icon="{faLink}" />
|
||||
</a>
|
||||
</button>
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<div id="frame-parent" style="width:{$width}px;"></div>
|
||||
</div>
|
||||
|
||||
<div id="home-link">
|
||||
<a rel="external" href="./../">
|
||||
<Fa icon="{faDesktop}" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Resizer
|
||||
breakpoints="{breakpoints}"
|
||||
minFrameWidth="{minFrameWidth}"
|
||||
maxFrameWidth="{maxFrameWidth}"
|
||||
/>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@reuters-graphics/style-color/scss/thematic/brand';
|
||||
@import '@reuters-graphics/style-main/scss/fonts/mixins';
|
||||
|
||||
header {
|
||||
@include font-display;
|
||||
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: center;
|
||||
margin: 0 auto 20px;
|
||||
max-width: 900px;
|
||||
button {
|
||||
margin: 0 4px 5px;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
color: #999;
|
||||
padding: 2px 2px;
|
||||
cursor: pointer;
|
||||
@include font-display;
|
||||
font-weight: 400;
|
||||
&.active {
|
||||
border-bottom: 2px solid #666;
|
||||
color: #666;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
a {
|
||||
color: #bbb;
|
||||
font-size: 12px;
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#frame-parent {
|
||||
border: 1px solid #ddd;
|
||||
margin: 0 auto;
|
||||
width: var(--width);
|
||||
}
|
||||
|
||||
div#home-link {
|
||||
position: fixed;
|
||||
bottom: 5px;
|
||||
left: 10px;
|
||||
font-size: 18px;
|
||||
a {
|
||||
color: #ccc;
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
228
src/components/Framer/Resizer/index.svelte
Normal file
228
src/components/Framer/Resizer/index.svelte
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
<script>
|
||||
import { faDesktop, faMobileAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import Fa from 'svelte-fa/src/fa.svelte';
|
||||
import { width } from './../stores.js';
|
||||
|
||||
export let breakpoints = [330, 510, 660, 930, 1200];
|
||||
export let maxFrameWidth = 1200;
|
||||
export let minFrameWidth = 320;
|
||||
|
||||
let container;
|
||||
|
||||
const sliderWidth = 90;
|
||||
let windowInnerWidth = 1200;
|
||||
$: minWidth = minFrameWidth;
|
||||
$: maxWidth = Math.min(windowInnerWidth - 70, maxFrameWidth);
|
||||
$: pixelRange = maxWidth - minWidth;
|
||||
$: if ($width > maxWidth) width.set(maxWidth);
|
||||
$: offset = ($width - minWidth) / pixelRange;
|
||||
|
||||
let sliding = false;
|
||||
let isFocused = false;
|
||||
|
||||
const roundToNearestFive = (d) => Math.ceil(d / 5) * 5;
|
||||
const getPx = () => Math.round(pixelRange * offset + minWidth);
|
||||
|
||||
let pixelLabel = null;
|
||||
|
||||
const move = (e) => {
|
||||
if (!sliding || !container) return;
|
||||
const { left } = container.getBoundingClientRect();
|
||||
offset = Math.min(Math.max(0, e.pageX - left), sliderWidth) / sliderWidth;
|
||||
pixelLabel = roundToNearestFive(getPx());
|
||||
};
|
||||
const handleKeyDown = (e) => {
|
||||
if (!isFocused) return;
|
||||
const { keyCode } = e;
|
||||
const pixelWidth = sliderWidth / pixelRange;
|
||||
// right
|
||||
if (keyCode === 39) {
|
||||
offset = Math.min(1, offset + pixelWidth / sliderWidth);
|
||||
// left
|
||||
} else if (keyCode === 37) {
|
||||
offset = Math.max(0, offset - pixelWidth / sliderWidth);
|
||||
}
|
||||
width.set(getPx());
|
||||
};
|
||||
const start = (e) => {
|
||||
sliding = true;
|
||||
move(e);
|
||||
};
|
||||
const end = () => {
|
||||
sliding = false;
|
||||
pixelLabel = null;
|
||||
width.set(roundToNearestFive(getPx()));
|
||||
};
|
||||
const onFocus = () => {
|
||||
isFocused = true;
|
||||
};
|
||||
const onBlur = () => {
|
||||
isFocused = false;
|
||||
};
|
||||
const increment = () => {
|
||||
const availableBreakpoints = breakpoints
|
||||
.filter((b) => b <= maxWidth)
|
||||
.filter((b) => b > $width);
|
||||
if (availableBreakpoints.length === 0) {
|
||||
width.set(maxWidth);
|
||||
} else {
|
||||
width.set(availableBreakpoints[0]);
|
||||
}
|
||||
};
|
||||
const decrement = () => {
|
||||
const availableBreakpoints = breakpoints.filter((b) => b < $width);
|
||||
if (availableBreakpoints.length === 0) {
|
||||
width.set(minWidth);
|
||||
} else {
|
||||
width.set(availableBreakpoints.slice(-1)[0]);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:mousemove="{move}"
|
||||
on:mouseup="{end}"
|
||||
on:keydown="{handleKeyDown}"
|
||||
bind:innerWidth="{windowInnerWidth}"
|
||||
/>
|
||||
|
||||
<div id="resizer">
|
||||
<div class="slider">
|
||||
<div class="label" style="{`opacity: ${sliding || isFocused ? 1 : 0};`}">
|
||||
{pixelLabel || $width}px
|
||||
</div>
|
||||
<button
|
||||
class="icon left"
|
||||
disabled="{$width === minWidth}"
|
||||
on:click="{decrement}"
|
||||
on:focus="{onFocus}"
|
||||
on:mouseover="{onFocus}"
|
||||
on:mouseleave="{onBlur}"
|
||||
>
|
||||
<Fa icon="{faMobileAlt}" fw />
|
||||
</button>
|
||||
<div class="slider-container" bind:this="{container}">
|
||||
<div class="track"></div>
|
||||
<div
|
||||
class="handle"
|
||||
tabindex="0"
|
||||
style="left: calc({offset * 100}% - 5px);"
|
||||
on:mousedown="{start}"
|
||||
on:focus="{onFocus}"
|
||||
on:blur="{onBlur}"
|
||||
></div>
|
||||
</div>
|
||||
<button
|
||||
class="icon right"
|
||||
disabled="{$width === maxWidth}"
|
||||
on:click="{increment}"
|
||||
on:focus="{onFocus}"
|
||||
on:mouseover="{onFocus}"
|
||||
on:mouseleave="{onBlur}"
|
||||
>
|
||||
<Fa icon="{faDesktop}" fw />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
#resizer {
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
padding: 15px;
|
||||
|
||||
.slider {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
& > div,
|
||||
button {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
div.label {
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
text-align: center;
|
||||
transition: opacity 0.2s;
|
||||
color: grey;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
button.icon {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
color: #bbb;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
&:active,
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
&:hover {
|
||||
color: #999;
|
||||
}
|
||||
&:active {
|
||||
transform: translate(1px, 1px);
|
||||
}
|
||||
&[disabled] {
|
||||
color: #ccc;
|
||||
cursor: default;
|
||||
&:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
&:active {
|
||||
transform: translate(0px, 0px);
|
||||
}
|
||||
}
|
||||
&.left {
|
||||
text-align: right;
|
||||
padding-right: 3px;
|
||||
}
|
||||
&.right {
|
||||
padding-left: 6px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
div.slider-container {
|
||||
width: 90px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
|
||||
div.track {
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
border-radius: 2px;
|
||||
top: calc(50% - 2px);
|
||||
background-color: lightgrey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.handle {
|
||||
z-index: 30;
|
||||
width: 10px;
|
||||
height: 20px;
|
||||
cursor: ew-resize;
|
||||
background: #bbb;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
border: 1px solid grey;
|
||||
top: calc(50% - 10px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
||||
&:active,
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3
src/components/Framer/stores.js
Normal file
3
src/components/Framer/stores.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { writable } from 'svelte/store';
|
||||
|
||||
export const width = writable(660);
|
||||
11
src/components/Framer/stories/docs/component.md
Normal file
11
src/components/Framer/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
An embed tool for development in the Graphics Kit.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Framer } from '@reuters-graphics/graphics-svelte-components';
|
||||
|
||||
const embeds = ['/embeds/my-chart/index.html'];
|
||||
</script>
|
||||
|
||||
<Framer embeds={embeds} />
|
||||
```
|
||||
54
src/components/Framer/uniqNames.js
Normal file
54
src/components/Framer/uniqNames.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
export default (embeds) => {
|
||||
const nakedEmbeds = embeds
|
||||
.map((e) => e.replace(/\?.+$/, ''))
|
||||
.map((e) => e.replace(/index\.html$/, ''))
|
||||
.map((e) => e.replace(/^http[s]*:\/\/[\w.]+\.com/, ''));
|
||||
|
||||
// If just one, get the last path part
|
||||
if (nakedEmbeds.length === 1) {
|
||||
return [
|
||||
nakedEmbeds[0]
|
||||
.split('/')
|
||||
.filter((d) => d)
|
||||
.slice(-1)[0],
|
||||
];
|
||||
}
|
||||
|
||||
// If many, test each path part for unique-ness
|
||||
const test = nakedEmbeds[0];
|
||||
let replacementForward = 0;
|
||||
for (const i in test.split('/')) {
|
||||
const pathPart = test.split('/')[i];
|
||||
const notUniq = nakedEmbeds.every((e) => e.split('/')[i] === pathPart);
|
||||
if (notUniq) {
|
||||
replacementForward += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacementForward === test.split('/').length) return nakedEmbeds;
|
||||
|
||||
let replacementBackward = 0;
|
||||
for (const i in test.split('/').reverse()) {
|
||||
const pathPart = test.split('/').reverse()[i];
|
||||
const notUniq = nakedEmbeds.every(
|
||||
(e) => e.split('/').reverse()[i] === pathPart
|
||||
);
|
||||
if (notUniq) {
|
||||
replacementBackward += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nakedEmbeds.map((e) => {
|
||||
if (replacementBackward > 0) {
|
||||
return e
|
||||
.split('/')
|
||||
.slice(replacementForward, replacementBackward * -1)
|
||||
.join('/');
|
||||
}
|
||||
return e.split('/').slice(replacementForward).join('/');
|
||||
});
|
||||
};
|
||||
69
src/components/Headline/Headline.stories.svelte
Normal file
69
src/components/Headline/Headline.stories.svelte
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore
|
||||
import withBylineDocs from './stories/docs/withByline.md?raw';
|
||||
// @ts-ignore
|
||||
import withCrownDocs from './stories/docs/withCrown.md?raw';
|
||||
// @ts-ignore
|
||||
import crownImgSrc from './stories/crown.png';
|
||||
|
||||
import Headline from './Headline.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Headline',
|
||||
component: Headline,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<Headline {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
section: 'World News',
|
||||
hed: 'Reuters Graphics interactive'
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story
|
||||
name="With byline"
|
||||
{...withStoryDocs(withBylineDocs)}
|
||||
>
|
||||
<Headline
|
||||
hed="{'Reuters Graphics Interactive'}"
|
||||
dek="{'The beginning of a beautiful page'}"
|
||||
section="{'Global news'}"
|
||||
sectionColour="{'orange'}"
|
||||
>
|
||||
<!-- Use named slots to add a byline... -->
|
||||
<span slot="byline">By <strong>Peppa Pig</strong></span>
|
||||
<!-- ...and a dateline. -->
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
</Story>
|
||||
|
||||
<Story
|
||||
name="With crown"
|
||||
{...withStoryDocs(withCrownDocs)}
|
||||
>
|
||||
<Headline>
|
||||
<!-- Add a crown -->
|
||||
<img slot="crown" src="{crownImgSrc}" alt="Illustration of Europe" />
|
||||
<!-- Override the hed with a named slot -->
|
||||
<h2 slot="hed" class="spaced font-serif">Europa</h2>
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
</Story>
|
||||
61
src/components/Headline/Headline.svelte
Normal file
61
src/components/Headline/Headline.svelte
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Headline
|
||||
*/
|
||||
export let hed: string = 'Reuters Graphics Interactive';
|
||||
/**
|
||||
* Dek
|
||||
*/
|
||||
export let dek: string | null = null;
|
||||
/**
|
||||
* Section title
|
||||
*/
|
||||
export let section: string | null = null;
|
||||
/**
|
||||
* Section colour
|
||||
*/
|
||||
export let sectionColour: string = 'red';
|
||||
</script>
|
||||
|
||||
<section class="headline">
|
||||
<!-- Crown named slot -->
|
||||
<slot name="crown"></slot>
|
||||
<div class="title">
|
||||
{#if section}
|
||||
<p class={`section-title color-${sectionColour}`}>{section}</p>
|
||||
{/if}
|
||||
{#if $$slots.hed}
|
||||
<!-- Headline override named slot -->
|
||||
<slot name="hed"></slot>
|
||||
{:else}
|
||||
<h2>{hed}</h2>
|
||||
{/if}
|
||||
{#if dek}
|
||||
<p>{dek}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if ($$slots.byline || $$slots.dateline)}
|
||||
<aside class="article-metadata">
|
||||
{#if $$slots.byline}
|
||||
<div class="byline-container">
|
||||
<div class="byline">
|
||||
<!-- Byline named slot -->
|
||||
<slot name="byline"></slot>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $$slots.dateline}
|
||||
<div class="dateline-container">
|
||||
<div class="published">
|
||||
<!-- Dateline named slot -->
|
||||
<slot name="dateline"></slot>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</aside>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
||||
BIN
src/components/Headline/stories/crown.png
Normal file
BIN
src/components/Headline/stories/crown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
14
src/components/Headline/stories/docs/component.md
Normal file
14
src/components/Headline/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Reuters Graphics headline
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Headline
|
||||
hed="{'Reuters Graphics Interactive'}"
|
||||
dek="{'The beginning of a beautiful page'}"
|
||||
section="{'Global news'}"
|
||||
sectionColour="{'orange'}"
|
||||
/>
|
||||
```
|
||||
19
src/components/Headline/stories/docs/withByline.md
Normal file
19
src/components/Headline/stories/docs/withByline.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Add a byline and dateline with `byline` and `dateline` named slots.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Headline
|
||||
hed="{'Reuters Graphics Interactive'}"
|
||||
dek="{'The beginning of a beautiful page'}"
|
||||
section="{'Global news'}"
|
||||
sectionColour="{'orange'}"
|
||||
>
|
||||
<!-- Use named slots to add a byline... -->
|
||||
<span slot="byline">By <strong>Peppa Pig</strong></span>
|
||||
<!-- ...and a dateline. -->
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
```
|
||||
16
src/components/Headline/stories/docs/withCrown.md
Normal file
16
src/components/Headline/stories/docs/withCrown.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Add a crown image in the `crown` named slot and override the headline in the `hed` named slot.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<Headline>
|
||||
<!-- Add a crown -->
|
||||
<img slot="crown" src="{`${assets}/images/crown.png`}" />
|
||||
<!-- Override the hed with a named slot -->
|
||||
<h2 slot="hed" class="spaced font-serif">Europa</h2>
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
```
|
||||
37
src/components/Hero/Hero.stories.svelte
Normal file
37
src/components/Hero/Hero.stories.svelte
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
// @ts-ignore
|
||||
import polarImgSrc from './stories/polar.jpg';
|
||||
|
||||
import Hero from './Hero.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/Hero',
|
||||
component: Hero,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<Hero {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args={{
|
||||
section: 'World News',
|
||||
hed: 'Reuters Graphics interactive',
|
||||
imgSrc: polarImgSrc,
|
||||
}}
|
||||
/>
|
||||
84
src/components/Hero/Hero.svelte
Normal file
84
src/components/Hero/Hero.svelte
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Headline
|
||||
*/
|
||||
export let hed: string = 'Reuters Graphics Interactive';
|
||||
/**
|
||||
* Hedline colour
|
||||
*/
|
||||
export let hedColour = 'white';
|
||||
/**
|
||||
* Dek
|
||||
*/
|
||||
export let dek: string | null = null;
|
||||
/**
|
||||
* Section title
|
||||
*/
|
||||
export let section: string | null = null;
|
||||
/**
|
||||
* Section colour
|
||||
*/
|
||||
export let sectionColour: string = 'red';
|
||||
|
||||
export let imgSrc: string;
|
||||
export let imgAltText: string;
|
||||
|
||||
|
||||
|
||||
export let overlay: boolean | string = true;
|
||||
|
||||
export let top = false;
|
||||
export let bottom = false;
|
||||
export let left = false;
|
||||
export let right = false;
|
||||
</script>
|
||||
|
||||
|
||||
<section class="hero-title">
|
||||
<figure>
|
||||
{#if $$slots.image}
|
||||
<slot name='image'></slot>
|
||||
{:else}
|
||||
<img src="{imgSrc}" alt="{imgAltText}" />
|
||||
{/if}
|
||||
{#if overlay}
|
||||
<div
|
||||
class="overlay"
|
||||
class:lightest={overlay === 'lightest'}
|
||||
class:lighter={overlay === 'lighter'}
|
||||
class:light={overlay === 'light'}
|
||||
class:dark={overlay === 'dark'}
|
||||
class:darker={overlay === 'darker'}
|
||||
class:darkest={overlay === 'darkest'}
|
||||
></div>
|
||||
{/if}
|
||||
</figure>
|
||||
<div
|
||||
class="title color-white"
|
||||
class:top={top}
|
||||
class:bottom={bottom}
|
||||
class:left={left}
|
||||
class:right={right}
|
||||
>
|
||||
{#if section}
|
||||
<p class={`section-title color-${sectionColour} text-shadow`}>{section}</p>
|
||||
{/if}
|
||||
|
||||
{#if $$slots.hed}
|
||||
<slot name="hed"></slot>
|
||||
{:else}
|
||||
<h2
|
||||
class={`text-shadow-darker color-${hedColour} important`}
|
||||
>{hed}</h2>
|
||||
{/if}
|
||||
|
||||
{#if $$slots.dek}
|
||||
<slot name="dek"></slot>
|
||||
{:else}
|
||||
{#if dek}
|
||||
<p class="text-shadow-darkest">{dek}</p>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
113
src/components/Hero/docs.svx
Normal file
113
src/components/Hero/docs.svx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
---
|
||||
title: Hero
|
||||
description: A headline over a hero image
|
||||
slug: hero
|
||||
---
|
||||
|
||||
<script>
|
||||
import DemoContainer from '../_docs/DemoContainer/index.svelte';
|
||||
import Hero from './index.svelte';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
||||
## {title}
|
||||
|
||||
{description}
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<Hero hed="Eisbär" section="Climate Change" dek="The last of the white bears">
|
||||
<img slot="image" src="{`${assets}/images/polar.jpg`}" alt="A polar bear" />
|
||||
</Hero>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Hero
|
||||
hed='Eisbär'
|
||||
section='Climate Change'
|
||||
dek="The last of the white bears"
|
||||
>
|
||||
<img slot='image' src={`${assets}/images/polar.jpg`} alt='A polar bear' />
|
||||
</Hero>
|
||||
</DemoContainer>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<!-- Place the title with top, bottom, left & right props-->
|
||||
<Hero
|
||||
hed="Eisbär"
|
||||
section="Climate Change"
|
||||
dek="The last of the white bears"
|
||||
overlay="{'darker'}"
|
||||
bottom
|
||||
left
|
||||
>
|
||||
<img slot="image" src="{`${assets}/images/polar.jpg`}" alt="A polar bear" />
|
||||
</Hero>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Hero
|
||||
hed='Eisbär'
|
||||
section='Climate Change'
|
||||
dek="The last of the white bears"
|
||||
overlay={'darker'}
|
||||
bottom
|
||||
left
|
||||
>
|
||||
<img slot='image' src={`${assets}/images/polar.jpg`} alt='A polar bear' />
|
||||
</Hero>
|
||||
</DemoContainer>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<Hero section="Climate Change" overlay="{false}">
|
||||
<img slot="image" src="{`${assets}/images/polar.jpg`}" alt="A polar bear" />
|
||||
<!-- Override the hed and/or dek with named slots -->
|
||||
<h2 slot="hed" class="uppercase spaced-more color-blue text-shadow-lighter">
|
||||
Eisbaer
|
||||
</h2>
|
||||
<p slot="dek" class="color-blue text-shadow-lighter">
|
||||
The last of the white bears
|
||||
</p>
|
||||
</Hero>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Hero section="Climate Change" overlay={false}>
|
||||
<img slot="image" src="{`${assets}/images/polar.jpg`}" alt="A polar bear" />
|
||||
<h2 slot="hed" class="uppercase spaced-more color-blue text-shadow-lighter">Eisbaer</h2>
|
||||
<p slot="dek" class="color-blue text-shadow-lighter">The last of the white bears</p>
|
||||
</Hero>
|
||||
</DemoContainer>
|
||||
|
||||
<style lang="scss">
|
||||
/*Fudging some styles for the demo...*/
|
||||
:global {
|
||||
section.hero-title {
|
||||
figure {
|
||||
margin: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
div.title {
|
||||
padding: 0 15px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
17
src/components/Hero/stories/docs/component.md
Normal file
17
src/components/Hero/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
> 🔨 **Under construction**: We're working on this component to make it better. Pardon our mess.
|
||||
|
||||
Reuters Graphics headline
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Headline
|
||||
hed="{'Reuters Graphics Interactive'}"
|
||||
dek="{'The beginning of a beautiful page'}"
|
||||
section="{'Global news'}"
|
||||
sectionColour="{'orange'}"
|
||||
/>
|
||||
```
|
||||
19
src/components/Hero/stories/docs/withByline.md
Normal file
19
src/components/Hero/stories/docs/withByline.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Add a byline and dateline with `byline` and `dateline` named slots.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Headline
|
||||
hed="{'Reuters Graphics Interactive'}"
|
||||
dek="{'The beginning of a beautiful page'}"
|
||||
section="{'Global news'}"
|
||||
sectionColour="{'orange'}"
|
||||
>
|
||||
<!-- Use named slots to add a byline... -->
|
||||
<span slot="byline">By <strong>Peppa Pig</strong></span>
|
||||
<!-- ...and a dateline. -->
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
```
|
||||
16
src/components/Hero/stories/docs/withCrown.md
Normal file
16
src/components/Hero/stories/docs/withCrown.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Add a crown image in the `crown` named slot and override the headline in the `hed` named slot.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Headline } from '@reuters-graphics/graphics-svelte-components';
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<Headline>
|
||||
<!-- Add a crown -->
|
||||
<img slot="crown" src="{`${assets}/images/crown.png`}" />
|
||||
<!-- Override the hed with a named slot -->
|
||||
<h2 slot="hed" class="spaced font-serif">Europa</h2>
|
||||
<span slot="dateline">Published Jan. 1, 2020</span>
|
||||
</Headline>
|
||||
```
|
||||
BIN
src/components/Hero/stories/polar.jpg
Normal file
BIN
src/components/Hero/stories/polar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
28
src/components/PymChild/PymChild.stories.svelte
Normal file
28
src/components/PymChild/PymChild.stories.svelte
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import PymChild from './PymChild.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Utilities/PymChild',
|
||||
component: PymChild,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<PymChild {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
/>
|
||||
14
src/components/PymChild/PymChild.svelte
Normal file
14
src/components/PymChild/PymChild.svelte
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<script lang="ts">
|
||||
/** Pym.js polling interval */
|
||||
export let polling: number = 500;
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import pym from 'pym.js';
|
||||
|
||||
// @ts-ignore
|
||||
let pymChild;
|
||||
|
||||
onMount(() => {
|
||||
pymChild = new pym.Child({ polling });
|
||||
});
|
||||
</script>
|
||||
9
src/components/PymChild/stories/docs/component.md
Normal file
9
src/components/PymChild/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
A Pym.js child instance for embeddables.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { PymChild } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<PymChild polling="{500}" />
|
||||
```
|
||||
32
src/components/ReutersLogo/ReutersLogo.stories.svelte
Normal file
32
src/components/ReutersLogo/ReutersLogo.stories.svelte
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<script>
|
||||
import { Meta, Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import ReutersLogo from './ReutersLogo.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
const meta = {
|
||||
title: 'Components/ReutersLogo',
|
||||
component: ReutersLogo,
|
||||
...withComponentDocs(componentDocs),
|
||||
argTypes: {
|
||||
logoColour: { control: 'color' },
|
||||
textColour: { control: 'color' },
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Meta {...meta} />
|
||||
|
||||
<Template let:args>
|
||||
<ReutersLogo {...args} />
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
/>
|
||||
22
src/components/ReutersLogo/ReutersLogo.svelte
Normal file
22
src/components/ReutersLogo/ReutersLogo.svelte
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<script lang="ts">
|
||||
/** "Kinesis" colour */
|
||||
export let logoColour: string = '#FA6400';
|
||||
/** Text colour */
|
||||
export let textColour: string = '#404040';
|
||||
/** CSS width value */
|
||||
export let width: string = '100%';
|
||||
</script>
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
focusable="false"
|
||||
viewBox="0 0 301 72"
|
||||
style="width: {width};"
|
||||
>
|
||||
<path
|
||||
d="M79.231 36.006a4.171 4.171 0 11-8.342 0 4.171 4.171 0 018.342 0zm-12.81-7.292a2.506 2.506 0 100 5.011 2.506 2.506 0 000-5.01zm-2.959-8.744a3.187 3.187 0 100 6.373 3.187 3.187 0 000-6.373zm-5.165-6.632a2.944 2.944 0 100 5.888 2.944 2.944 0 000-5.888zM51.8 9.387a2.51 2.51 0 100 5.02 2.51 2.51 0 000-5.02zm-6.882-1.626a2.123 2.123 0 100 4.245 2.123 2.123 0 000-4.245zm-6.745.297a1.637 1.637 0 100 3.273 1.637 1.637 0 000-3.273zm-5.868 1.416a1.408 1.408 0 100 2.816 1.408 1.408 0 000-2.816zm-5.015 2.238a1.214 1.214 0 100 2.43 1.214 1.214 0 000-2.43zm-4.426 2.904a1.41 1.41 0 100 2.82 1.41 1.41 0 000-2.82zm-4.182 4.064a1.633 1.633 0 100 3.265 1.633 1.633 0 000-3.265zm-3.501 5.452a1.94 1.94 0 100 3.881 1.94 1.94 0 000-3.88zm-2.023 7.008a2.058 2.058 0 100 4.116 2.058 2.058 0 000-4.116zm.143 7.139a2.508 2.508 0 100 5.016 2.508 2.508 0 000-5.016zm2.956 7.387a3.185 3.185 0 100 6.37 3.185 3.185 0 000-6.37zm5.203 7.099a2.942 2.942 0 100 5.883 2.942 2.942 0 000-5.883zm6.27 4.703a2.505 2.505 0 100 5.01 2.505 2.505 0 000-5.01zm7.08 2.544a2.12 2.12 0 100 4.241 2.12 2.12 0 000-4.24zm6.733.666a1.63 1.63 0 100 3.26 1.63 1.63 0 000-3.26zm5.885-.96a1.41 1.41 0 100 2.821 1.41 1.41 0 000-2.82zm5.01-1.86a1.212 1.212 0 100 2.425 1.212 1.212 0 000-2.424zm4.425-3.29a1.415 1.415 0 100 2.829 1.415 1.415 0 000-2.83zm4.189-4.51a1.637 1.637 0 100 3.274 1.637 1.637 0 000-3.273zm3.492-6.07a1.939 1.939 0 100 3.879 1.939 1.939 0 000-3.878zm2.03-7.231a2.048 2.048 0 100 4.096 2.048 2.048 0 000-4.096zm-8.018-2.354a1.943 1.943 0 100 3.887 1.943 1.943 0 000-3.886zm-7.522-1.729a1.1 1.1 0 100 2.201 1.1 1.1 0 000-2.201zm-2.08-5.232a1.513 1.513 0 100 3.026 1.513 1.513 0 000-3.027zm-4.94-4.396a2.066 2.066 0 100 4.13 2.066 2.066 0 000-4.13zm-6.704-.742a2.33 2.33 0 100 4.66 2.33 2.33 0 000-4.66zm-5.678 3.236a2.216 2.216 0 100 4.432 2.216 2.216 0 000-4.432zm-2.91 6.09a1.492 1.492 0 100 2.984 1.492 1.492 0 000-2.985zm.116 5.686a1.098 1.098 0 100 2.197 1.098 1.098 0 000-2.197zm1.971 4.365a1.514 1.514 0 100 3.028 1.514 1.514 0 000-3.028zm4.97 3.369a2.064 2.064 0 100 4.128 2.064 2.064 0 000-4.128zm6.685.171a2.326 2.326 0 100 4.653 2.326 2.326 0 000-4.653zm5.712-3.023a2.222 2.222 0 100 4.444 2.222 2.222 0 000-4.444zm2.9-4.616a1.494 1.494 0 100 2.988 1.494 1.494 0 000-2.988zM57.58 26.61a2.498 2.498 0 100 4.996 2.498 2.498 0 000-4.996zm-4.232-7.013a2.986 2.986 0 100 5.972 2.986 2.986 0 000-5.972zm-6.385-3.924a2.676 2.676 0 100 5.353 2.676 2.676 0 000-5.353zm-7.088-.78a2.35 2.35 0 100 4.7 2.35 2.35 0 000-4.7zm-7.085 2.27a1.758 1.758 0 100 3.516 1.758 1.758 0 000-3.517zm-5.984 4.16a1.618 1.618 0 100 3.234 1.618 1.618 0 000-3.235zm-4.045 5.94a1.624 1.624 0 100 3.248 1.624 1.624 0 000-3.249zm-1.704 6.503a1.936 1.936 0 100 3.873 1.936 1.936 0 000-3.873zm.991 6.655a2.501 2.501 0 100 5.002 2.501 2.501 0 000-5.002zm4.229 6.055a2.98 2.98 0 100 5.96 2.98 2.98 0 000-5.96zm6.416 4.522a2.686 2.686 0 100 5.372 2.686 2.686 0 000-5.372zm7.066 1.415a2.366 2.366 0 100 4.733 2.366 2.366 0 000-4.733zm7.09-1.05a1.758 1.758 0 100 3.516 1.758 1.758 0 000-3.516zm5.992-3.852a1.62 1.62 0 100 3.238 1.62 1.62 0 000-3.238zm4.042-6a1.62 1.62 0 100 3.239 1.62 1.62 0 000-3.238zm16.53-20.253a3.603 3.603 0 100 7.205 3.603 3.603 0 000-7.205zm-4.581-8.281a3.066 3.066 0 100 6.133 3.066 3.066 0 000-6.133zm-6.8-6.497a2.642 2.642 0 100 5.284 2.642 2.642 0 000-5.284zm-7.626-3.932a2.14 2.14 0 100 4.279 2.14 2.14 0 000-4.28zM46.907.534a1.842 1.842 0 100 3.685 1.842 1.842 0 000-3.685zM39.855 0a1.585 1.585 0 100 3.17 1.585 1.585 0 000-3.17zm-7.03.526a1.847 1.847 0 100 3.694 1.847 1.847 0 000-3.693zm-7.5 2.022a2.141 2.141 0 100 4.282 2.141 2.141 0 000-4.282zm-7.633 3.924a2.644 2.644 0 100 5.288 2.644 2.644 0 000-5.288zm-6.801 6.505a3.064 3.064 0 100 6.127 3.064 3.064 0 000-6.127zM6.315 21.27a3.603 3.603 0 100 7.205 3.603 3.603 0 000-7.205zM4.662 31.835a4.171 4.171 0 100 8.342 4.171 4.171 0 000-8.342zm1.655 11.696a3.606 3.606 0 100 7.21 3.606 3.606 0 000-7.21zm4.578 9.374a3.062 3.062 0 100 6.125 3.062 3.062 0 000-6.125zm6.792 7.34a2.644 2.644 0 100 5.288 2.644 2.644 0 000-5.288zm7.638 4.927a2.142 2.142 0 100 4.284 2.142 2.142 0 000-4.284zm7.505 2.616a1.845 1.845 0 100 3.69 1.845 1.845 0 000-3.69zm7.041 1.036a1.588 1.588 0 100 3.176 1.588 1.588 0 000-3.176zm7.03-1.038a1.845 1.845 0 100 3.69 1.845 1.845 0 000-3.69zm7.505-2.61a2.148 2.148 0 100 4.297 2.148 2.148 0 000-4.297zm7.632-4.938a2.646 2.646 0 100 5.292 2.646 2.646 0 000-5.292zm6.8-7.335a3.059 3.059 0 100 6.118 3.059 3.059 0 000-6.118zm4.578-9.366a3.609 3.609 0 100 7.217 3.609 3.609 0 000-7.217z"
|
||||
fill="{logoColour}"></path>
|
||||
<path
|
||||
d="M121.865 50.29c0 .287-.167.497-.498.497h-5.085c-.455 0-.624-.422-.83-.833l-5.997-10.096h-.922c-1.087 0-4.451-.119-5.41-.168v10.264c0 .456-.336.833-.793.833h-4.165a.837.837 0 01-.83-.833V22.667c0-.833.494-1.125 1.333-1.248 2.166-.331 6.82-.537 9.864-.537 6.41 0 12.331 2.291 12.331 9.407v.378c0 4.407-2.204 6.82-5.616 8.146l6.532 11.141a.543.543 0 01.086.336zm-6.746-20c0-3.123-2.422-4.456-6.586-4.456-.878 0-4.666.084-5.41.168v9.026c.661.043 4.695.127 5.41.127 4.284 0 6.586-.826 6.586-4.488v-.378zm144.946 20c0 .287-.166.497-.498.497h-5.084c-.456 0-.625-.422-.83-.833l-5.998-10.096h-.922c-1.087 0-4.45-.119-5.41-.168v10.264c0 .456-.335.833-.793.833h-4.165a.837.837 0 01-.83-.833V22.667c0-.833.494-1.125 1.333-1.248 2.167-.331 6.82-.537 9.865-.537 6.41 0 12.33 2.291 12.33 9.407v.378c0 4.407-2.204 6.82-5.615 8.146l6.532 11.141a.544.544 0 01.085.336zm-6.745-20c0-3.123-2.423-4.456-6.587-4.456-.878 0-4.666.084-5.41.168v9.026c.661.043 4.695.127 5.41.127 4.284 0 6.587-.826 6.587-4.488v-.378zm-121.253 7.603h13.327a.835.835 0 00.833-.827V34.06a.837.837 0 00-.833-.829h-13.327v-3.818c0-3.482.134-3.578 3.284-3.578h10.54a.842.842 0 00.835-.836v-2.66c0-.585-.21-.796-.835-.876-1.332-.204-4.045-.58-8.995-.58-6.912 0-10.578-.048-10.578 8.53v13.174c0 8.579 3.666 8.531 10.578 8.531 4.95 0 7.663-.372 8.995-.583.625-.08.835-.288.835-.875v-2.66a.837.837 0 00-.835-.83h-10.54c-3.15 0-3.284-.094-3.284-3.583v-4.693zm82.266 0h13.326a.835.835 0 00.834-.827V34.06a.837.837 0 00-.834-.829h-13.326v-3.818c0-3.482.133-3.578 3.283-3.578h10.54a.842.842 0 00.836-.836v-2.66c0-.585-.21-.796-.835-.876-1.333-.204-4.045-.58-8.996-.58-6.912 0-10.578-.048-10.578 8.53v13.174c0 8.579 3.666 8.531 10.578 8.531 4.95 0 7.663-.372 8.996-.583.625-.08.835-.288.835-.875v-2.66a.837.837 0 00-.835-.83h-10.541c-3.15 0-3.284-.094-3.284-3.583v-4.693zM177 39.331V22.004c0-.462-.374-.791-.837-.791h-4.121c-.458 0-.83.329-.83.79v17.328c0 4.749-2.192 7-6.69 7-4.489 0-6.685-2.251-6.685-7V22.004c0-.462-.372-.791-.834-.791h-4.114c-.457 0-.836.329-.836.79v17.328c0 8.873 5.77 11.786 12.47 11.786 6.705 0 12.477-2.913 12.477-11.786zm26.923-13.292h-8.304v23.915a.84.84 0 01-.839.833h-4.117a.834.834 0 01-.83-.833V26.04h-8.315c-.455 0-.834-.288-.834-.745v-3.29c0-.462.379-.791.834-.791h22.404c.458 0 .835.33.835.791v3.29c0 .457-.377.745-.834.745zm81.586 16.38c0-3.994-2.27-6.06-5.788-7.48-2.766-1.112-5.196-2.057-7.046-2.785-1.842-.73-3.41-1.993-3.41-3.364 0-1.87 2.319-2.955 5.392-2.955 3.626 0 6.477.506 9.227.757h.084c.414 0 .703-.338.703-.75V22.76c0-.413-.327-.706-.746-.787-1.411-.339-5.044-1.094-9.015-1.094-7.74 0-11.432 3.33-11.432 7.912 0 2.911 1.283 5.781 4.912 7.279 3.636 1.5 11.08 3.228 11.08 6.35 0 2.414-1.124 3.741-4.833 3.741-3.848 0-7.745-.545-9.412-.754h-.083a.793.793 0 00-.789.793v3c0 .458.375.745.79.828 2.002.46 5.735 1.094 9.41 1.094 8.335 0 10.956-4.5 10.956-8.703zm3.039-15.501c0 3.27 2.412 6.038 6.092 6.038 3.68 0 6.119-2.769 6.119-6.038 0-3.296-2.44-6.006-6.12-6.006-3.679 0-6.091 2.71-6.091 6.006zm.887 0c0-2.908 2.049-5.23 5.205-5.23 3.183 0 5.23 2.322 5.23 5.23 0 2.909-2.047 5.263-5.23 5.263-3.156 0-5.204-2.354-5.204-5.263zm7.97 2.69l-1.321-2.25c.69-.267 1.136-.754 1.136-1.644v-.075c0-1.438-1.196-1.9-2.491-1.9-.612 0-1.553.042-1.99.11-.169.023-.269.082-.269.25v5.51a.17.17 0 00.169.167h.84c.091 0 .158-.076.158-.167v-2.073c.195.01.875.035 1.092.035h.187l1.213 2.038c.04.082.074.167.166.167h1.027c.065 0 .1-.043.1-.1 0-.019 0-.043-.017-.067zm-1.345-3.893c0 .738-.464.905-1.331.905-.14 0-.955-.017-1.092-.026v-1.821a21.3 21.3 0 011.092-.034c.842 0 1.331.269 1.331.9v.076z"
|
||||
fill="{textColour}"></path>
|
||||
</svg>
|
||||
9
src/components/ReutersLogo/stories/docs/component.md
Normal file
9
src/components/ReutersLogo/stories/docs/component.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
The official home of the Reuters logo.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { ReutersLogo } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<ReutersLogo />
|
||||
```
|
||||
331
src/components/SEO/analytics.js
Normal file
331
src/components/SEO/analytics.js
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/* eslint-disable */
|
||||
const attachScript = function (i, s, o, g, r, a, m) {
|
||||
i.GoogleAnalyticsObject = r;
|
||||
(i[r] =
|
||||
i[r] ||
|
||||
function () {
|
||||
(i[r].q = i[r].q || []).push(arguments);
|
||||
}),
|
||||
(i[r].l = 1 * new Date());
|
||||
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
|
||||
a.async = 1;
|
||||
a.src = g;
|
||||
m.parentNode.insertBefore(a, m);
|
||||
};
|
||||
/* eslint-enable */
|
||||
|
||||
export default (page, title) => {
|
||||
attachScript(
|
||||
window,
|
||||
document,
|
||||
'script',
|
||||
'https://www.google-analytics.com/analytics.js',
|
||||
'ga'
|
||||
);
|
||||
|
||||
window.ga('create', 'UA-41619329-3', { cookieDomain: 'auto' });
|
||||
window.ga('require', 'linkid', 'linkid.js');
|
||||
window.ga('send', 'pageview', {
|
||||
page,
|
||||
title,
|
||||
});
|
||||
|
||||
if (!inIframe()) {
|
||||
//start time on page tracking if not in an iframe
|
||||
riveted.init({
|
||||
reportInterval: 30,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// checks if page is in an iframe
|
||||
function inIframe() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* START: Riveted time on page tracking code
|
||||
* see aditional documentation here: https://riveted.parsnip.io/
|
||||
*/
|
||||
|
||||
var riveted = (function () {
|
||||
var started = false;
|
||||
var stopped = false;
|
||||
var turnedOff = false;
|
||||
var clockTime = 0;
|
||||
var startTime = new Date();
|
||||
var clockTimer = null;
|
||||
var idleTimer = null;
|
||||
var sendEvent;
|
||||
var sendUserTiming;
|
||||
var reportInterval;
|
||||
var idleTimeout;
|
||||
var nonInteraction;
|
||||
var universalGA;
|
||||
var classicGA;
|
||||
var googleTagManager;
|
||||
|
||||
function init(options) {
|
||||
/*
|
||||
* Determine which version of GA is being used
|
||||
* "ga", "_gaq", and "dataLayer" are the possible globals
|
||||
*/
|
||||
|
||||
if (typeof ga === 'function') {
|
||||
universalGA = true;
|
||||
}
|
||||
|
||||
if (typeof _gaq !== 'undefined' && typeof _gaq.push === 'function') {
|
||||
classicGA = true;
|
||||
}
|
||||
|
||||
if (
|
||||
typeof dataLayer !== 'undefined' &&
|
||||
typeof dataLayer.push === 'function'
|
||||
) {
|
||||
googleTagManager = true;
|
||||
}
|
||||
|
||||
// Set up options and defaults
|
||||
options = options || {};
|
||||
reportInterval = parseInt(options.reportInterval, 10) || 5;
|
||||
idleTimeout = parseInt(options.idleTimeout, 10) || 30;
|
||||
|
||||
if (typeof options.eventHandler === 'function') {
|
||||
sendEvent = options.eventHandler;
|
||||
}
|
||||
|
||||
if (typeof options.userTimingHandler === 'function') {
|
||||
sendUserTiming = options.userTimingHandler;
|
||||
}
|
||||
|
||||
if (
|
||||
'nonInteraction' in options &&
|
||||
(options.nonInteraction === false || options.nonInteraction === 'false')
|
||||
) {
|
||||
nonInteraction = false;
|
||||
} else {
|
||||
nonInteraction = true;
|
||||
}
|
||||
|
||||
// Basic activity event listeners
|
||||
addListener(document, 'keydown', trigger);
|
||||
addListener(document, 'click', trigger);
|
||||
addListener(window, 'mousemove', throttle(trigger, 500));
|
||||
addListener(window, 'scroll', throttle(trigger, 500));
|
||||
|
||||
// Page visibility listeners
|
||||
addListener(document, 'visibilitychange', visibilityChange);
|
||||
addListener(document, 'webkitvisibilitychange', visibilityChange);
|
||||
|
||||
//sends initial zero value
|
||||
sendEvent(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Throttle function borrowed from:
|
||||
* Underscore.js 1.5.2
|
||||
* http://underscorejs.org
|
||||
* (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
* Underscore may be freely distributed under the MIT license.
|
||||
*/
|
||||
|
||||
function throttle(func, wait) {
|
||||
var context, args, result;
|
||||
var timeout = null;
|
||||
var previous = 0;
|
||||
var later = function () {
|
||||
previous = new Date();
|
||||
timeout = null;
|
||||
result = func.apply(context, args);
|
||||
};
|
||||
return function () {
|
||||
var now = new Date();
|
||||
if (!previous) previous = now;
|
||||
var remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = func.apply(context, args);
|
||||
} else if (!timeout) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Cross-browser event listening
|
||||
*/
|
||||
|
||||
function addListener(element, eventName, handler) {
|
||||
if (element.addEventListener) {
|
||||
element.addEventListener(eventName, handler, false);
|
||||
} else if (element.attachEvent) {
|
||||
element.attachEvent('on' + eventName, handler);
|
||||
} else {
|
||||
element['on' + eventName] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Function for logging User Timing event on initial interaction
|
||||
*/
|
||||
|
||||
sendUserTiming = function (timingValue) {
|
||||
if (googleTagManager) {
|
||||
dataLayer.push({
|
||||
event: 'RivetedTiming',
|
||||
eventCategory: 'Riveted',
|
||||
timingVar: 'First Interaction',
|
||||
timingValue: timingValue,
|
||||
});
|
||||
} else {
|
||||
if (universalGA) {
|
||||
ga('send', 'timing', 'Riveted', 'First Interaction', timingValue);
|
||||
}
|
||||
|
||||
if (classicGA) {
|
||||
_gaq.push([
|
||||
'_trackTiming',
|
||||
'Riveted',
|
||||
'First Interaction',
|
||||
timingValue,
|
||||
null,
|
||||
100,
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Function for logging ping events
|
||||
*/
|
||||
|
||||
sendEvent = function (time) {
|
||||
if (googleTagManager) {
|
||||
dataLayer.push({
|
||||
event: 'Riveted',
|
||||
eventCategory: 'Riveted',
|
||||
eventAction: 'Time Spent',
|
||||
eventLabel: time,
|
||||
eventValue: reportInterval,
|
||||
eventNonInteraction: nonInteraction,
|
||||
});
|
||||
} else {
|
||||
if (universalGA) {
|
||||
ga(
|
||||
'send',
|
||||
'event',
|
||||
'Riveted',
|
||||
'Time Spent',
|
||||
time.toString(),
|
||||
reportInterval,
|
||||
{ nonInteraction: nonInteraction }
|
||||
);
|
||||
}
|
||||
|
||||
if (classicGA) {
|
||||
_gaq.push([
|
||||
'_trackEvent',
|
||||
'Riveted',
|
||||
'Time Spent',
|
||||
time.toString(),
|
||||
reportInterval,
|
||||
nonInteraction,
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function setIdle() {
|
||||
clearTimeout(idleTimer);
|
||||
stopClock();
|
||||
}
|
||||
|
||||
function visibilityChange() {
|
||||
if (document.hidden || document.webkitHidden) {
|
||||
setIdle();
|
||||
}
|
||||
}
|
||||
|
||||
function clock() {
|
||||
clockTime += 1;
|
||||
if (clockTime > 0 && clockTime % reportInterval === 0) {
|
||||
sendEvent(clockTime);
|
||||
}
|
||||
}
|
||||
|
||||
function stopClock() {
|
||||
stopped = true;
|
||||
clearTimeout(clockTimer);
|
||||
}
|
||||
|
||||
function turnOff() {
|
||||
setIdle();
|
||||
turnedOff = true;
|
||||
}
|
||||
|
||||
function turnOn() {
|
||||
turnedOff = false;
|
||||
}
|
||||
|
||||
function restartClock() {
|
||||
stopped = false;
|
||||
clearTimeout(clockTimer);
|
||||
clockTimer = setInterval(clock, 1000);
|
||||
}
|
||||
|
||||
function startRiveted() {
|
||||
// Calculate seconds from start to first interaction
|
||||
var currentTime = new Date();
|
||||
var diff = currentTime - startTime;
|
||||
|
||||
// Set global
|
||||
started = true;
|
||||
|
||||
// Send User Timing Event
|
||||
sendUserTiming(diff);
|
||||
|
||||
// Start clock
|
||||
clockTimer = setInterval(clock, 1000);
|
||||
}
|
||||
|
||||
function trigger() {
|
||||
if (turnedOff) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!started) {
|
||||
startRiveted();
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
restartClock();
|
||||
}
|
||||
|
||||
clearTimeout(idleTimer);
|
||||
idleTimer = setTimeout(setIdle, idleTimeout * 1000 + 100);
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
trigger: trigger,
|
||||
setIdle: setIdle,
|
||||
on: turnOn,
|
||||
off: turnOff,
|
||||
};
|
||||
})();
|
||||
|
||||
/* END: Riveted time on page tracking code */
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue