commit fc28e8bae8b6ce678ea13e568b5a187b085c011c Author: Jon McClure Date: Thu Aug 11 15:43:38 2022 +0100 reinit new repo diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..5b3fb5c9 --- /dev/null +++ b/.eslintrc.js @@ -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], + }, + }, + ], +}; diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..5003c690 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @hobbes7878 @fcage @MinamiFunakoshiTR diff --git a/.github/COMMIT_ERROR_ISSUE_TEMPLATE.md b/.github/COMMIT_ERROR_ISSUE_TEMPLATE.md new file mode 100644 index 00000000..a5ebe07c --- /dev/null +++ b/.github/COMMIT_ERROR_ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ +--- +title: Build error on commit +assignees: hobbes7878, MinamiFunakoshiTR +labels: bug +--- + +A commit caused docs to fail to build: {{ sha }} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..fee34b12 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -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 diff --git a/.github/docs.yaml b/.github/docs.yaml new file mode 100644 index 00000000..e03b15bf --- /dev/null +++ b/.github/docs.yaml @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f37acbde --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 00000000..8ca7675b --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,5 @@ +{ + "MD013": false, + "MD033": false, + "MD041": false +} \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..4fd02195 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..96c69b81 --- /dev/null +++ b/.prettierrc @@ -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 +} \ No newline at end of file diff --git a/.storybook/ArticleWrapper.svelte b/.storybook/ArticleWrapper.svelte new file mode 100644 index 00000000..fbf8d291 --- /dev/null +++ b/.storybook/ArticleWrapper.svelte @@ -0,0 +1,4 @@ + +
+ +
\ No newline at end of file diff --git a/.storybook/Theme.js b/.storybook/Theme.js new file mode 100644 index 00000000..964f63dd --- /dev/null +++ b/.storybook/Theme.js @@ -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', +}); \ No newline at end of file diff --git a/.storybook/main.cjs b/.storybook/main.cjs new file mode 100644 index 00000000..6255cbc4 --- /dev/null +++ b/.storybook/main.cjs @@ -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, + }, +} \ No newline at end of file diff --git a/.storybook/manager.js b/.storybook/manager.js new file mode 100644 index 00000000..ea8af2b1 --- /dev/null +++ b/.storybook/manager.js @@ -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, +}); \ No newline at end of file diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 00000000..05da1e9d --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 00000000..044b80b0 --- /dev/null +++ b/.storybook/preview.js @@ -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 })]; diff --git a/.storybook/preview.scss b/.storybook/preview.scss new file mode 100644 index 00000000..13674701 --- /dev/null +++ b/.storybook/preview.scss @@ -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; + } +} + diff --git a/.storybook/svelte-highlighting.js b/.storybook/svelte-highlighting.js new file mode 100644 index 00000000..7501035a --- /dev/null +++ b/.storybook/svelte-highlighting.js @@ -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: /(^$)/i, + lookbehind: true, + inside: Prism.languages[lang], + }; + includedCdataInside['cdata'] = /^$/i; + + const inside = { + 'included-cdata': { + pattern: //i, + inside: includedCdataInside, + }, + }; + inside['language-' + lang] = { + pattern: /[\s\S]+/, + inside: Prism.languages[lang], + }; + + const def = {}; + def[tagName] = { + pattern: RegExp( + /(<__[\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'); +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..b2c8b0b2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["silvenon.mdx"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..3c2a3e58 --- /dev/null +++ b/.vscode/settings.json @@ -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" + } +} \ No newline at end of file diff --git a/.vscode/svelte.scripts.code-snippets b/.vscode/svelte.scripts.code-snippets new file mode 100644 index 00000000..e06f1ed3 --- /dev/null +++ b/.vscode/svelte.scripts.code-snippets @@ -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": [ + "" + ] + } +} \ No newline at end of file diff --git a/.vscode/svelte.styles.code-snippets b/.vscode/svelte.styles.code-snippets new file mode 100644 index 00000000..bc626671 --- /dev/null +++ b/.vscode/svelte.styles.code-snippets @@ -0,0 +1,49 @@ +{ + "Svelte SCSS style": { + "scope": "svelte", + "prefix": "scss", + "body": [ + "" + ], + "description": "Add a Svelte SCSS style tag" + }, + "Reuters Graphics styles theme": { + "scope": "svelte", + "prefix": "theme", + "body": [ + "" + ], + "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" + } +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..eed8ca4e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,50 @@ +![](https://graphics.thomsonreuters.com/style-assets/images/logos/reuters-graphics-logo/svg/graphics-logo-color-dark.svg) + +# 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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..3a381939 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +![](https://graphics.thomsonreuters.com/style-assets/images/logos/reuters-graphics-logo/svg/graphics-logo-color-dark.svg) + +# ⚙️ graphics-components + +Home of the next generation. diff --git a/assetinfo.json b/assetinfo.json new file mode 100644 index 00000000..41e7aead --- /dev/null +++ b/assetinfo.json @@ -0,0 +1,3 @@ +{ + "assetid": ["205245"] +} \ No newline at end of file diff --git a/bin/buildPackage/index.js b/bin/buildPackage/index.js new file mode 100644 index 00000000..5cfb53f1 --- /dev/null +++ b/bin/buildPackage/index.js @@ -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(); \ No newline at end of file diff --git a/bin/buildPackage/locations.js b/bin/buildPackage/locations.js new file mode 100644 index 00000000..c50a60f0 --- /dev/null +++ b/bin/buildPackage/locations.js @@ -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'); diff --git a/bin/buildPackage/process/other.js b/bin/buildPackage/process/other.js new file mode 100644 index 00000000..eef0858a --- /dev/null +++ b/bin/buildPackage/process/other.js @@ -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); +} diff --git a/bin/buildPackage/process/svelte.js b/bin/buildPackage/process/svelte.js new file mode 100644 index 00000000..abb508d2 --- /dev/null +++ b/bin/buildPackage/process/svelte.js @@ -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(/()|(]*?)\s(?:type|lang)=(["']).*?\3/g, '$1$2') + .replace(/()|(]*?)\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)); +} diff --git a/bin/buildPackage/process/typescript.js b/bin/buildPackage/process/typescript.js new file mode 100644 index 00000000..376fcfa4 --- /dev/null +++ b/bin/buildPackage/process/typescript.js @@ -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); +} diff --git a/bin/newComponent/index.cjs b/bin/newComponent/index.cjs new file mode 100644 index 00000000..05824c7d --- /dev/null +++ b/bin/newComponent/index.cjs @@ -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(); \ No newline at end of file diff --git a/bin/newComponent/template/YourComponent/YourComponent.stories.svelte b/bin/newComponent/template/YourComponent/YourComponent.stories.svelte new file mode 100644 index 00000000..09f52838 --- /dev/null +++ b/bin/newComponent/template/YourComponent/YourComponent.stories.svelte @@ -0,0 +1,43 @@ + + + + + + + \ No newline at end of file diff --git a/bin/newComponent/template/YourComponent/YourComponent.svelte b/bin/newComponent/template/YourComponent/YourComponent.svelte new file mode 100644 index 00000000..25e68616 --- /dev/null +++ b/bin/newComponent/template/YourComponent/YourComponent.svelte @@ -0,0 +1,51 @@ + + +
+
+

{altText}

+
+ + diff --git a/bin/newComponent/template/YourComponent/stories/docs/component.md b/bin/newComponent/template/YourComponent/stories/docs/component.md new file mode 100644 index 00000000..d95b9b42 --- /dev/null +++ b/bin/newComponent/template/YourComponent/stories/docs/component.md @@ -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 + + + + +``` diff --git a/bin/newComponent/template/YourComponent/stories/shark.jpg b/bin/newComponent/template/YourComponent/stories/shark.jpg new file mode 100644 index 00000000..352b0ab9 Binary files /dev/null and b/bin/newComponent/template/YourComponent/stories/shark.jpg differ diff --git a/bin/preprocess/index.cjs b/bin/preprocess/index.cjs new file mode 100644 index 00000000..7d58f4a9 --- /dev/null +++ b/bin/preprocess/index.cjs @@ -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 +} \ No newline at end of file diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 00000000..b560deca --- /dev/null +++ b/lefthook.yml @@ -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} diff --git a/package.json b/package.json new file mode 100644 index 00000000..56a23d62 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/src/actions/cssVariables/cssVariables.stories.mdx b/src/actions/cssVariables/cssVariables.stories.mdx new file mode 100644 index 00000000..cf00aa95 --- /dev/null +++ b/src/actions/cssVariables/cssVariables.stories.mdx @@ -0,0 +1,42 @@ +import { Meta } from '@storybook/addon-docs'; +import { parameters } from '$docs/utils/docsPage.js'; + + + +![](https://graphics.thomsonreuters.com/style-assets/images/logos/reuters-graphics-logo/svg/graphics-logo-color-dark.svg) + +# `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 + + + +
+

My text...

+
+ + +``` diff --git a/src/actions/cssVariables/index.js b/src/actions/cssVariables/index.js new file mode 100644 index 00000000..523e09e8 --- /dev/null +++ b/src/actions/cssVariables/index.js @@ -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; + }, + }; +}; \ No newline at end of file diff --git a/src/actions/resizeObserver/index.js b/src/actions/resizeObserver/index.js new file mode 100644 index 00000000..616257ee --- /dev/null +++ b/src/actions/resizeObserver/index.js @@ -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); + }, + }; +} diff --git a/src/components/Ai2svelte/ai2svelte/ai-chart.exclude.svelte b/src/components/Ai2svelte/ai2svelte/ai-chart.exclude.svelte new file mode 100644 index 00000000..7e53ec04 --- /dev/null +++ b/src/components/Ai2svelte/ai2svelte/ai-chart.exclude.svelte @@ -0,0 +1,601 @@ + + + + +
+ + {#if width && width >= 0 && width < 510} +
+
+
+
+

Shake intensity

+
+
+

Light

+
+
+

Moderate

+
+
+

Cap-Haitien

+
+
+

Strong

+
+
+

Very strong

+
+
+

Gonaïves

+
+
+

Caribbean

+

Sea

+
+
+

HAITI

+
+
+

Jeremie

+
+
+

Port-au-Prince

+
+
+

Epicenter

+
+
+

Jacmel

+
+
+

Les Cayes

+
+
+

50 mi

+
+
+

Dominican

+

Republic

+
+
+

50 km

+
+
+ {/if} + + {#if width && width >= 510 && width < 660} +
+
+
+
+

Shake intensity

+
+
+

Light

+
+
+

Moderate

+
+
+

Cap-Haitien

+
+
+

Strong

+
+
+

Very strong

+
+
+

Gonaïves

+
+
+

Caribbean

+

Sea

+
+
+

HAITI

+
+
+

Jeremie

+
+
+

Port-au-Prince

+
+
+

Epicenter

+
+
+

Dominican

+

Republic

+
+
+

Jacmel

+
+
+

Les Cayes

+
+
+

50 mi

+
+
+

50 km

+
+
+ {/if} + + {#if width && width >= 660} +
+
+
+
+

Shake intensity

+
+
+

Light

+
+
+

Moderate

+
+
+

Cap-Haitien

+
+
+

Strong

+
+
+

Very strong

+
+
+

Gonaïves

+
+
+

Caribbean

+

Sea

+
+
+

HAITI

+
+
+

Dominican

+

Republic

+
+
+

Jeremie

+
+
+

Epicenter

+
+
+

Port-au-Prince

+
+
+

Jacmel

+
+
+

Les Cayes

+
+
+

50 mi

+
+
+

50 km

+
+
+ {/if} +
+ + + + diff --git a/src/components/Ai2svelte/ai2svelte/ai-linechart.exclude.svelte b/src/components/Ai2svelte/ai2svelte/ai-linechart.exclude.svelte new file mode 100644 index 00000000..6649e664 --- /dev/null +++ b/src/components/Ai2svelte/ai2svelte/ai-linechart.exclude.svelte @@ -0,0 +1,230 @@ + + + + +
+ + {#if width && width >= 0 && width < 510} +
+
+
+
+ {/if} + + {#if width && width >= 510 && width < 660} +
+
+
+
+ {/if} + + {#if width && width >= 660 && width < 930} +
+
+
+
+

5%

+
+
+

0

+
+
+

Dow

+
+
+

S&P 500

+
+
+

Nasdaq

+
+
+

−5

+
+
+

−10

+
+
+

−15

+
+
+

Jan. 2, 2022

+
+
+

Jan. 9

+
+
+ {/if} + + {#if width && width >= 930 && width < 1200} +
+
+
+
+ {/if} + + {#if width && width >= 1200} +
+
+
+
+ {/if} +
+ + + + diff --git a/src/components/Ai2svelte/ai2svelte/data-table.exclude.svelte b/src/components/Ai2svelte/ai2svelte/data-table.exclude.svelte new file mode 100644 index 00000000..9c331b15 --- /dev/null +++ b/src/components/Ai2svelte/ai2svelte/data-table.exclude.svelte @@ -0,0 +1,24 @@ + + + + + + + + + +
Date S&P 500 + Dow Nasdaq +
December 31, 20210%00%
January 3, 20221%11%
January 4, 20221%1−0%
January 5, 2022−1%0−3%
January 6, 2022−1%−0−4%
January 7, 2022−2%−0−5%
January 10, 2022−2%−1−4%
diff --git a/src/components/Ai2svelte/docs.svx b/src/components/Ai2svelte/docs.svx new file mode 100644 index 00000000..50f37291 --- /dev/null +++ b/src/components/Ai2svelte/docs.svx @@ -0,0 +1,320 @@ +--- +title: Ai2svelte +description: A shortcut for ai2svelte graphics. +slug: ai2svelte +--- + + + +
+ +## {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. + +
+ +```svelte + + + +``` + + + + + +
+ +## 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). + +
+ +🚨 **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. + +
+ +Graphics kit users can set your `ariaHidden` prop and write your `ariaDescription` in the google doc like this: + +
+ +```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. + +[] +``` + +
+ +## 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`. + +
+ +```svelte + + + +``` + + + + + +
+ +## Chart title, description, source and note + +Add additional chart chatter through slots. + +
+ +```svelte + + + + + +
+

Earthquake in Haiti

+

A 7.0 magnitude earthquake struck the island on Tuesday.

+
+ +
+ + +``` + + + + +
+

Earthquake in Haiti

+

A 7.0 magnitude earthquake struck the island on Tuesday.

+
+ +
+
+ + + +
+ +## 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: + +
+ +```bash +# In your Google doc... +[blocks] + +# ... +Type: ai2svelte +ComponentName: my-chart + +[] +``` + +```svelte + + + +{#each content.blocks as block} + {#if block.Type === 'ai2svelte'} + {#await fetchComponent(block.ComponentName)} +
+ {:then component} + + +
+ {#if block.Title}

{block.Title}

{/if} + {#if block.Chatter}

{block.Chatter}

{/if} +
+ + +
+ {:catch error} + {console.error( + `Error fetching component: ./ai2svelte/${block.ComponentName}.svelte`, + error + )} + {/await} + {/if} +{/each} +``` + +
+ +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. + +
+ +
+ +## 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. + +
+ +```svelte + + + + + + +``` + + + {#await fetchComponent('ai-linechart') then component} + + + + {/await} + + +
+ +This is what `` looks like. (You can make an HTML table using DataWrapper.) + +
+ +```svelte + + + + + + + + + +
Date S&P 500 + Dow Nasdaq +
December 31, 20210%00%
January 3, 20221%11%
January 4, 20221%1−0%
January 5, 2022−1%0−3%
January 6, 2022−1%−0−4%
January 7, 2022−2%−0−5%
January 10, 2022−2%−1−4%
+ +``` + +
+ +[Read this](https://accessibility.psu.edu/images/charts/) for more information on screen-reader data tables for charts. + +
diff --git a/src/components/Ai2svelte/index.svelte b/src/components/Ai2svelte/index.svelte new file mode 100644 index 00000000..ddb11c44 --- /dev/null +++ b/src/components/Ai2svelte/index.svelte @@ -0,0 +1,63 @@ + + +
+ {#if (ariaHidden && (ariaDescription || $$slots.hidden)) || !ariaHidden} + {#if $$slots.title} +
+ +
+ {/if} + {#if ariaDescription} +

{ariaDescription}

+ {/if} + {#if $$slots.hidden} +
+ +
+ {/if} +
+ +
+ {#if $$slots.notes} +
+ +
+ {/if} + {/if} +
+ + diff --git a/src/components/Article/Article.stories.svelte b/src/components/Article/Article.stories.svelte new file mode 100644 index 00000000..2c230dba --- /dev/null +++ b/src/components/Article/Article.stories.svelte @@ -0,0 +1,126 @@ + + + + + + + +
+
+
Article container
+
15px padding
+
+
+
+ + +
+
+
narrower
+
narrow
+
normal
+
wide
+
wider
+
widest
+
fluid
+
+
+
+
+
narrower*
+
narrow*
+
normal*
+
wide*
+
wider*
+
widest
+
fluid
+
+
+
+ + \ No newline at end of file diff --git a/src/components/Article/Article.svelte b/src/components/Article/Article.svelte new file mode 100644 index 00000000..89fbc296 --- /dev/null +++ b/src/components/Article/Article.svelte @@ -0,0 +1,59 @@ + + +
+ + +
+ + diff --git a/src/components/Article/stories/docs/component.md b/src/components/Article/stories/docs/component.md new file mode 100644 index 00000000..ba1fd02f --- /dev/null +++ b/src/components/Article/stories/docs/component.md @@ -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 + + +
+ +
+``` diff --git a/src/components/Article/stories/docs/customWellWidths.md b/src/components/Article/stories/docs/customWellWidths.md new file mode 100644 index 00000000..172e50da --- /dev/null +++ b/src/components/Article/stories/docs/customWellWidths.md @@ -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 +
+
+
+
+
+
+
+
+
+``` + +> 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 +
+ +
+ + +``` + +... or you can make your component entirely configurable within the article well doing something like this: + +```svelte + + +
+ +
+ + +``` + +Here's an example of how custom* `columnWidths` can be used to change the article well columns: diff --git a/src/components/BeforeAfter/BeforeAfter.stories.svelte b/src/components/BeforeAfter/BeforeAfter.stories.svelte new file mode 100644 index 00000000..b0544858 --- /dev/null +++ b/src/components/BeforeAfter/BeforeAfter.stories.svelte @@ -0,0 +1,123 @@ + + + + + + + + + + +
+
July 7, 2020
+

Initially, this site was far smaller.

+
+
+
Oct. 20, 2020
+

But then forces built up.

+
+ +
+ + +
+ + + +
+

+ On July 7, 2020, the base contained only a few transport vehicles. +

+
+
+ +
+

But by October, tanks and artillery could be seen.

+

+ In total, over 50 pieces of heavy machinery and 200 personnel are now + based here. +

+
+
+
+ +
\ No newline at end of file diff --git a/src/components/BeforeAfter/BeforeAfter.svelte b/src/components/BeforeAfter/BeforeAfter.svelte new file mode 100644 index 00000000..01246d0f --- /dev/null +++ b/src/components/BeforeAfter/BeforeAfter.svelte @@ -0,0 +1,322 @@ + + + + +{#if beforeSrc && beforeAlt && afterSrc && afterAlt} +
+
+ {afterAlt} + + {beforeAlt} + {#if $$slots.beforeOverlay} +
+ + +
+ {/if} + {#if $$slots.afterOverlay} +
+ + +
+ {/if} +
+
+
+
+
+
+ {#if $$slots.caption} +
+ + +
+ {/if} +{/if} + + diff --git a/src/components/BeforeAfter/stories/docs/ariaDescriptions.md b/src/components/BeforeAfter/stories/docs/ariaDescriptions.md new file mode 100644 index 00000000..9c4e2863 --- /dev/null +++ b/src/components/BeforeAfter/stories/docs/ariaDescriptions.md @@ -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 + + +
+

+ On July 7, 2020, the base contained only a few transport vehicles. +

+
+
+ +
+

But by October, tanks and artillery could be seen.

+

+ In total, over 50 pieces of heavy machinery and 200 personnel are now + based here. +

+
+
+
+``` diff --git a/src/components/BeforeAfter/stories/docs/component.md b/src/components/BeforeAfter/stories/docs/component.md new file mode 100644 index 00000000..62391d53 --- /dev/null +++ b/src/components/BeforeAfter/stories/docs/component.md @@ -0,0 +1,15 @@ +A before and after image comparison component. + +```svelte + + + +``` diff --git a/src/components/BeforeAfter/stories/docs/withOverlays.md b/src/components/BeforeAfter/stories/docs/withOverlays.md new file mode 100644 index 00000000..28f7cd7d --- /dev/null +++ b/src/components/BeforeAfter/stories/docs/withOverlays.md @@ -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 + +
+
July 7, 2020
+

Initially, this site was far smaller.

+
+
+
Oct. 20, 2020
+

But then forces built up.

+
+
+ + +``` diff --git a/src/components/BeforeAfter/stories/myrne-after.jpg b/src/components/BeforeAfter/stories/myrne-after.jpg new file mode 100644 index 00000000..62d97fdf Binary files /dev/null and b/src/components/BeforeAfter/stories/myrne-after.jpg differ diff --git a/src/components/BeforeAfter/stories/myrne-before.jpg b/src/components/BeforeAfter/stories/myrne-before.jpg new file mode 100644 index 00000000..96ed5241 Binary files /dev/null and b/src/components/BeforeAfter/stories/myrne-before.jpg differ diff --git a/src/components/BodyText/BodyText.stories.svelte b/src/components/BodyText/BodyText.stories.svelte new file mode 100644 index 00000000..8e047dc7 --- /dev/null +++ b/src/components/BodyText/BodyText.stories.svelte @@ -0,0 +1,35 @@ + + + + + + + diff --git a/src/components/BodyText/BodyText.svelte b/src/components/BodyText/BodyText.svelte new file mode 100644 index 00000000..66c79148 --- /dev/null +++ b/src/components/BodyText/BodyText.svelte @@ -0,0 +1,17 @@ + + +
+ {#if text} + {@html marked.parse(text)} + {/if} +
diff --git a/src/components/BodyText/stories/docs/component.md b/src/components/BodyText/stories/docs/component.md new file mode 100644 index 00000000..096e410e --- /dev/null +++ b/src/components/BodyText/stories/docs/component.md @@ -0,0 +1,15 @@ +Parse mardown-formatted text. + +```svelte + + + +``` diff --git a/src/components/DatawrapperChart/DatawrapperChart.stories.svelte b/src/components/DatawrapperChart/DatawrapperChart.stories.svelte new file mode 100644 index 00000000..bfb02459 --- /dev/null +++ b/src/components/DatawrapperChart/DatawrapperChart.stories.svelte @@ -0,0 +1,61 @@ + + + + + + + + + + +
+

Global abortion access

+

A map of worldwide access to abortion.

+
+ +
+
\ No newline at end of file diff --git a/src/components/DatawrapperChart/DatawrapperChart.svelte b/src/components/DatawrapperChart/DatawrapperChart.svelte new file mode 100644 index 00000000..1eefc3ec --- /dev/null +++ b/src/components/DatawrapperChart/DatawrapperChart.svelte @@ -0,0 +1,83 @@ + + +
+ {#if $$slots.title} +
+ + +
+ {/if} + +
+ +
+ + {#if $$slots.notes} +
+ + +
+ {/if} +
+ + diff --git a/src/components/DatawrapperChart/stories/docs/component.md b/src/components/DatawrapperChart/stories/docs/component.md new file mode 100644 index 00000000..48bf740a --- /dev/null +++ b/src/components/DatawrapperChart/stories/docs/component.md @@ -0,0 +1,25 @@ +Easily add a responsive Datawrapper embed on your page. + +```svelte + + + +``` + +##### 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. diff --git a/src/components/DatawrapperChart/stories/docs/withChatter.md b/src/components/DatawrapperChart/stories/docs/withChatter.md new file mode 100644 index 00000000..2459808b --- /dev/null +++ b/src/components/DatawrapperChart/stories/docs/withChatter.md @@ -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. diff --git a/src/components/EmbedPreviewerLink/EmbedPreviewerLink.stories.svelte b/src/components/EmbedPreviewerLink/EmbedPreviewerLink.stories.svelte new file mode 100644 index 00000000..627ce816 --- /dev/null +++ b/src/components/EmbedPreviewerLink/EmbedPreviewerLink.stories.svelte @@ -0,0 +1,31 @@ + + + + + + + diff --git a/src/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte b/src/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte new file mode 100644 index 00000000..29d24078 --- /dev/null +++ b/src/components/EmbedPreviewerLink/EmbedPreviewerLink.svelte @@ -0,0 +1,30 @@ + + +{#if dev} +
+ + + +
+{/if} + + + \ No newline at end of file diff --git a/src/components/EmbedPreviewerLink/stories/docs/component.md b/src/components/EmbedPreviewerLink/stories/docs/component.md new file mode 100644 index 00000000..44f08784 --- /dev/null +++ b/src/components/EmbedPreviewerLink/stories/docs/component.md @@ -0,0 +1,11 @@ +An embed tool for development in graphics kit. + +```svelte + + + +``` diff --git a/src/components/EndNotes/EndNotes.stories.svelte b/src/components/EndNotes/EndNotes.stories.svelte new file mode 100644 index 00000000..0e2682b4 --- /dev/null +++ b/src/components/EndNotes/EndNotes.stories.svelte @@ -0,0 +1,37 @@ + + + + + + + diff --git a/src/components/EndNotes/EndNotes.svelte b/src/components/EndNotes/EndNotes.svelte new file mode 100644 index 00000000..ffd16866 --- /dev/null +++ b/src/components/EndNotes/EndNotes.svelte @@ -0,0 +1,16 @@ + + +
+ {#if text} + {@html marked.parse(text)} + {/if} +
diff --git a/src/components/EndNotes/stories/docs/component.md b/src/components/EndNotes/stories/docs/component.md new file mode 100644 index 00000000..3df5b030 --- /dev/null +++ b/src/components/EndNotes/stories/docs/component.md @@ -0,0 +1,17 @@ +End notes section. + +```svelte + + + +``` diff --git a/src/components/FeaturePhoto/FeaturePhoto.stories.svelte b/src/components/FeaturePhoto/FeaturePhoto.stories.svelte new file mode 100644 index 00000000..826a43e7 --- /dev/null +++ b/src/components/FeaturePhoto/FeaturePhoto.stories.svelte @@ -0,0 +1,56 @@ + + + + + + + + + diff --git a/src/components/FeaturePhoto/FeaturePhoto.svelte b/src/components/FeaturePhoto/FeaturePhoto.svelte new file mode 100644 index 00000000..96b89965 --- /dev/null +++ b/src/components/FeaturePhoto/FeaturePhoto.svelte @@ -0,0 +1,115 @@ + + +
+ {#if !lazy || (intersectable && intersecting)} + {altText} + {:else} +
+ {/if} + {#if caption} +
{caption}
+ {/if} + {#if (!altText)} +
Missing altText
+ {/if} +
+ + diff --git a/src/components/FeaturePhoto/stories/docs/component.md b/src/components/FeaturePhoto/stories/docs/component.md new file mode 100644 index 00000000..ea8aa327 --- /dev/null +++ b/src/components/FeaturePhoto/stories/docs/component.md @@ -0,0 +1,16 @@ +A full-width photo inside the text well. + +```svelte + + + +``` diff --git a/src/components/FeaturePhoto/stories/docs/missingAltText.md b/src/components/FeaturePhoto/stories/docs/missingAltText.md new file mode 100644 index 00000000..48016d3e --- /dev/null +++ b/src/components/FeaturePhoto/stories/docs/missingAltText.md @@ -0,0 +1 @@ +If your photo is missing `altText` a small warning will overlay the image. diff --git a/src/components/FeaturePhoto/stories/shark.jpg b/src/components/FeaturePhoto/stories/shark.jpg new file mode 100644 index 00000000..5b54e9f3 Binary files /dev/null and b/src/components/FeaturePhoto/stories/shark.jpg differ diff --git a/src/components/Framer/Framer.stories.svelte b/src/components/Framer/Framer.stories.svelte new file mode 100644 index 00000000..b7b11dbd --- /dev/null +++ b/src/components/Framer/Framer.stories.svelte @@ -0,0 +1,33 @@ + + + + + + + diff --git a/src/components/Framer/Framer.svelte b/src/components/Framer/Framer.svelte new file mode 100644 index 00000000..8903e39c --- /dev/null +++ b/src/components/Framer/Framer.svelte @@ -0,0 +1,142 @@ + + +
+
+ +
+ + + +
+
+ + + + + + diff --git a/src/components/Framer/Resizer/index.svelte b/src/components/Framer/Resizer/index.svelte new file mode 100644 index 00000000..7e950af5 --- /dev/null +++ b/src/components/Framer/Resizer/index.svelte @@ -0,0 +1,228 @@ + + + + +
+
+
+ {pixelLabel || $width}px +
+ +
+
+
+
+ +
+
+ + diff --git a/src/components/Framer/stores.js b/src/components/Framer/stores.js new file mode 100644 index 00000000..69e7100b --- /dev/null +++ b/src/components/Framer/stores.js @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store'; + +export const width = writable(660); diff --git a/src/components/Framer/stories/docs/component.md b/src/components/Framer/stories/docs/component.md new file mode 100644 index 00000000..fc2c8eb2 --- /dev/null +++ b/src/components/Framer/stories/docs/component.md @@ -0,0 +1,11 @@ +An embed tool for development in the Graphics Kit. + +```svelte + + + +``` diff --git a/src/components/Framer/uniqNames.js b/src/components/Framer/uniqNames.js new file mode 100644 index 00000000..0a912806 --- /dev/null +++ b/src/components/Framer/uniqNames.js @@ -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('/'); + }); +}; diff --git a/src/components/Headline/Headline.stories.svelte b/src/components/Headline/Headline.stories.svelte new file mode 100644 index 00000000..50f6982f --- /dev/null +++ b/src/components/Headline/Headline.stories.svelte @@ -0,0 +1,69 @@ + + + + + + + + + + + + By Peppa Pig + + Published Jan. 1, 2020 + + + + + + + Illustration of Europe + +

Europa

+ Published Jan. 1, 2020 +
+
\ No newline at end of file diff --git a/src/components/Headline/Headline.svelte b/src/components/Headline/Headline.svelte new file mode 100644 index 00000000..fa41812d --- /dev/null +++ b/src/components/Headline/Headline.svelte @@ -0,0 +1,61 @@ + + +
+ + +
+ {#if section} +

{section}

+ {/if} + {#if $$slots.hed} + + + {:else} +

{hed}

+ {/if} + {#if dek} +

{dek}

+ {/if} +
+ {#if ($$slots.byline || $$slots.dateline)} + + {/if} +
+ + + diff --git a/src/components/Headline/stories/crown.png b/src/components/Headline/stories/crown.png new file mode 100644 index 00000000..c6bec93e Binary files /dev/null and b/src/components/Headline/stories/crown.png differ diff --git a/src/components/Headline/stories/docs/component.md b/src/components/Headline/stories/docs/component.md new file mode 100644 index 00000000..1ca969f3 --- /dev/null +++ b/src/components/Headline/stories/docs/component.md @@ -0,0 +1,14 @@ +Reuters Graphics headline + +```svelte + + + +``` diff --git a/src/components/Headline/stories/docs/withByline.md b/src/components/Headline/stories/docs/withByline.md new file mode 100644 index 00000000..650bb74e --- /dev/null +++ b/src/components/Headline/stories/docs/withByline.md @@ -0,0 +1,19 @@ +Add a byline and dateline with `byline` and `dateline` named slots. + +```svelte + + + + + By Peppa Pig + + Published Jan. 1, 2020 + +``` diff --git a/src/components/Headline/stories/docs/withCrown.md b/src/components/Headline/stories/docs/withCrown.md new file mode 100644 index 00000000..a2c2740e --- /dev/null +++ b/src/components/Headline/stories/docs/withCrown.md @@ -0,0 +1,16 @@ +Add a crown image in the `crown` named slot and override the headline in the `hed` named slot. + +```svelte + + + + + + +

Europa

+ Published Jan. 1, 2020 +
+``` diff --git a/src/components/Hero/Hero.stories.svelte b/src/components/Hero/Hero.stories.svelte new file mode 100644 index 00000000..adc6d449 --- /dev/null +++ b/src/components/Hero/Hero.stories.svelte @@ -0,0 +1,37 @@ + + + + + + + \ No newline at end of file diff --git a/src/components/Hero/Hero.svelte b/src/components/Hero/Hero.svelte new file mode 100644 index 00000000..482fbdb0 --- /dev/null +++ b/src/components/Hero/Hero.svelte @@ -0,0 +1,84 @@ + + + +
+
+ {#if $$slots.image} + + {:else} + {imgAltText} + {/if} + {#if overlay} +
+ {/if} +
+
+ {#if section} +

{section}

+ {/if} + + {#if $$slots.hed} + + {:else} +

{hed}

+ {/if} + + {#if $$slots.dek} + + {:else} + {#if dek} +

{dek}

+ {/if} + {/if} +
+
+ diff --git a/src/components/Hero/docs.svx b/src/components/Hero/docs.svx new file mode 100644 index 00000000..1c0c8ce1 --- /dev/null +++ b/src/components/Hero/docs.svx @@ -0,0 +1,113 @@ +--- +title: Hero +description: A headline over a hero image +slug: hero +--- + + + +
+ +## {title} + +{description} + +
+ +```svelte + + + + A polar bear + +``` + + + + A polar bear + + + +```svelte + + + + + A polar bear + +``` + + + + A polar bear + + + +```svelte + + + + A polar bear + +

+ Eisbaer +

+

+ The last of the white bears +

+
+``` + + + + A polar bear +

Eisbaer

+

The last of the white bears

+
+
+ + diff --git a/src/components/Hero/stories/docs/component.md b/src/components/Hero/stories/docs/component.md new file mode 100644 index 00000000..4b70b7af --- /dev/null +++ b/src/components/Hero/stories/docs/component.md @@ -0,0 +1,17 @@ + +> 🔨 **Under construction**: We're working on this component to make it better. Pardon our mess. + +Reuters Graphics headline + +```svelte + + + +``` diff --git a/src/components/Hero/stories/docs/withByline.md b/src/components/Hero/stories/docs/withByline.md new file mode 100644 index 00000000..650bb74e --- /dev/null +++ b/src/components/Hero/stories/docs/withByline.md @@ -0,0 +1,19 @@ +Add a byline and dateline with `byline` and `dateline` named slots. + +```svelte + + + + + By Peppa Pig + + Published Jan. 1, 2020 + +``` diff --git a/src/components/Hero/stories/docs/withCrown.md b/src/components/Hero/stories/docs/withCrown.md new file mode 100644 index 00000000..a2c2740e --- /dev/null +++ b/src/components/Hero/stories/docs/withCrown.md @@ -0,0 +1,16 @@ +Add a crown image in the `crown` named slot and override the headline in the `hed` named slot. + +```svelte + + + + + + +

Europa

+ Published Jan. 1, 2020 +
+``` diff --git a/src/components/Hero/stories/polar.jpg b/src/components/Hero/stories/polar.jpg new file mode 100644 index 00000000..fe7cac61 Binary files /dev/null and b/src/components/Hero/stories/polar.jpg differ diff --git a/src/components/PymChild/PymChild.stories.svelte b/src/components/PymChild/PymChild.stories.svelte new file mode 100644 index 00000000..07083a63 --- /dev/null +++ b/src/components/PymChild/PymChild.stories.svelte @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/components/PymChild/PymChild.svelte b/src/components/PymChild/PymChild.svelte new file mode 100644 index 00000000..a0232ba2 --- /dev/null +++ b/src/components/PymChild/PymChild.svelte @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/src/components/PymChild/stories/docs/component.md b/src/components/PymChild/stories/docs/component.md new file mode 100644 index 00000000..56138a8a --- /dev/null +++ b/src/components/PymChild/stories/docs/component.md @@ -0,0 +1,9 @@ +A Pym.js child instance for embeddables. + +```svelte + + + +``` diff --git a/src/components/ReutersLogo/ReutersLogo.stories.svelte b/src/components/ReutersLogo/ReutersLogo.stories.svelte new file mode 100644 index 00000000..143948d7 --- /dev/null +++ b/src/components/ReutersLogo/ReutersLogo.stories.svelte @@ -0,0 +1,32 @@ + + + + + + + diff --git a/src/components/ReutersLogo/ReutersLogo.svelte b/src/components/ReutersLogo/ReutersLogo.svelte new file mode 100644 index 00000000..e218a9bf --- /dev/null +++ b/src/components/ReutersLogo/ReutersLogo.svelte @@ -0,0 +1,22 @@ + + + diff --git a/src/components/ReutersLogo/stories/docs/component.md b/src/components/ReutersLogo/stories/docs/component.md new file mode 100644 index 00000000..ea1aa851 --- /dev/null +++ b/src/components/ReutersLogo/stories/docs/component.md @@ -0,0 +1,9 @@ +The official home of the Reuters logo. + +```svelte + + + +``` diff --git a/src/components/SEO/analytics.js b/src/components/SEO/analytics.js new file mode 100644 index 00000000..a2606fe4 --- /dev/null +++ b/src/components/SEO/analytics.js @@ -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 */ diff --git a/src/components/SEO/docs.svx b/src/components/SEO/docs.svx new file mode 100644 index 00000000..7be76e28 --- /dev/null +++ b/src/components/SEO/docs.svx @@ -0,0 +1,29 @@ +--- +title: SEO +description: Add SEO to the page. +slug: seo +--- + +
+ +## {title} + +{description} + +
+ +```svelte + + + +``` diff --git a/src/components/SEO/index.svelte b/src/components/SEO/index.svelte new file mode 100644 index 00000000..ac5c4d51 --- /dev/null +++ b/src/components/SEO/index.svelte @@ -0,0 +1,160 @@ + + + + + {seoTitle} + + + + + + + + + + + + + + + + + + + + + + + + + + + + {@html `<${'script'} type="application/ld+json">${JSON.stringify( + orgLdJson + )}`} + {@html `<${'script'} type="application/ld+json">${JSON.stringify( + articleLdJson + )}`} + diff --git a/src/components/SEO/publisherTags.js b/src/components/SEO/publisherTags.js new file mode 100644 index 00000000..3d38a0b2 --- /dev/null +++ b/src/components/SEO/publisherTags.js @@ -0,0 +1,26 @@ +export default () => { + const googletag = window.googletag || {}; + googletag.cmd = googletag.cmd || []; + (function() { + const gads = document.createElement('script'); + gads.async = true; + gads.type = 'text/javascript'; + const useSSL = document.location.protocol === 'https:'; + gads.src = + (useSSL ? 'https:' : 'http:') + + '//www.googletagservices.com/tag/js/gpt.js'; + const node = document.getElementsByTagName('script')[0]; + node.parentNode.insertBefore(gads, node); + })(); + googletag.cmd.push(function() { + googletag + .defineSlot( + '/4735792/reuters_investigates', + [[300, 250]], + 'div-gpt-ad-1441822201033-0' + ) + .addService(googletag.pubads()); + googletag.pubads().enableSingleRequest(); + googletag.enableServices(); + }); +}; diff --git a/src/components/Scroller/Background.svelte b/src/components/Scroller/Background.svelte new file mode 100644 index 00000000..5e08553b --- /dev/null +++ b/src/components/Scroller/Background.svelte @@ -0,0 +1,34 @@ + + +{#each steps as step, i} + + {#if preload === 0 || (i >= index - preload && i <= index + preload)} +
+ +
+ {/if} +{/each} + + diff --git a/src/components/Scroller/Embedded/Background.svelte b/src/components/Scroller/Embedded/Background.svelte new file mode 100644 index 00000000..b3f7eb7b --- /dev/null +++ b/src/components/Scroller/Embedded/Background.svelte @@ -0,0 +1,14 @@ + + +
+
+ +
+
diff --git a/src/components/Scroller/Embedded/Foreground.svelte b/src/components/Scroller/Embedded/Foreground.svelte new file mode 100644 index 00000000..7f74f7e3 --- /dev/null +++ b/src/components/Scroller/Embedded/Foreground.svelte @@ -0,0 +1,31 @@ + + +{#if step.foreground === '' || !step.foreground} + +
+{:else if typeof step.foreground === 'string'} +
+
+ {@html marked.parse(step.foreground)} +
+
+{:else} +
+ +
+{/if} + + + diff --git a/src/components/Scroller/Embedded/index.svelte b/src/components/Scroller/Embedded/index.svelte new file mode 100644 index 00000000..a27644d3 --- /dev/null +++ b/src/components/Scroller/Embedded/index.svelte @@ -0,0 +1,21 @@ + + +{#each steps as step, index} + + {#if embeddedLayout === 'bf'} + + + + {:else} + + + {/if} +{/each} \ No newline at end of file diff --git a/src/components/Scroller/Foreground.svelte b/src/components/Scroller/Foreground.svelte new file mode 100644 index 00000000..e62f6230 --- /dev/null +++ b/src/components/Scroller/Foreground.svelte @@ -0,0 +1,46 @@ + + +{#each steps as step, i} +
+ {#if step.foreground === '' || !step.foreground} + +
+ {:else} +
+ {#if typeof step.foreground === 'string'} + {@html marked.parse(step.foreground)} + {:else} + + {/if} +
+ {/if} +
+{/each} + + + diff --git a/src/components/Scroller/demos/ai2html/ai-scroller-1.exclude.svelte b/src/components/Scroller/demos/ai2html/ai-scroller-1.exclude.svelte new file mode 100644 index 00000000..5d04181a --- /dev/null +++ b/src/components/Scroller/demos/ai2html/ai-scroller-1.exclude.svelte @@ -0,0 +1,120 @@ + + + + +
+ + {#if width && width >= 1200} +
+
+
+
+ {/if} + + {#if width && width >= 930 && width < 1200} +
+
+
+
+ {/if} + + {#if width && width >= 660 && width < 930} +
+
+
+
+ {/if} + + {#if width && width >= 510 && width < 660} +
+
+
+
+ {/if} + + {#if width && width >= 0 && width < 510} +
+
+
+
+ {/if} +
+ + + + + + diff --git a/src/components/Scroller/demos/ai2html/ai-scroller-2.exclude.svelte b/src/components/Scroller/demos/ai2html/ai-scroller-2.exclude.svelte new file mode 100644 index 00000000..bdd0e9ca --- /dev/null +++ b/src/components/Scroller/demos/ai2html/ai-scroller-2.exclude.svelte @@ -0,0 +1,265 @@ + + + + +
+ + {#if width && width >= 1200} +
+
+
+
+

This thing here is

+

particularly important

+
+
+ {/if} + + {#if width && width >= 930 && width < 1200} +
+
+
+
+

This thing here is

+

particularly important

+
+
+ {/if} + + {#if width && width >= 660 && width < 930} +
+
+
+
+

This thing here is

+

particularly important

+
+
+ {/if} + + {#if width && width >= 510 && width < 660} +
+
+
+
+

This thing here is

+

particularly important

+
+
+ {/if} + + {#if width && width >= 0 && width < 510} +
+
+
+
+

This thing here is

+

particularly important

+
+
+ {/if} +
+ + + + + + diff --git a/src/components/Scroller/demos/ai2html/ai-scroller-3.exclude.svelte b/src/components/Scroller/demos/ai2html/ai-scroller-3.exclude.svelte new file mode 100644 index 00000000..b1143865 --- /dev/null +++ b/src/components/Scroller/demos/ai2html/ai-scroller-3.exclude.svelte @@ -0,0 +1,265 @@ + + + + +
+ + {#if width && width >= 1200} +
+
+
+
+

Something

+

happened here

+
+
+ {/if} + + {#if width && width >= 930 && width < 1200} +
+
+
+
+

Something

+

happened here

+
+
+ {/if} + + {#if width && width >= 660 && width < 930} +
+
+
+
+

Something

+

happened here

+
+
+ {/if} + + {#if width && width >= 510 && width < 660} +
+
+
+
+

Something

+

happened here

+
+
+ {/if} + + {#if width && width >= 0 && width < 510} +
+
+
+
+

Something

+

happened here

+
+
+ {/if} +
+ + + + + + diff --git a/src/components/Scroller/demos/basic/_Clicker.svelte b/src/components/Scroller/demos/basic/_Clicker.svelte new file mode 100644 index 00000000..569f251e --- /dev/null +++ b/src/components/Scroller/demos/basic/_Clicker.svelte @@ -0,0 +1,9 @@ + + +

Interactive step

+ +

The count is {count}

+ + \ No newline at end of file diff --git a/src/components/Scroller/demos/basic/_Stacked.svelte b/src/components/Scroller/demos/basic/_Stacked.svelte new file mode 100644 index 00000000..8cfddc1a --- /dev/null +++ b/src/components/Scroller/demos/basic/_Stacked.svelte @@ -0,0 +1,30 @@ + + +
+
+
+ + + \ No newline at end of file diff --git a/src/components/Scroller/demos/basic/_Step.svelte b/src/components/Scroller/demos/basic/_Step.svelte new file mode 100644 index 00000000..332faf38 --- /dev/null +++ b/src/components/Scroller/demos/basic/_Step.svelte @@ -0,0 +1,13 @@ + +
+ + \ No newline at end of file diff --git a/src/components/Scroller/docs.svx b/src/components/Scroller/docs.svx new file mode 100644 index 00000000..a78c6ccb --- /dev/null +++ b/src/components/Scroller/docs.svx @@ -0,0 +1,459 @@ +--- +title: Scroller +description: Easy scrollytelling with layout options. +slug: scroller +--- + + + +
+ +## {title} + +Easy scrollytelling. + +
+ +This component is designed to handle most common layouts for scrollytelling. If you need something more complex, though, you should use [svelte-scroller](https://github.com/sveltejs/svelte-scroller), which is a lower level component you can more easily customize. + +
+ +`steps` _an array of objects_ + +- `background` A background component **REQUIRED** +- `backgroundProps` An object of props given to background component +- `foreground` Either a markdown string or a foreground component **REQUIRED** +- `foregroundProps` An object of props given to foreground component + +`backgroundSize` + +- Adjusts the size of the background graphic by passing a class name corresponding to one of our well widths: `normal`, `wide`, `wider`, `widest` or `fluid`. + +
+ +```svelte + + + +``` + + + + + +
+ +Control the layout of your component with the `foregroundPosition` prop. + +`foregroundPosition` + +- `middle` Text and graphic middle _default_ +- `left` Text to left, graphic stays middle +- `right` Text to right, graphic stays middle +- `left opposite` Text to left, graphic to right +- `right opposite` Text to right, graphic to left + +Both text and graphic will revert to the middle when the window drops below 1200px. + +**Note:** Make sure your `backgroundSize` prop is _not_ set to `fluid` if using either left or right opposite option. + +
+ +```svelte + + + +``` + + + + + +
+ +`stackBackground` + +- Determines whether previous background step graphics will stack below current one. +- `true` _default_ Background graphics from previous steps will remain visible below active one, allowing you to stack graphics with transparent backgrounds. +- `false` Only the background graphic from the current step will show and backgrounds from previous steps are hidden. + +`preload` + +- Determines how many background steps to load before and after the currently active one. This prop basically lazyloads backgrounds, which is useful for making sure you're not loading too many images at once for long scrolls. +- Default is `1`, which loads one background before and one after the currently active one. +- `0` disables lazyloading and loads all backgrounds at once. + +
+ +```svelte + +``` + + + + + +
+ +Pass a component to `foreground` and use `backgroundProps` and `foregroundProps` to pass data to your components. + +
+ +```svelte + + + +``` + + + + + +
+ +Add an ID to your scroller and use SCSS `:global` rules to override any styles. + +
+ +This is the main way you should customise this component for any layouts not covered by the props we've already mentioned. Most small layout shifts can be handled with simple SCSS. Check out the component in your browser's inspector to find which elements you want to target with custom styles. + +If you're still struggling to make this component work for your design, it may be a good sign you should reach for [svelte-scroller](https://github.com/sveltejs/svelte-scroller) instead. + +
+ +
+ +```svelte + + + + +``` + + + + + +
+ +You can also use this component to layout an AI-based graphics scroller via an [ArchieML](http://archieml.org/)-formatted Google doc by using the following pattern to dynamically import [ai2svelte](https://github.com/reuters-graphics/ai2svelte) components for the background: + +
+ +If you're using the [graphics kit](https://github.com/reuters-graphics/bluprint_graphics-kit), this pattern is already wired up in the boilerplate included in the Page.svelte component. Free money! + +
+ +
+ +```bash +# In your Google doc... +[blocks] + +# ... +Type: ai-scroller +ID: my-scroller +BackgroundSize: fluid +ForegroundPosition: middle + +[.steps] +Background: ai-scroller-1 +Foreground: \#\#\#\# Step 1 + +Lorem ipsum... +:end + +Background: ai-scroller-2 +Foreground: \#\#\#\# Step 2 + +Lorem ipsum... +:end + +Background: ai-scroller-3 +Foreground: \#\#\#\# Step 3 + +Lorem ipsum... +:end + +[] + +[] +``` + +```svelte + + + +{#each content.blocks as block} + {#if block.Type === 'ai-scroller'} + {#await makeScrollerSteps(block.steps)} +
+ {:then steps} + + {:catch error} + {console.error('Error making steps for scroller', error)} + {/await} + {/if} +{/each} +``` + +
+ +This pattern 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. +
+ +
+ +##### Tips +Each step needs its own .ai file unless you want to show the same graphic twice. The .ai file can be named anything -- just make sure you add the correct file name, without the .ai extension, to the google doc. + +Tip: First, make one master .ai file that contains graphics for all steps. Put graphics for each step into its own layer, which you might name step-1, step-2, etc. When you are ready to export, create separate .ai files for each step by hiding all but one layer and choosing save as. This makes it easier to manage multiple files and make sure graphics in different steps are aligned. + +Example workflow: + +Make maps-scroll-master.ai +Make graphics for each step and put them in separate layers: step-1, step-2, etc. +Once all graphics are ready, show just the step-1 layer and hide all others. Save the file as maps-scroll-1.ai, then run the ai2svelte script. In the google doc, set Background: maps-scroll-1 for step 1. +Do the same for all other layers + +
+ + + {#await makeSteps(ai2svelteSteps) then steps} + + {/await} + + +
+ +This component can also unravel the scrolling experience into a flat, linear layout. Use this when the graphic will be embedded in an iframe. + +`embedded` + +- Setting to `true` will unroll the scroll experience into a flat layout. `true` will also cause the `foregroundPosition` prop to be ignored. (Use global SCSS to adjust the style of foreground text.) + +`embeddedLayout` + +- `fb` (default) will layout the foreground before the background in each step. +- `bf` will lead with the background in each step. + +
+ +```svelte + +``` + + + {#await makeSteps(ai2svelteSteps) then steps} + + {/await} + + + \ No newline at end of file diff --git a/src/components/Scroller/index.svelte b/src/components/Scroller/index.svelte new file mode 100644 index 00000000..6faf60b1 --- /dev/null +++ b/src/components/Scroller/index.svelte @@ -0,0 +1,155 @@ + + +{#if !embedded} +
+ +
+
+
+ +
+
+
+ +
+ +
+
+
+{:else} +
+ +
+{/if} + + diff --git a/src/components/Section/Section.stories.svelte b/src/components/Section/Section.stories.svelte new file mode 100644 index 00000000..6543d70b --- /dev/null +++ b/src/components/Section/Section.stories.svelte @@ -0,0 +1,149 @@ + + + + + + + + + +
+ +
+
+
+ Column +
+
+ Column +
+
+ Column +
+
+
+
+ Column +
+
+ Column +
+
+
+
+
+ + +
+
+
Article
+
narrower
+
narrow
+
normal
+
wide
+
wider
+
narrower
+
narrow
+
normal.skip-narrow
+
wide.skip-normal.skip-narrow
+
wider.skip-wide
+
+
+
+ + + \ No newline at end of file diff --git a/src/components/Section/Section.svelte b/src/components/Section/Section.svelte new file mode 100644 index 00000000..f441ebd5 --- /dev/null +++ b/src/components/Section/Section.svelte @@ -0,0 +1,77 @@ + + +
+ + +
+ + diff --git a/src/components/Section/stories/docs/component.md b/src/components/Section/stories/docs/component.md new file mode 100644 index 00000000..6a447385 --- /dev/null +++ b/src/components/Section/stories/docs/component.md @@ -0,0 +1,14 @@ +The `Section` component is the basic building block of stories. + +> 📌 Many of our other components already use the `Section` component, internally. You'll usually only need to use it yourself if you're making something custom. + +```svelte + + + +
+ +
+``` diff --git a/src/components/Section/stories/docs/customLayouts.md b/src/components/Section/stories/docs/customLayouts.md new file mode 100644 index 00000000..9529c0c2 --- /dev/null +++ b/src/components/Section/stories/docs/customLayouts.md @@ -0,0 +1,13 @@ +Our article well is designed to handle basic page layout when you're not trying to think about it, but it's also made to get out of the way quickly when you need to do something custom. + +If you need to get really radical, the easiest way is to create a `Section` with a `fluid` width -- which basically cancels out the article well dimensions -- and then code whatever you need from scratch or with another framework. + +```svelte +
+
+ +
+
+``` + +The demo below does exactly that to create an edge-to-edge grid with [bootstrap](https://getbootstrap.com/docs/5.2/layout/grid/). diff --git a/src/components/Section/stories/docs/snapWidths.md b/src/components/Section/stories/docs/snapWidths.md new file mode 100644 index 00000000..1a307136 --- /dev/null +++ b/src/components/Section/stories/docs/snapWidths.md @@ -0,0 +1,50 @@ +Normally, `Section` containers resize fluidly below the original `width`. Sometimes, though, you may want the container to snap to the fixed widths of the article well -- for example, if you have a static graphic that looks fine at the set column breakpoints, but isn't so great at widths inbetween. + +You can use the `snap` prop to force the container to snap to each column width successively as the window sizes down. + +```svelte +
+ +
+``` + +If you want to skip certain column widths entirely, you can add one or more class of `skip-{column width}` to the `Section`. + +```svelte + +
+ +
+``` + +This is probably easier to see in action than explain in words, so check out the "Canvas" tab for the demo below and resize the window to get a better picture of how it all works. + +> **NOTE:** The snap width breakpoints only work on `Section` components with widths `wider` and below. `widest` and `fluid` are both fluidly responsive widths by default, since they go edge-to-edge. + +#### Using with custom column widths + +Snap width breakpoints are hard-coded to the default article well column widths, so if you set custom `columnWidths` on the `Article` tag, you can't use this functionality without a little extra work. + +Luckily, it's still pretty easy. Just add a `cls` or `id` to your `Section` so you can target it with some custom SCSS. Now, defined a few SCSS variables corresponding to your custom column widths and use the `snap-columns` SCSS mixin to get the same functionality at your custom breakpoints. + +```svelte +
+ +
+ + +``` diff --git a/src/components/Sharer/Sharer.stories.svelte b/src/components/Sharer/Sharer.stories.svelte new file mode 100644 index 00000000..f487ba20 --- /dev/null +++ b/src/components/Sharer/Sharer.stories.svelte @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/components/Sharer/Sharer.svelte b/src/components/Sharer/Sharer.svelte new file mode 100644 index 00000000..8651c74c --- /dev/null +++ b/src/components/Sharer/Sharer.svelte @@ -0,0 +1,153 @@ + + + +
+
+ + + +
+
+ + + \ No newline at end of file diff --git a/src/components/Sharer/stories/docs/component.md b/src/components/Sharer/stories/docs/component.md new file mode 100644 index 00000000..abd100a3 --- /dev/null +++ b/src/components/Sharer/stories/docs/component.md @@ -0,0 +1,9 @@ +Add share tools to a page. + +```svelte + + + +``` diff --git a/src/components/Sharer/utils/copyToClipboard.js b/src/components/Sharer/utils/copyToClipboard.js new file mode 100644 index 00000000..21d1470f --- /dev/null +++ b/src/components/Sharer/utils/copyToClipboard.js @@ -0,0 +1,10 @@ +const copyToClipboard = (str) => { + const el = document.createElement('textarea'); + el.value = str; + document.body.appendChild(el); + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); +}; + +export default copyToClipboard; diff --git a/src/components/Sharer/utils/facebook.js b/src/components/Sharer/utils/facebook.js new file mode 100644 index 00000000..c1e1d3c8 --- /dev/null +++ b/src/components/Sharer/utils/facebook.js @@ -0,0 +1,22 @@ +import { getHref } from './meta'; + +const handlePost = () => { + const windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes'; + const width = 550; + const height = 420; + const winHeight = screen.height; + const winWidth = screen.width; + + const left = Math.round(winWidth / 2 - width / 2); + const top = winHeight > height ? Math.round(winHeight / 2 - height / 2) : 0; + + const target = `https://www.facebook.com/sharer/sharer.php?u=${getHref()}`; + + window.open( + target, + 'intent', + `${windowOptions},width=${width},height=${height},left=${left},top=${top}` + ); +}; + +export default handlePost; diff --git a/src/components/Sharer/utils/meta.js b/src/components/Sharer/utils/meta.js new file mode 100644 index 00000000..7473523f --- /dev/null +++ b/src/components/Sharer/utils/meta.js @@ -0,0 +1,7 @@ +export const getTitle = () => { + const ogTag = document.querySelector("meta[property='og:title']"); + if (ogTag) return encodeURIComponent(ogTag.getAttribute('content')); + return encodeURIComponent(document.title); +}; + +export const getHref = () => encodeURIComponent(window.location.href); diff --git a/src/components/Sharer/utils/twitter.js b/src/components/Sharer/utils/twitter.js new file mode 100644 index 00000000..129959da --- /dev/null +++ b/src/components/Sharer/utils/twitter.js @@ -0,0 +1,22 @@ +import { getHref, getTitle } from './meta'; + +const handleTweet = () => { + const windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes'; + const width = 550; + const height = 420; + const winHeight = screen.height; + const winWidth = screen.width; + + const left = Math.round(winWidth / 2 - width / 2); + const top = winHeight > height ? Math.round(winHeight / 2 - height / 2) : 0; + + const target = `https://twitter.com/intent/tweet?text=${getTitle()}&via=Reuters&url=${getHref()}`; + + window.open( + target, + 'intent', + `${windowOptions},width=${width},height=${height},left=${left},top=${top}` + ); +}; + +export default handleTweet; diff --git a/src/components/SiteFooter/CompanyLinks.svelte b/src/components/SiteFooter/CompanyLinks.svelte new file mode 100644 index 00000000..3e6f2db7 --- /dev/null +++ b/src/components/SiteFooter/CompanyLinks.svelte @@ -0,0 +1,160 @@ + + +{#if links.social_links} +
+
+
+

Information you can trust

+

{links.company_description}

+
+ +
+
+{/if} + + diff --git a/src/components/SiteFooter/LegalLinks.svelte b/src/components/SiteFooter/LegalLinks.svelte new file mode 100644 index 00000000..c5e09d02 --- /dev/null +++ b/src/components/SiteFooter/LegalLinks.svelte @@ -0,0 +1,167 @@ + + +{#if links.ad_links} + +{/if} + + diff --git a/src/components/SiteFooter/QuickLinks.svelte b/src/components/SiteFooter/QuickLinks.svelte new file mode 100644 index 00000000..9eaf7e9e --- /dev/null +++ b/src/components/SiteFooter/QuickLinks.svelte @@ -0,0 +1,237 @@ + + + + +{#if links.latest_links} + +{/if} + + diff --git a/src/components/SiteFooter/Referrals/IntersectionObserver.svelte b/src/components/SiteFooter/Referrals/IntersectionObserver.svelte new file mode 100644 index 00000000..d2a68d41 --- /dev/null +++ b/src/components/SiteFooter/Referrals/IntersectionObserver.svelte @@ -0,0 +1,45 @@ + + +
+ +
diff --git a/src/components/SiteFooter/Referrals/Link.svelte b/src/components/SiteFooter/Referrals/Link.svelte new file mode 100644 index 00000000..8410a45e --- /dev/null +++ b/src/components/SiteFooter/Referrals/Link.svelte @@ -0,0 +1,78 @@ + + + + + + diff --git a/src/components/SiteFooter/Referrals/Referrals.svelte b/src/components/SiteFooter/Referrals/Referrals.svelte new file mode 100644 index 00000000..3169a466 --- /dev/null +++ b/src/components/SiteFooter/Referrals/Referrals.svelte @@ -0,0 +1,56 @@ + + +{#if metadata} + +{/if} + + diff --git a/src/components/SiteFooter/Referrals/index.svelte b/src/components/SiteFooter/Referrals/index.svelte new file mode 100644 index 00000000..5fba2dab --- /dev/null +++ b/src/components/SiteFooter/Referrals/index.svelte @@ -0,0 +1,43 @@ + + + + {#if intersecting} +
+

+ More from Reuters Graphics +

+ {#if (typeof window !== 'undefined')} + + {/if} +
+ {/if} +
+ + diff --git a/src/components/SiteFooter/SiteFooter.stories.svelte b/src/components/SiteFooter/SiteFooter.stories.svelte new file mode 100644 index 00000000..1f07a653 --- /dev/null +++ b/src/components/SiteFooter/SiteFooter.stories.svelte @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/SiteFooter/SiteFooter.svelte b/src/components/SiteFooter/SiteFooter.svelte new file mode 100644 index 00000000..6eeda362 --- /dev/null +++ b/src/components/SiteFooter/SiteFooter.svelte @@ -0,0 +1,78 @@ + + + + +
+
+ + + + +
+
+ + diff --git a/src/components/SiteFooter/data.json b/src/components/SiteFooter/data.json new file mode 100644 index 00000000..ea1bf722 --- /dev/null +++ b/src/components/SiteFooter/data.json @@ -0,0 +1,210 @@ +[ + { + "company_description": "Reuters, the news and media division of Thomson Reuters, is the world’s largest multimedia news provider, reaching billions of people worldwide every day. Reuters provides business, financial, national and international news to professionals via desktop terminals, the world's media organizations, industry events and directly to consumers.", + "disclaimer_link": "https://www.reuters.com/info-pages/disclaimer/", + "copyright_link": "https://www.thomsonreuters.com/en/policies/copyright.html", + "copyright_year": "2022", + "latest_links": [ + { + "text": "Home", + "url": "/", + "self": true + } + ], + "browse_links": [ + { + "text": "World", + "url": "/world/", + "self": true + }, + { + "text": "Business", + "url": "/business/", + "self": true + }, + { + "text": "Legal", + "url": "/legal/", + "self": true + }, + { + "text": "Markets", + "url": "/markets/", + "self": true + }, + { + "text": "Breakingviews", + "url": "/breakingviews/", + "self": true + }, + { + "text": "Technology", + "url": "/technology/", + "self": true + }, + { + "text": "Investigations", + "url": "/investigates/" + }, + { + "text": "Lifestyle", + "url": "/lifestyle/", + "self": true + } + ], + "media_links": [ + { + "text": "Videos", + "url": "https://www.reuters.com/video/", + "symbol": "videos" + }, + { + "text": "Pictures", + "url": "https://www.reuters.com/news/pictures", + "symbol": "pictures" + }, + { + "text": "Graphics", + "url": "https://graphics.reuters.com/", + "symbol": "graphics" + } + ], + "about_links": [ + { + "text": "About Reuters", + "url": "https://www.reutersagency.com/en/about/about-us/" + }, + { + "text": "Careers", + "url": "https://www.thomsonreuters.com/en/careers.html" + }, + { + "text": "Reuters News Agency", + "url": "https://www.reutersagency.com/en/?utm_source=website&utm_medium=reuters&utm_campaign=site-referral&utm_content=us&utm_term=0" + }, + { + "text": "Brand Attribution Guidelines", + "url": "https://www.reutersagency.com/en/about/about-us/brand-attribution-guidelines/" + }, + { + "text": "Reuters Leadership", + "url": "https://www.reutersagency.com/en/about/leadership-team/" + }, + { + "text": "Reuters Fact Check", + "url": "https://www.reuters.com/fact-check/" + }, + { + "text": "Reuters Diversity Report", + "url": "https://www.reuters.com/DiversityReportApril2022" + } + ], + "stay_informed_links": [ + { + "text": "Download the App", + "url": "https://www.reuters.com/tools/mobile/us" + }, + { + "text": "Newsletters", + "url": "https://newslink.reuters.com/join/subscribe" + } + ], + "social_links": [ + { + "type": "twitter", + "url": "https://www.twitter.com/Reuters" + }, + { + "type": "facebook", + "url": "https://www.facebook.com/Reuters" + }, + { + "type": "instagram", + "url": "https://www.instagram.com/Reuters" + }, + { + "type": "youtube", + "url": "https://www.youtube.com/user/ReutersVideo" + }, + { + "type": "linkedin", + "url": "https://www.linkedin.com/company/10256858/" + } + ], + "tr_products": [ + { + "name": "Westlaw", + "description": "Build the strongest argument relying on authoritative content, attorney-editor expertise, and industry defining technology.", + "url": "https://legal.thomsonreuters.com/en/products/westlaw" + }, + { + "name": "Onesource", + "description": "The most comprehensive solution to manage all your complex and ever-expanding tax and compliance needs.", + "url": "https://tax.thomsonreuters.com/en/onesource" + }, + { + "name": "Checkpoint", + "description": "The industry leader for online information for tax, accounting and finance professionals.", + "url": "https://tax.thomsonreuters.com/en/checkpoint" + } + ], + "refinitiv_products": [ + { + "name": "Refinitiv Workspace", + "description": " Access unmatched financial data, news and content in a highly-customised workflow experience on desktop, web and mobile.", + "url": " https://www.refinitiv.com/en/products/refinitiv-workspace" + }, + { + "name": "Refinitiv Data Catalogue", + "description": " Browse an unrivalled portfolio of real-time and historical market data and insights from worldwide sources and experts.", + "url": " https://www.refinitiv.com/en/financial-data" + }, + { + "name": "Refinitiv World-Check", + "description": "Screen for heightened risk individual and entities globally to help uncover hidden risks in business relationships and human networks.", + "url": "https://www.refinitiv.com/en/products/world-check-kyc-screening" + } + ], + "ad_links": [ + { + "text": "Advertise With Us", + "url": "https://www.reutersagency.com/en/services/advertising-solutions/" + }, + { + "text": "Advertising Guidelines", + "url": "/info-pages/advertising-guidelines/" + } + ], + "misc_links": [ + { + "text": "Cookies", + "url": "https://www.thomsonreuters.com/en/privacy-statement.html#cookies" + }, + { + "text": "Terms of Use", + "url": "/info-pages/terms-of-use/" + }, + { + "text": "Privacy", + "url": "https://www.thomsonreuters.com/en/privacy-statement.html" + }, + { + "text": "Digital Accessibility", + "url": "https://www.thomsonreuters.com/en/policies/digital-accessibility-policy.html" + }, + { + "text": "Corrections", + "url": "/info-pages/contact-us/" + }, + { + "text": "Site Feedback", + "url": "https://trdigital.iad1.qualtrics.com/jfe/form/SV_8kte8gArGyCGVhz" + }, + { + "text": "Do Not Sell My Personal Information", + "url": "javascript:window.OneTrust.ToggleInfoDisplay();", + "self": "true" + } + ] + } +] \ No newline at end of file diff --git a/src/components/SiteFooter/stories/docs/component.md b/src/components/SiteFooter/stories/docs/component.md new file mode 100644 index 00000000..4f70ed7e --- /dev/null +++ b/src/components/SiteFooter/stories/docs/component.md @@ -0,0 +1,11 @@ +Reuters dotcom site footer with graphics referrals, ported from [Raptor UI components](https://github.com/tr/rcom-arc_raptor-ui/tree/develop/packages/rcom-raptor-ui_common/src/components/site-footer). + +> **Note:** In the [Graphics Kit](https://github.com/reuters-graphics/bluprint_graphics-kit/blob/master/pages/index.svelte), you can find this component in `pages/index.svelte`. Customise it there for the default page. + +```svelte + + + +``` diff --git a/src/components/SiteFooter/stories/docs/customReferrals.md b/src/components/SiteFooter/stories/docs/customReferrals.md new file mode 100644 index 00000000..d9c83559 --- /dev/null +++ b/src/components/SiteFooter/stories/docs/customReferrals.md @@ -0,0 +1,26 @@ +Pass up to four custom referrals to the `referrals` prop. + +```svelte + + + +``` diff --git a/src/components/SiteFooter/stories/docs/darkTheme.md b/src/components/SiteFooter/stories/docs/darkTheme.md new file mode 100644 index 00000000..999724e7 --- /dev/null +++ b/src/components/SiteFooter/stories/docs/darkTheme.md @@ -0,0 +1,26 @@ +Customise colours by passing an object to the `theme` prop. You can customise any or all of the following default properties: + +```javascript +// Customisable colours +{ + background: '#fff', + primary: '#404040', + accent: '#fa6400', + rules: '#d0d0d0', + shadow: '0 1px 4px 2px rgb(64 64 64 / 8%)', +} +``` + +The component also exports some preset themes you can use or customise: + +```svelte + + + + + + + +``` diff --git a/src/components/SiteFooter/svgs/Facebook.svelte b/src/components/SiteFooter/svgs/Facebook.svelte new file mode 100644 index 00000000..7c7c9d18 --- /dev/null +++ b/src/components/SiteFooter/svgs/Facebook.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/SiteFooter/svgs/Graphics.svelte b/src/components/SiteFooter/svgs/Graphics.svelte new file mode 100644 index 00000000..e0ecd621 --- /dev/null +++ b/src/components/SiteFooter/svgs/Graphics.svelte @@ -0,0 +1,21 @@ + + + diff --git a/src/components/SiteFooter/svgs/Instagram.svelte b/src/components/SiteFooter/svgs/Instagram.svelte new file mode 100644 index 00000000..3ca3c716 --- /dev/null +++ b/src/components/SiteFooter/svgs/Instagram.svelte @@ -0,0 +1,23 @@ + + + + + diff --git a/src/components/SiteFooter/svgs/LinkedIn.svelte b/src/components/SiteFooter/svgs/LinkedIn.svelte new file mode 100644 index 00000000..7b3d65ac --- /dev/null +++ b/src/components/SiteFooter/svgs/LinkedIn.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/SiteFooter/svgs/Pictures.svelte b/src/components/SiteFooter/svgs/Pictures.svelte new file mode 100644 index 00000000..6735ea6c --- /dev/null +++ b/src/components/SiteFooter/svgs/Pictures.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/components/SiteFooter/svgs/Twitter.svelte b/src/components/SiteFooter/svgs/Twitter.svelte new file mode 100644 index 00000000..c376aacc --- /dev/null +++ b/src/components/SiteFooter/svgs/Twitter.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/SiteFooter/svgs/Videos.svelte b/src/components/SiteFooter/svgs/Videos.svelte new file mode 100644 index 00000000..44010879 --- /dev/null +++ b/src/components/SiteFooter/svgs/Videos.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/components/SiteFooter/svgs/YouTube.svelte b/src/components/SiteFooter/svgs/YouTube.svelte new file mode 100644 index 00000000..1a3b8a54 --- /dev/null +++ b/src/components/SiteFooter/svgs/YouTube.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/src/components/SiteHeader/MobileMenu/index.svelte b/src/components/SiteHeader/MobileMenu/index.svelte new file mode 100644 index 00000000..d400a5ca --- /dev/null +++ b/src/components/SiteHeader/MobileMenu/index.svelte @@ -0,0 +1,167 @@ + + +{#if isMobileMenuOpen} +
+
+ + +
+ {#each data.sections as section} +
+ {section.name} + {#if section.children} + + {/if} +
+ {/each} +
+{/if} + + diff --git a/src/components/SiteHeader/NavBar/DownArrow.svelte b/src/components/SiteHeader/NavBar/DownArrow.svelte new file mode 100644 index 00000000..00b5fed1 --- /dev/null +++ b/src/components/SiteHeader/NavBar/DownArrow.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/src/components/SiteHeader/NavBar/NavDropdown/MoreDropdown.svelte b/src/components/SiteHeader/NavBar/NavDropdown/MoreDropdown.svelte new file mode 100644 index 00000000..5cefa58c --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/MoreDropdown.svelte @@ -0,0 +1,89 @@ + + + +
+
+ {#each sections as section} +
+ + {section.name} + + {#if section.children} +
    + {#each section.children as sub} +
  • + {sub.name} +
  • + {/each} +
+ {/if} +
+ {/each} +
+
+
+ + diff --git a/src/components/SiteHeader/NavBar/NavDropdown/SectionDrowdown.svelte b/src/components/SiteHeader/NavBar/NavDropdown/SectionDrowdown.svelte new file mode 100644 index 00000000..0be36f12 --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/SectionDrowdown.svelte @@ -0,0 +1,111 @@ + + + + + + Browse {section.name} + + +
+ {#if splitCount > 0} +
    + {#each section.children.slice(0, splitCount) as sub} +
  • + + {sub.name} + +
  • + {/each} +
+ {/if} +
    + {#each section.children.slice(splitCount) as sub} +
  • + + {sub.name} + +
  • + {/each} +
+
+
+ + diff --git a/src/components/SiteHeader/NavBar/NavDropdown/Spinner/index.svelte b/src/components/SiteHeader/NavBar/NavDropdown/Spinner/index.svelte new file mode 100644 index 00000000..df9dc115 --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/Spinner/index.svelte @@ -0,0 +1,49 @@ +
+
+
+
+
+ + diff --git a/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/index.svelte b/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/index.svelte new file mode 100644 index 00000000..d31b71e7 --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/index.svelte @@ -0,0 +1,107 @@ + + +
+ +
+ {#if withSection} + + {story.primary_section.name} + + {/if} + {story.title} + {#if !withSection} + + {/if} +
+ {#if thumbnail} +
+ {thumbnail.alt_text} +
+ {/if} + +
+ + diff --git a/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/time.js b/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/time.js new file mode 100644 index 00000000..d30c0c75 --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/StoryCard/time.js @@ -0,0 +1,70 @@ +import advancedFormat from 'dayjs/plugin/advancedFormat.js'; +import dayjs from 'dayjs'; +import localizedFormat from 'dayjs/plugin/localizedFormat.js'; +import relativeTime from 'dayjs/plugin/relativeTime.js'; +import timezone from 'dayjs/plugin/timezone.js'; +import updateLocale from 'dayjs/plugin/updateLocale.js'; +import utc from 'dayjs/plugin/utc.js'; + +dayjs.extend(relativeTime); +dayjs.extend(localizedFormat); +dayjs.extend(advancedFormat); +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.extend(updateLocale); + +dayjs.updateLocale('en', { + relativeTime: { + future: 'in %s', + past: '%s ago', + s: 'a few seconds', + m: 'a min', + mm: '%d min', + h: 'an hour', + hh: '%d hours', + d: 'a day', + dd: '%d days', + M: 'a month', + MM: '%d months', + y: 'a year', + yy: '%d years', + }, +}); + +const getTimeZone = (local) => { + if (local) { + return dayjs.tz.guess(); + } + + return 'UTC'; +}; + +const diff = (dateFrom, dateTo, measurement = 'day') => { + return dayjs(dateFrom).diff(dayjs(dateTo), measurement, true); +}; + +const olderThanHour = (dateFrom, dateTo, hours = 1) => { + return diff(dateFrom, dateTo, 'hour') < -hours; +}; + +const isSameDay = (dateFrom, dateTo) => { + const first = new Date(dateFrom); + const second = new Date(dateTo); + return ( + first.getFullYear() === second.getFullYear() && + first.getMonth() === second.getMonth() && + first.getDate() === second.getDate() + ); +}; + +export const getTime = (datetime) => { + const publishTime = dayjs(datetime, { utc: true }); + const showRelativeTime = !olderThanHour(publishTime, Date.now()); + const showTime = isSameDay(publishTime, Date.now()); + const timezone = getTimeZone(false); + if (showRelativeTime) { + return dayjs().to(publishTime); + } + if (showTime) return dayjs(datetime).tz(timezone).format('h:mm A z'); + return publishTime.format('MMMM D, YYYY'); +}; diff --git a/src/components/SiteHeader/NavBar/NavDropdown/index.svelte b/src/components/SiteHeader/NavBar/NavDropdown/index.svelte new file mode 100644 index 00000000..bdfa40c0 --- /dev/null +++ b/src/components/SiteHeader/NavBar/NavDropdown/index.svelte @@ -0,0 +1,223 @@ + + + + + diff --git a/src/components/SiteHeader/NavBar/index.svelte b/src/components/SiteHeader/NavBar/index.svelte new file mode 100644 index 00000000..9fe411d9 --- /dev/null +++ b/src/components/SiteHeader/NavBar/index.svelte @@ -0,0 +1,255 @@ + + + + + + + diff --git a/src/components/SiteHeader/NavBar/utils/index.js b/src/components/SiteHeader/NavBar/utils/index.js new file mode 100644 index 00000000..dd3c8404 --- /dev/null +++ b/src/components/SiteHeader/NavBar/utils/index.js @@ -0,0 +1,2 @@ +export const normalizeUrl = (url) => + /^http/.test(url) ? url : `https://www.reuters.com${url}`; diff --git a/src/components/SiteHeader/SiteHeader.stories.svelte b/src/components/SiteHeader/SiteHeader.stories.svelte new file mode 100644 index 00000000..4769de7d --- /dev/null +++ b/src/components/SiteHeader/SiteHeader.stories.svelte @@ -0,0 +1,51 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/SiteHeader/SiteHeader.svelte b/src/components/SiteHeader/SiteHeader.svelte new file mode 100644 index 00000000..e9eb6ee2 --- /dev/null +++ b/src/components/SiteHeader/SiteHeader.svelte @@ -0,0 +1,267 @@ + + + + +
+ +
+ + + + diff --git a/src/components/SiteHeader/data.json b/src/components/SiteHeader/data.json new file mode 100644 index 00000000..b630e0f2 --- /dev/null +++ b/src/components/SiteHeader/data.json @@ -0,0 +1 @@ +[{"sections":[{"id":"/world/","url":"/world/","name":"World","type":"section","children":[{"id":"/world/africa/","url":"/world/africa/","name":"Africa"},{"id":"/world/americas/","url":"/world/americas/","name":"Americas"},{"id":"/world/asia-pacific/","url":"/world/asia-pacific/","name":"Asia Pacific"},{"id":"/world/china/","url":"/world/china/","name":"China"},{"id":"/world/europe/","url":"/world/europe/","name":"Europe"},{"id":"/world/india/","url":"/world/india/","name":"India"},{"id":"/world/middle-east/","url":"/world/middle-east/","name":"Middle East"},{"id":"/world/uk/","url":"/world/uk/","name":"United Kingdom"},{"id":"/world/us/","url":"/world/us/","name":"United States"},{"id":"/world/the-great-reboot/","url":"/world/the-great-reboot/","name":"The Great Reboot"},{"id":"/world/reuters-next/","url":"/world/reuters-next/","name":"Reuters Next"}]},{"id":"/business/","url":"/business/","name":"Business","type":"section","children":[{"id":"/business/aerospace-defense/","url":"/business/aerospace-defense/","name":"Aerospace & Defense"},{"id":"/business/autos-transportation/","url":"/business/autos-transportation/","name":"Autos & Transportation"},{"id":"/business/energy/","url":"/business/energy/","name":"Energy"},{"id":"/business/environment/","url":"/business/environment/","name":"Environment"},{"id":"/business/finance/","url":"/business/finance/","name":"Finance"},{"id":"/business/healthcare-pharmaceuticals/","url":"/business/healthcare-pharmaceuticals/","name":"Healthcare & Pharmaceuticals"},{"id":"/business/media-telecom/","url":"/business/media-telecom/","name":"Media & Telecom"},{"id":"/business/retail-consumer/","url":"/business/retail-consumer/","name":"Retail & Consumer"},{"id":"/business/sustainable-business/","url":"/business/sustainable-business/","name":"Sustainable Business"},{"id":"/business/charged/","url":"/business/charged/","name":"Charged"},{"id":"/business/future-of-health/","url":"/business/future-of-health/","name":"Future of Health"},{"id":"/business/future-of-money/","url":"/business/future-of-money/","name":"Future of Money"},{"id":"/business/take-five/","url":"/business/take-five/","name":"Take Five"},{"id":"/business/reuters-impact/","url":"/business/reuters-impact/","name":"Reuters Impact"},{"id":"/business/davos/","url":"/business/davos/","name":"Davos"}]},{"id":"/legal/","url":"/legal/","name":"Legal","type":"section","children":[{"id":"/legal/government/","url":"/legal/government/","name":"Government"},{"id":"/legal/legalindustry/","url":"/legal/legalindustry/","name":"Legal Industry"},{"id":"/legal/litigation/","url":"/legal/litigation/","name":"Litigation"},{"id":"/legal/transactional/","url":"/legal/transactional/","name":"Transactional"}]},{"id":"/markets/","url":"/markets/","name":"Markets","type":"section","children":[{"id":"/markets/asia/","url":"/markets/asia/","name":"Asian Markets"},{"id":"/markets/carbon/","url":"/markets/carbon/","name":"Carbon Markets"},{"id":"/markets/commodities/","url":"/markets/commodities/","name":"Commodities"},{"id":"/markets/currencies/","url":"/markets/currencies/","name":"Currencies"},{"id":"/markets/deals/","url":"/markets/deals/","name":"Deals"},{"id":"/markets/emerging/","url":"/markets/emerging/","name":"Emerging Markets"},{"id":"/markets/europe/","url":"/markets/europe/","name":"European Markets"},{"id":"/markets/funds/","url":"/markets/funds/","name":"Funds"},{"id":"/markets/global-market-data/","url":"/markets/global-market-data/","name":"Global Market Data"},{"id":"/markets/rates-bonds/","url":"/markets/rates-bonds/","name":"Rates & Bonds"},{"id":"/markets/stocks/","url":"/markets/stocks/","name":"Stocks"},{"id":"/markets/us/","url":"/markets/us/","name":"U.S. Markets"},{"id":"/markets/wealth/","url":"/markets/wealth/","name":"Wealth"},{"id":"/markets/macromatters/","url":"/markets/macromatters/","name":"Macro Matters"}]},{"id":"/breakingviews/","url":"/breakingviews/","name":"Breakingviews","type":"section"},{"id":"/technology/","url":"/technology/","name":"Technology","type":"section","children":[{"id":"/technology/disrupted/","url":"/technology/disrupted/","name":"Disrupted"},{"id":"/technology/reuters-momentum/","url":"/technology/reuters-momentum/","name":"Reuters Momentum"}]},{"id":"/investigates/","url":"https://www.reuters.com/investigates/","name":"Investigations","type":"link"},{"id":"/lifestyle/sports/","url":"/lifestyle/sports/","name":"Sports","type":"section","children":[{"id":"/lifestyle/sports/athletics/","url":"/lifestyle/sports/athletics/","name":"Athletics"},{"id":"/lifestyle/sports/cricket/","url":"/lifestyle/sports/cricket/","name":"Cricket"},{"id":"/lifestyle/sports/cycling/","url":"/lifestyle/sports/cycling/","name":"Cycling"},{"id":"/lifestyle/sports/golf/","url":"/lifestyle/sports/golf/","name":"Golf"},{"id":"/lifestyle/sports/motor-sports/","url":"/lifestyle/sports/motor-sports/","name":"Motor Sports"},{"id":"/lifestyle/sports/soccer/","url":"/lifestyle/sports/soccer/","name":"Soccer"},{"id":"/lifestyle/sports/tennis/","url":"/lifestyle/sports/tennis/","name":"Tennis"}]},{"id":"/lifestyle/","url":"/lifestyle/","name":"Lifestyle","type":"section","children":[{"id":"/lifestyle/oddly-enough/","url":"/lifestyle/oddly-enough/","name":"Oddly Enough"},{"id":"/lifestyle/science/","url":"/lifestyle/science/","name":"Science"}]},{"id":"/graphics/","url":"https://graphics.reuters.com/","name":"Graphics","type":"link"},{"id":"/pictures/","url":"/pictures/","name":"Pictures","type":"section"},{"id":"/video/","url":"/video/","name":"Video","type":"section"}],"home_url":"/","search_url":"/site-search/","sign_in_url":"/signin/","sign_up_url":"/signup/","subscribe_url":"","my_account_url":"/myaccount/","my_view_url":"/myview/all","following_url":"/myview/following-feed","saved_url":"/myview/saved"}] \ No newline at end of file diff --git a/src/components/SiteHeader/scss/_breakpoints.scss b/src/components/SiteHeader/scss/_breakpoints.scss new file mode 100644 index 00000000..c90bba4d --- /dev/null +++ b/src/components/SiteHeader/scss/_breakpoints.scss @@ -0,0 +1,59 @@ +// From FSC: +// Our design layouts are based on 2 artboard sizes: +// 375px for small screens and 1440px for desktop. + +// However, these are snapshots to inform the core layouts. +// Some aspects of the design system adapt to breakpoints between these sizes. + +// Our design layouts therefore show the snapshots for mobile on the 375px artboard and wide on the 1440px artboard. + +// Note that if we refer only to desktop it means all layouts above 1023px, similarly mobile in general is below 1024px. + +// Note that the desktop breakpoint kicks-in at 1024px, which also accommodates iPad/tablet in landscape. +// This is the last size before the layout adapts to the mobile view. + +$maxWidth: 1440px; + +@mixin for-small-mobile { + @media (max-width: 518px) { @content; } +} + +@mixin for-mobile { + @media (max-width: 745px) { @content; } +} + +@mixin for-tablet { + @media (min-width: 746px) and (max-width: 1023px) { @content; } +} + +@mixin for-tablet-up { + @media (min-width: 746px) { @content; } +} + +@mixin for-tablet-down { + @media (max-width: 1023px) { @content; } +} + +@mixin for-tight-desktop { + @media (min-width: 1024px) and (max-width: 1060px) { @content; } +} + +@mixin for-desktop { + @media (min-width: 1024px) { @content; } +} + +@mixin for-wide-desktop { + @media (min-width: 1300px) { @content; } +} + +@mixin for-extra-wide-desktop { + @media (min-width: 1440px) { @content; } +} + +@mixin above-max { + @media (min-width: $maxWidth) { @content; } +} + +@mixin max-width { + max-width: $maxWidth; +} \ No newline at end of file diff --git a/src/components/SiteHeader/scss/_colors.scss b/src/components/SiteHeader/scss/_colors.scss new file mode 100644 index 00000000..0d4f8882 --- /dev/null +++ b/src/components/SiteHeader/scss/_colors.scss @@ -0,0 +1,25 @@ +// Sheet of project colors +$tr-orange: #fa6400; +$tr-dark-orange: #dc4300; +$tr-light-orange: #ffa100; +$tr-dark-grey: #404040; +$tr-medium-grey: #666666; +$tr-light-grey: #afafaf; +$tr-muted-grey: #d0d0d0; +$tr-hover-background-grey: #f8f8f8; +$tr-light-muted-grey: #f4f4f4; +$tr-ultra-light-grey: #fafafa; +$tr-dark-blue: #005da2; +$tr-light-blue: #0099c4; +$tr-muted-blue: #4386B9; +$tr-lighter-blue: #7FACCE; +$tr-superlight-blue: #e5eef5; +$tr-dark-purple: #621f95; +$tr-light-purple: #6e3ab7; +$tr-dark-red: #a00000; +$tr-light-red: #dc0a0a; +$tr-dark-green: #387c2b; +$tr-light-green: #77a22d; +$black: #000; +$white: #fff; +$ad-placeholder: #ffb1b1; \ No newline at end of file diff --git a/src/components/SiteHeader/scss/_eases.scss b/src/components/SiteHeader/scss/_eases.scss new file mode 100644 index 00000000..ae365710 --- /dev/null +++ b/src/components/SiteHeader/scss/_eases.scss @@ -0,0 +1,9 @@ +$principleDefaultEase: cubic-bezier(0.25, 0.1, 0.25, 1); +$easeOutExpo: cubic-bezier(0.19, 1, 0.22, 1); +$easeOutCubic: cubic-bezier(0.215, 0.61, 0.355, 1); +$easeInOutCubic: cubic-bezier(0.645, 0.045, 0.355, 1); +$easeInOutQuad: cubic-bezier(0.455, 0.03, 0.515, 0.955); +$easeInQuad: cubic-bezier(0.55, 0.085, 0.68, 0.53); +$easeOutQuad: cubic-bezier(0.25, 0.46, 0.45, 0.94); +$navContainerEase: cubic-bezier(0.25, 0.1, 0.25, 1); +$rtvDefaultEase: cubic-bezier(0.165, 0.84, 0.44, 1); diff --git a/src/components/SiteHeader/scss/_grids.scss b/src/components/SiteHeader/scss/_grids.scss new file mode 100644 index 00000000..9d82c440 --- /dev/null +++ b/src/components/SiteHeader/scss/_grids.scss @@ -0,0 +1,126 @@ +@use 'sass:math'; + +@import '_breakpoints.scss'; + +@mixin spacing-single($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (math.div(32, 1440) * 100vw * $delta); + } + + @include for-tablet-down { + @each $property in $properties { + #{$property}: (math.div(16, 375) * 100vw * $delta); + } + } + + @include above-max { + @each $property in $properties { + #{$property}: (32px * $delta); + } + } +} + +@mixin spacing-single-half($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (calc(16 / 1440) * 100vw * $delta); + } + + @include for-tablet-down { + @each $property in $properties { + #{$property}: (math.div(8, 375) * 100vw * $delta); + } + } + + @include above-max { + @each $property in $properties { + #{$property}: (16px * $delta); + } + } +} + +@mixin spacing-single-34($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (math.div(24, 1440) * 100vw * $delta); + } + + @include for-tablet-down { + @each $property in $properties { + #{$property}: (math.div(12, 375) * 100vw * $delta); + } + } + + @include above-max { + @each $property in $properties { + #{$property}: (24px * $delta); + } + } + } + +@mixin spacing-150($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (math.div(48, 1440) * 100vw * $delta); + } + + @include for-tablet-down { + @each $property in $properties { + #{$property}: (math.div(24, 375) * 100vw * $delta); + } + } + + @include above-max { + @each $property in $properties { + #{$property}: (48px * $delta); + } + } +} + +@mixin spacing-75($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (math.div(24, 1440) * 100vw * $delta); + } + + @include above-max { + @each $property in $properties { + #{$property}: (24px * $delta); + } + } +} + +@mixin spacing-50($properties, $delta: 1) { + @each $property in $properties { + #{$property}: (math.div(16, 1440) * 100vw * $delta); + } + + @include above-max { + @each $property in $properties { + #{$property}: (16px * $delta); + } + } +} + +@mixin responsive-columns($columns, $size: 1fr) { + @content; + + display: grid; + grid-template-columns: repeat($columns, $size); + + // Desktop grid (all screens greater than or equal to 1024px) + @include spacing-single(grid-column-gap); + + // Mobile grid (all screens up to and including 1023px) + @include for-tablet-down { + grid-template-columns: repeat(4, $size); + } +} + +@mixin at-4-columns { + @include for-tablet-down { + @content; + } +} + +@mixin above-4-columns { + @include for-desktop { + @content; + } +} \ No newline at end of file diff --git a/src/components/SiteHeader/scss/_z-indexes.scss b/src/components/SiteHeader/scss/_z-indexes.scss new file mode 100644 index 00000000..731682e8 --- /dev/null +++ b/src/components/SiteHeader/scss/_z-indexes.scss @@ -0,0 +1,15 @@ +/* +Several components utilize z-index, the CSS property that helps control layout by providing a third axis to arrange content. We utilize a default z-index scale that’s been designed to properly layer navigation, tooltips and popovers, modals, and more. +These higher values start at an arbitrary number, high and specific enough to ideally avoid conflicts. We need a standard set of these across our layered components—tooltips, popovers, navbars, dropdowns, modals—so we can be reasonably consistent in the behaviors. +To handle overlapping borders within components (e.g., buttons and inputs in input groups), we use low single digit z-index values of 1, 2, and 3 for default, hover, and active states. On hover/focus/active, we bring a particular element to the forefront with a higher z-index value to show their border over the sibling elements. +*/ + +$zindex-dropdown: 1000; +$zindex-sticky: 1020; +$zindex-fixed: 1030; +$zindex-modal-backdrop: 1040; +$zindex-offcanvas: 1050; +$zindex-modal: 1060; +$zindex-popover: 1070; +$zindex-tooltip: 1080; +$zindex-close-button: 1090; \ No newline at end of file diff --git a/src/components/SiteHeader/stories/docs/component.md b/src/components/SiteHeader/stories/docs/component.md new file mode 100644 index 00000000..33e23a81 --- /dev/null +++ b/src/components/SiteHeader/stories/docs/component.md @@ -0,0 +1,13 @@ +Reuters dotcom site header, ported from [Raptor UI components](https://github.com/tr/rcom-arc_raptor-ui/tree/develop/packages/rcom-raptor-ui_common/src/components/site-header). + +(Go to the "Canvas" tab to see this component better...) + +> **Note:** In the [Graphics Kit](https://github.com/reuters-graphics/bluprint_graphics-kit/blob/master/pages/index.svelte), you can find this component in `pages/index.svelte`. Customise it there for the default page. + +```svelte + + + +``` diff --git a/src/components/SiteHeader/stories/docs/darkTheme.md b/src/components/SiteHeader/stories/docs/darkTheme.md new file mode 100644 index 00000000..5940d741 --- /dev/null +++ b/src/components/SiteHeader/stories/docs/darkTheme.md @@ -0,0 +1,26 @@ +Customise colours by passing an object to the `theme` prop. You can customise any or all of the following default properties: + +```javascript +// Customisable colours +{ + background: '#fff', + primary: '#404040', + accent: '#fa6400', + rules: '#d0d0d0', + shadow: '0 1px 4px 2px rgb(64 64 64 / 8%)', +} +``` + +The component also exports some preset themes you can use or customise: + +```svelte + + + + + + + +``` diff --git a/src/components/SiteHeader/svgs/Close.svelte b/src/components/SiteHeader/svgs/Close.svelte new file mode 100644 index 00000000..b31acc41 --- /dev/null +++ b/src/components/SiteHeader/svgs/Close.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/components/SiteHeader/svgs/Menu.svelte b/src/components/SiteHeader/svgs/Menu.svelte new file mode 100644 index 00000000..ea16b735 --- /dev/null +++ b/src/components/SiteHeader/svgs/Menu.svelte @@ -0,0 +1,22 @@ + + + + + diff --git a/src/components/Spinner/Spinner.stories.svelte b/src/components/Spinner/Spinner.stories.svelte new file mode 100644 index 00000000..903b8eec --- /dev/null +++ b/src/components/Spinner/Spinner.stories.svelte @@ -0,0 +1,34 @@ + + + + + + + diff --git a/src/components/Spinner/Spinner.svelte b/src/components/Spinner/Spinner.svelte new file mode 100644 index 00000000..3e05d118 --- /dev/null +++ b/src/components/Spinner/Spinner.svelte @@ -0,0 +1,89 @@ + + +
+
+
+
+
+
+
+ + diff --git a/src/components/Spinner/stories/docs/component.md b/src/components/Spinner/stories/docs/component.md new file mode 100644 index 00000000..802497f2 --- /dev/null +++ b/src/components/Spinner/stories/docs/component.md @@ -0,0 +1,15 @@ +Simple loading spinner. Use it to help hide long loading times for components that may be loading expensive media files or crunching through lots of data. + +```svelte + + +{#if somethingsLoading} + +{:else} +

The real deal is here.

+{/if} +``` diff --git a/src/components/Video/Controls.svelte b/src/components/Video/Controls.svelte new file mode 100644 index 00000000..eb79b0cd --- /dev/null +++ b/src/components/Video/Controls.svelte @@ -0,0 +1,70 @@ + + + + + diff --git a/src/components/Video/docs.svx b/src/components/Video/docs.svx new file mode 100644 index 00000000..dbdfb9d2 --- /dev/null +++ b/src/components/Video/docs.svx @@ -0,0 +1,293 @@ +--- +title: Video +description: General-purpose video component. Can play on load or when the video comes into view and has play/pause controls. Supports videos with or without audio. +slug: video +--- + + + +
+ + ## {title} + + {description} + +
+ + +```svelte + + +