├── .babelrc ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ └── config.yml └── workflows │ ├── build.yml │ └── push.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── LICENSE ├── README.md ├── app.json ├── editor.js ├── package-lock.json ├── package.json ├── public ├── index.html └── static │ ├── 50Jahre_Frauenwahlrecht_negativ.svg │ ├── 50Jahre_Frauenwahlrecht_positiv.svg │ ├── USA_Farblos_665px-Farblos_665px.png │ ├── apple-touch-icon.png │ ├── bordell.jpeg │ ├── brigitte_meyer.jpg │ ├── carousel │ ├── berg.png │ ├── binswanger.png │ ├── binswanger_dark.png │ ├── calle.png │ ├── eth.png │ ├── homestory.gif │ ├── homestory.png │ ├── jackson.png │ ├── mike.png │ ├── murdoch.png │ ├── niggli.png │ ├── rinck.png │ ├── solstad.png │ ├── strahlen.png │ ├── test.png │ └── vault.png │ ├── christof_moser.jpg │ ├── dada.jpg │ ├── dada_dark.png │ ├── desert.jpg │ ├── dynamic_hello.js │ ├── fonts │ ├── gt-america-standard-medium.eot │ ├── gt-america-standard-medium.ttf │ ├── gt-america-standard-medium.woff │ ├── gt-america-standard-regular.eot │ ├── gt-america-standard-regular.ttf │ ├── gt-america-standard-regular.woff │ ├── rubis-bold.eot │ ├── rubis-bold.ttf │ ├── rubis-bold.woff │ ├── rubis-bold.woff2 │ ├── rubis-regular.eot │ ├── rubis-regular.ttf │ ├── rubis-regular.woff │ └── rubis-regular.woff2 │ ├── geo │ ├── bfs-g3g12.json │ ├── ch-cantons-wo-lakes.json │ ├── ch-cantons.json │ ├── countries2016-20m-europe.json │ ├── country-names.csv │ ├── epsg2056-projected-ch-cantons-wo-lakes.json │ ├── epsg2056-projected-ch-cantons.json │ ├── nuts2013-20m-l2-custom-gdp.json │ ├── nuts2016-20m-l2.json │ ├── solar.png │ ├── world-atlas-110m-without-antarctic.json │ └── world-atlas-110m.json │ ├── im-gespr-3.gif │ ├── landscape.jpg │ ├── main.vtt │ ├── nl-chart.png │ ├── profilePicture1.png │ ├── profilePicture2.png │ ├── rothaus_landscape.jpg │ ├── rothaus_portrait.jpg │ ├── sample.mp3 │ ├── skilifte-front.svg │ ├── top-story-badge.png │ ├── tweet_preview.jpg │ ├── twitter_icon.jpg │ └── video.jpg ├── rollup.config.js ├── scripts └── renderNewsletter.js ├── src ├── README.md ├── catalogTheme.css ├── catalogTheme.js ├── chart.ts ├── components │ ├── AudioPlayer │ │ ├── Player.js │ │ ├── docs.md │ │ └── index.js │ ├── BlockQuote │ │ ├── BlockQuote.js │ │ ├── BlockQuoteParagraph.js │ │ ├── docs.md │ │ └── index.js │ ├── Button │ │ ├── docs.md │ │ └── index.tsx │ ├── Callout │ │ ├── CalloutMenu.tsx │ │ ├── docs.md │ │ └── index.tsx │ ├── Center │ │ ├── docs.md │ │ └── index.js │ ├── Chart │ │ ├── Bars.docs.md │ │ ├── Bars.js │ │ ├── Bars.schema.js │ │ ├── ChartContext.js │ │ ├── ChartContext.utils.js │ │ ├── ColorLegend.js │ │ ├── ContextBox.js │ │ ├── Csv.js │ │ ├── Editor │ │ │ ├── AxisFormatDropdown.js │ │ │ ├── ColorDropdownElement.js │ │ │ ├── ColorField.js │ │ │ ├── ColorPickerCallout.js │ │ │ ├── CustomValueDropdown.js │ │ │ ├── FormFields.js │ │ │ ├── TickField.js │ │ │ ├── docs.data.js │ │ │ ├── docs.md │ │ │ ├── index.js │ │ │ └── utils.js │ │ ├── Hemicycle.docs.md │ │ ├── Hemicycle.js │ │ ├── Hemicycle.schema.js │ │ ├── LICENSE │ │ ├── Layout.constants.js │ │ ├── LineGroup.js │ │ ├── Lines.context.js │ │ ├── Lines.docs.md │ │ ├── Lines.js │ │ ├── Lines.schema.js │ │ ├── Lines.utils.js │ │ ├── Lollipops.docs.md │ │ ├── Maps.docs.data.js │ │ ├── Maps.docs.md │ │ ├── Maps.js │ │ ├── Maps.layout.js │ │ ├── Maps.schema.js │ │ ├── ScatterPlotCanvas.js │ │ ├── ScatterPlotContextBox.js │ │ ├── ScatterPlotElements.js │ │ ├── ScatterPlotGroup.js │ │ ├── ScatterPlots.docs.md │ │ ├── ScatterPlots.js │ │ ├── ScatterPlots.schema.js │ │ ├── ScatterPlots.utils.js │ │ ├── Slopes.docs.md │ │ ├── Table.docs.md │ │ ├── Table.js │ │ ├── Table.schema.js │ │ ├── TimeBarGroup.js │ │ ├── TimeBars.context.js │ │ ├── TimeBars.docs.md │ │ ├── TimeBars.js │ │ ├── TimeBars.schema.js │ │ ├── TimeBars.utils.js │ │ ├── TimeBarsAnnotations.js │ │ ├── XAxis.js │ │ ├── colorMaps.js │ │ ├── docs.js │ │ ├── index.js │ │ ├── utils.js │ │ └── utils.test.js │ ├── Collapsable │ │ ├── Collapsable.js │ │ ├── docs.md │ │ └── index.js │ ├── Colors │ │ ├── ColorContext.tsx │ │ ├── docs.md │ │ └── useColorContext.js │ ├── CommentBody │ │ ├── email │ │ │ ├── BlockCode.js │ │ │ ├── Blockquote.js │ │ │ ├── Container.js │ │ │ ├── List.js │ │ │ ├── Paragraph.js │ │ │ └── index.js │ │ └── web │ │ │ ├── BlockCode.js │ │ │ ├── Blockquote.js │ │ │ ├── Container.js │ │ │ ├── List.js │ │ │ ├── Paragraph.js │ │ │ └── index.js │ ├── CommentTeaser │ │ ├── CommentTeaser.js │ │ ├── DiscussionFooter.js │ │ ├── docs.imports.js │ │ ├── docs.md │ │ └── index.js │ ├── Discussion │ │ ├── Composer │ │ │ ├── CommentComposer.js │ │ │ ├── CommentComposerPlaceholder.tsx │ │ │ ├── CommentDraftHelper.js │ │ │ ├── docs.imports.js │ │ │ ├── docs.md │ │ │ └── index.js │ │ ├── DiscussionCommentsWrapper.tsx │ │ ├── DiscussionContext.docs.js │ │ ├── DiscussionContext.js │ │ ├── Internal │ │ │ ├── Comment │ │ │ │ ├── ActionsMenu.tsx │ │ │ │ ├── Body.js │ │ │ │ ├── CommentActions.tsx │ │ │ │ ├── CommentEmbed.js │ │ │ │ ├── Context.js │ │ │ │ ├── Header.js │ │ │ │ ├── HeaderMetaLine.tsx │ │ │ │ ├── IconLink.js │ │ │ │ ├── RelativeTime.js │ │ │ │ ├── VoteButtons.tsx │ │ │ │ ├── index.js │ │ │ │ └── render.js │ │ │ ├── Composer │ │ │ │ ├── Actions.js │ │ │ │ ├── Error.js │ │ │ │ ├── Header.tsx │ │ │ │ ├── Tags.js │ │ │ │ └── index.js │ │ │ ├── PropTypes.js │ │ │ ├── docs.imports.js │ │ │ └── docs.md │ │ ├── Statements │ │ │ ├── StatementNode.js │ │ │ ├── helpers │ │ │ │ ├── ColorContextHelper.js │ │ │ │ ├── colorHelper.js │ │ │ │ └── tagHelper.js │ │ │ └── index.js │ │ ├── Tree │ │ │ ├── BoardComment.tsx │ │ │ ├── CommentNode.tsx │ │ │ ├── LoadMore.js │ │ │ ├── docs.imports.js │ │ │ ├── docs.md │ │ │ └── index.ts │ │ ├── __docs__ │ │ │ ├── comments.js │ │ │ └── exampleMdast.js │ │ ├── config.js │ │ └── docs.md │ ├── Dossier │ │ ├── Headline.js │ │ ├── Lead.js │ │ ├── More.js │ │ ├── Subheader.js │ │ ├── Tag.js │ │ ├── Teaser.js │ │ ├── Teaser.md │ │ ├── TeaserIntro.js │ │ ├── TileHeadline.js │ │ ├── docs.md │ │ └── index.js │ ├── DynamicComponent │ │ ├── docs.md │ │ ├── index.js │ │ └── require.js │ ├── ErrorBoundary │ │ └── index.js │ ├── Figure │ │ ├── Byline.js │ │ ├── Caption.js │ │ ├── Image.js │ │ ├── SwitchImage.js │ │ ├── docs.md │ │ ├── index.js │ │ ├── utils.js │ │ └── utils.test.js │ ├── Form │ │ ├── Autocomplete.docs.md │ │ ├── Autocomplete.js │ │ ├── Checkbox.docs.md │ │ ├── Checkbox.tsx │ │ ├── Dropdown.docs.md │ │ ├── Dropdown.tsx │ │ ├── DropdownLabel.tsx │ │ ├── Field.docs.js │ │ ├── Field.tsx │ │ ├── FieldSet.js │ │ ├── NativeDropdown.tsx │ │ ├── Radio.docs.md │ │ ├── Radio.tsx │ │ ├── Slider.docs.md │ │ ├── Slider.tsx │ │ ├── VirtualDropdown.tsx │ │ ├── constants.ts │ │ └── docs.md │ ├── Format │ │ ├── Tag.js │ │ ├── docs.md │ │ └── index.js │ ├── Grid │ │ ├── docs.md │ │ └── index.js │ ├── IconButton │ │ ├── docs.md │ │ └── index.tsx │ ├── Icons │ │ ├── CustomIcons │ │ │ ├── BackIcon.js │ │ │ ├── CustomIconBase.js │ │ │ ├── DiscussionIcon.js │ │ │ ├── FontSizeIcon.js │ │ │ ├── MarkdownIcon.js │ │ │ ├── MdCheckCircleOutlined.js │ │ │ ├── MdCheckSmall.js │ │ │ ├── MdInsertChartOutlined.js │ │ │ ├── SearchMenuIcon.js │ │ │ ├── ShareIcon.js │ │ │ ├── SpotifyIcon.js │ │ │ ├── SubscriptIcon.js │ │ │ └── SuperscriptIcon.js │ │ ├── IconContext.js │ │ ├── docs.md │ │ └── index.js │ ├── IllustrationHtml │ │ ├── docs.md │ │ └── index.js │ ├── InfoBox │ │ ├── InfoBox.js │ │ ├── ListItem.js │ │ ├── Subhead.js │ │ ├── Text.js │ │ ├── Title.js │ │ ├── docs.md │ │ └── index.js │ ├── LazyLoad │ │ ├── Image.js │ │ ├── docs.md │ │ └── index.js │ ├── List │ │ ├── List.js │ │ ├── docs.md │ │ └── index.js │ ├── Loader │ │ ├── docs.md │ │ └── index.js │ ├── Logo │ │ ├── BrandMark.js │ │ ├── docs.md │ │ └── index.js │ ├── Overlay │ │ ├── Overlay.tsx │ │ ├── OverlayBody.tsx │ │ ├── OverlayToolbar.tsx │ │ ├── docs.imports.js │ │ ├── docs.md │ │ └── index.js │ ├── Progress │ │ ├── Circle.js │ │ ├── docs.md │ │ └── index.js │ ├── PullQuote │ │ ├── PullQuote.js │ │ ├── Source.js │ │ ├── Text.js │ │ ├── docs.md │ │ └── index.js │ ├── RawHtml │ │ ├── docs.md │ │ └── index.js │ ├── SeriesNav │ │ ├── SeriesNav.js │ │ ├── SeriesNavTile.js │ │ ├── SeriesNavTileContent.js │ │ ├── __docs__ │ │ │ ├── index.js │ │ │ └── seriesNavExample.js │ │ ├── docs.md │ │ └── index.js │ ├── ShareImage │ │ ├── ShareImagePreview.js │ │ ├── SharePreviewFacebook.js │ │ ├── SharePreviewTwitter.js │ │ ├── docs.md │ │ └── index.js │ ├── Social │ │ ├── Header.js │ │ ├── Tweet.js │ │ ├── docs.md │ │ └── index.js │ ├── Spinner │ │ ├── docs.md │ │ └── index.js │ ├── Tabs │ │ ├── Scroller.tsx │ │ ├── TabButton.tsx │ │ └── docs.md │ ├── TeaserActiveDebates │ │ ├── ActiveDebates.js │ │ ├── DebateComment.js │ │ ├── DebateHeader.js │ │ ├── DebateTeaser.js │ │ ├── __docs__ │ │ │ ├── activeDebatesExample.js │ │ │ └── index.js │ │ ├── docs.md │ │ └── index.js │ ├── TeaserCarousel │ │ ├── ArticleCount.js │ │ ├── Carousel.js │ │ ├── Context.js │ │ ├── Format.js │ │ ├── Grid.js │ │ ├── Headline.js │ │ ├── Lead.js │ │ ├── Row.js │ │ ├── Subject.js │ │ ├── Tile.js │ │ ├── TileContainer.js │ │ ├── constants.js │ │ ├── docs.md │ │ └── index.js │ ├── TeaserEmbedComment │ │ └── index.js │ ├── TeaserFeed │ │ ├── Container.js │ │ ├── Credit.js │ │ ├── Format.js │ │ ├── Headline.js │ │ ├── Highlight.js │ │ ├── InternalOnlyTag.js │ │ ├── Lead.js │ │ ├── docs.md │ │ ├── index.js │ │ └── utils.js │ ├── TeaserFront │ │ ├── Credit.js │ │ ├── CreditLink.js │ │ ├── Format.js │ │ ├── Image.js │ │ ├── Image.md │ │ ├── ImageHeadline.js │ │ ├── Lead.js │ │ ├── Logo.js │ │ ├── Split.js │ │ ├── Split.md │ │ ├── SplitHeadline.js │ │ ├── Subject.js │ │ ├── Text.js │ │ ├── Tile.js │ │ ├── Tile.md │ │ ├── TileHeadline.js │ │ ├── TileRow.js │ │ ├── Typo.js │ │ ├── Typo.md │ │ ├── TypoHeadline.js │ │ ├── index.js │ │ └── mediaQueries.js │ ├── TeaserMyMagazine │ │ ├── TeaserMyMagazine.js │ │ ├── __docs__ │ │ │ ├── index.js │ │ │ └── myMagazineExamples.js │ │ ├── docs.md │ │ └── index.js │ ├── TeaserShared │ │ ├── SectionTitle.js │ │ ├── docs.md │ │ └── index.js │ ├── TitleBlock │ │ ├── docs.md │ │ └── index.js │ ├── Typography │ │ ├── Editorial.js │ │ ├── Interaction.js │ │ ├── Meta.js │ │ ├── Scribble.js │ │ ├── docs.md │ │ ├── fontRules.js │ │ ├── index.js │ │ ├── styles.js │ │ └── utils.js │ ├── Variables │ │ └── index.js │ ├── Video │ │ ├── Image.js │ │ ├── Meta.js │ │ ├── Video.js │ │ ├── docs.md │ │ └── index.js │ ├── VideoPlayer │ │ ├── Icons │ │ │ ├── Fullscreen.js │ │ │ ├── Play.js │ │ │ ├── Rewind.js │ │ │ ├── Subtitles.js │ │ │ └── Volume.js │ │ ├── Player.js │ │ ├── docs.md │ │ ├── fullscreen.js │ │ └── index.js │ └── globalMediaState.js ├── development │ ├── process.docs.md │ └── typescript.docs.md ├── editor.ts ├── global.css ├── index.js ├── lib.ts ├── lib │ ├── globalMediaState.js │ ├── helpers.js │ ├── inQuotes.docs.md │ ├── inQuotes.js │ ├── slug.docs.md │ ├── slug.js │ ├── slug.test.js │ ├── styleMixins.js │ ├── textGauger.js │ ├── timeFormat.js │ ├── timeago.js │ ├── timeago.test.js │ ├── timeahead.js │ ├── timeahead.test.js │ ├── timeduration.js │ ├── timeduration.test.js │ ├── translate.docs.md │ ├── translate.ts │ ├── translations.json │ ├── useBodyScrollLock.js │ ├── useBoundingClientRect.js │ ├── useCurrentMinute.js │ ├── useDebounce.js │ ├── useHeaderHeight.docs.md │ ├── useHeaderHeight.js │ ├── useMediaQuery.js │ ├── usePrevious.js │ └── warn.js ├── react-app-env.d.ts ├── templates.ts ├── templates │ ├── Article │ │ ├── Container.js │ │ ├── base.js │ │ ├── blocks.js │ │ ├── docs.md │ │ ├── dynamicComponent.js │ │ ├── email │ │ │ ├── docs.md │ │ │ └── index.js │ │ ├── index.js │ │ ├── teasers.js │ │ ├── test │ │ │ ├── article.stub.js │ │ │ └── render.test.js │ │ ├── utils.js │ │ └── utils.test.js │ ├── Comment │ │ ├── docs.md │ │ ├── email.js │ │ ├── index.js │ │ ├── schema.js │ │ └── web.js │ ├── Discussion │ │ ├── docs.md │ │ └── index.js │ ├── Dossier │ │ ├── docs.md │ │ └── index.js │ ├── EditorialNewsletter │ │ ├── docs.md │ │ ├── email │ │ │ ├── Blockquote.js │ │ │ ├── Button.js │ │ │ ├── Center.js │ │ │ ├── Container.js │ │ │ ├── Cover.js │ │ │ ├── Figure.js │ │ │ ├── Footer.js │ │ │ ├── HR.js │ │ │ ├── Header.js │ │ │ ├── Headlines.js │ │ │ ├── List.js │ │ │ ├── Paragraph.js │ │ │ ├── SubSup.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── schema.js │ │ └── web │ │ │ ├── Button.js │ │ │ ├── Container.js │ │ │ ├── Figure.js │ │ │ ├── ListP.js │ │ │ └── index.js │ ├── Format │ │ ├── docs.md │ │ └── index.js │ ├── Front │ │ ├── docs.md │ │ ├── index.js │ │ └── liveTeasers.js │ ├── Page │ │ ├── docs.md │ │ └── index.js │ ├── Section │ │ ├── docs.md │ │ └── index.js │ ├── docs.js │ └── shared │ │ └── email │ │ ├── components │ │ ├── BlockQuote.js │ │ ├── Caption.js │ │ ├── Center.js │ │ ├── Figure.js │ │ ├── Heading.js │ │ ├── HorizontalRule.js │ │ ├── InfoBox.js │ │ ├── Link.js │ │ ├── List.js │ │ ├── Note.js │ │ ├── Paragraph.js │ │ ├── PullQuote.js │ │ ├── Sub.js │ │ ├── Sup.js │ │ └── TitleBlock.js │ │ ├── rules │ │ ├── articleCollectionRule.js │ │ ├── blockQuoteRule.js │ │ ├── centerRule.js │ │ ├── figureGroupRule.js │ │ ├── figureRule.js │ │ ├── hrRule.js │ │ ├── infoBoxRule.js │ │ ├── inlineHeadingsRule.js │ │ ├── inlineRules.js │ │ ├── legendRules.js │ │ ├── linkRule.js │ │ ├── listRule.js │ │ ├── noteRule.js │ │ ├── paragraphRule.js │ │ ├── pullQuoteRule.js │ │ ├── teaserGroupRule.js │ │ └── titleBlockRule.js │ │ └── util │ │ ├── ImageSizingUtil.js │ │ └── NodeDepthUtil.js └── theme │ ├── colors.docs.js │ ├── colors.js │ ├── env.js │ ├── fonts.js │ ├── mediaQueries.js │ ├── zIndex.docs.md │ └── zIndex.js ├── static.json └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/react", "@babel/preset-env"], 3 | "plugins": [ 4 | // Stage 2 5 | // ["@babel/plugin-proposal-decorators", { "legacy": true }], 6 | "@babel/plugin-proposal-function-sent", 7 | "@babel/plugin-proposal-export-namespace-from", 8 | "@babel/plugin-proposal-numeric-separator", 9 | "@babel/plugin-proposal-throw-expressions", 10 | 11 | // Stage 3 12 | "@babel/plugin-syntax-dynamic-import", 13 | "@babel/plugin-syntax-import-meta", 14 | ["@babel/plugin-proposal-class-properties", { "loose": false }], 15 | "@babel/plugin-proposal-json-strings" 16 | ] 17 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["react", "react-hooks", "prettier", "jsx-a11y"], 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:prettier/recommended" 7 | ], 8 | "parser": "babel-eslint", 9 | "env": { 10 | "browser": true, 11 | "es6": true, 12 | "node": true, 13 | "jest": true 14 | }, 15 | "rules": { 16 | "react-hooks/rules-of-hooks": "error", 17 | "react-hooks/exhaustive-deps": "off", 18 | "react/react-in-jsx-scope": "off", 19 | "react/jsx-no-target-blank": "off", 20 | "react/display-name": "off", 21 | "react/prop-types": "off", 22 | "no-unused-vars": "warn", 23 | "no-empty": "warn" 24 | }, 25 | "settings": { 26 | "react": { 27 | "version": "detect" 28 | } 29 | }, 30 | "overrides": [ 31 | { 32 | "files": ["*.ts", "*.tsx"], 33 | "extends": [ 34 | "eslint:recommended", 35 | "plugin:react/recommended", 36 | "plugin:@typescript-eslint/recommended", 37 | "plugin:prettier/recommended" 38 | ], 39 | "parser": "@typescript-eslint/parser", 40 | "rules": { 41 | "@typescript-eslint/no-loss-of-precision": "off", 42 | "react/prop-types": "off", 43 | "react/display-name": "off" 44 | } 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Bug report 4 | url: https://github.com/republik/plattform/issues/new/choose 5 | about: This repository has been moved into republik/plattform. -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test & Release Styleguide 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2-beta 14 | with: 15 | node-version: '12' 16 | - run: npm prune 17 | - run: npm install 18 | - run: npm run test 19 | - run: npm run build:lib 20 | - name: Release 21 | env: 22 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 23 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | run: npm run semantic-release 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | .env 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | .idea 20 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 14 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "jsxSingleQuote": true, 4 | "semi": false, 5 | "trailingComma": "none", 6 | "arrowParens": "avoid" 7 | } -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "styleguide", 3 | "scripts": {}, 4 | "env": { 5 | "REACT_APP_SG_BRAND_MARK_PATH": { 6 | "required": true 7 | }, 8 | "REACT_APP_SG_BRAND_MARK_VIEWBOX": { 9 | "required": true 10 | }, 11 | "REACT_APP_SG_COLORS": { 12 | "required": true 13 | }, 14 | "REACT_APP_SG_FONT_FACES": { 15 | "required": true 16 | }, 17 | "REACT_APP_SG_FONT_FAMILIES": { 18 | "required": true 19 | }, 20 | "REACT_APP_SG_LOGO_PATH": { 21 | "required": true 22 | }, 23 | "REACT_APP_SG_LOGO_VIEWBOX": { 24 | "required": true 25 | } 26 | }, 27 | "formation": {}, 28 | "addons": [], 29 | "buildpacks": [ 30 | { 31 | "url": "https://github.com/mars/create-react-app-buildpack" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /editor.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/cjs/editor/editor') 2 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Styleguide 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /public/static/USA_Farblos_665px-Farblos_665px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/USA_Farblos_665px-Farblos_665px.png -------------------------------------------------------------------------------- /public/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/apple-touch-icon.png -------------------------------------------------------------------------------- /public/static/bordell.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/bordell.jpeg -------------------------------------------------------------------------------- /public/static/brigitte_meyer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/brigitte_meyer.jpg -------------------------------------------------------------------------------- /public/static/carousel/berg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/berg.png -------------------------------------------------------------------------------- /public/static/carousel/binswanger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/binswanger.png -------------------------------------------------------------------------------- /public/static/carousel/binswanger_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/binswanger_dark.png -------------------------------------------------------------------------------- /public/static/carousel/calle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/calle.png -------------------------------------------------------------------------------- /public/static/carousel/eth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/eth.png -------------------------------------------------------------------------------- /public/static/carousel/homestory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/homestory.gif -------------------------------------------------------------------------------- /public/static/carousel/homestory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/homestory.png -------------------------------------------------------------------------------- /public/static/carousel/jackson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/jackson.png -------------------------------------------------------------------------------- /public/static/carousel/mike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/mike.png -------------------------------------------------------------------------------- /public/static/carousel/murdoch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/murdoch.png -------------------------------------------------------------------------------- /public/static/carousel/niggli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/niggli.png -------------------------------------------------------------------------------- /public/static/carousel/rinck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/rinck.png -------------------------------------------------------------------------------- /public/static/carousel/solstad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/solstad.png -------------------------------------------------------------------------------- /public/static/carousel/strahlen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/strahlen.png -------------------------------------------------------------------------------- /public/static/carousel/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/test.png -------------------------------------------------------------------------------- /public/static/carousel/vault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/carousel/vault.png -------------------------------------------------------------------------------- /public/static/christof_moser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/christof_moser.jpg -------------------------------------------------------------------------------- /public/static/dada.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/dada.jpg -------------------------------------------------------------------------------- /public/static/dada_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/dada_dark.png -------------------------------------------------------------------------------- /public/static/desert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/desert.jpg -------------------------------------------------------------------------------- /public/static/dynamic_hello.js: -------------------------------------------------------------------------------- 1 | define(['react', 'glamor'], function(React, glamor) { 2 | 'use strict' 3 | 4 | React = React && React.hasOwnProperty('default') ? React['default'] : React 5 | 6 | return props => 7 | React.createElement( 8 | 'div', 9 | glamor.css({ 10 | backgroundColor: 'purple', 11 | color: 'white' 12 | }), 13 | props.text || 'other side' 14 | ) 15 | }) 16 | -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-medium.eot -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-medium.ttf -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-medium.woff -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-regular.eot -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-regular.ttf -------------------------------------------------------------------------------- /public/static/fonts/gt-america-standard-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/gt-america-standard-regular.woff -------------------------------------------------------------------------------- /public/static/fonts/rubis-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-bold.eot -------------------------------------------------------------------------------- /public/static/fonts/rubis-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-bold.ttf -------------------------------------------------------------------------------- /public/static/fonts/rubis-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-bold.woff -------------------------------------------------------------------------------- /public/static/fonts/rubis-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-bold.woff2 -------------------------------------------------------------------------------- /public/static/fonts/rubis-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-regular.eot -------------------------------------------------------------------------------- /public/static/fonts/rubis-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-regular.ttf -------------------------------------------------------------------------------- /public/static/fonts/rubis-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-regular.woff -------------------------------------------------------------------------------- /public/static/fonts/rubis-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/fonts/rubis-regular.woff2 -------------------------------------------------------------------------------- /public/static/geo/solar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/geo/solar.png -------------------------------------------------------------------------------- /public/static/im-gespr-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/im-gespr-3.gif -------------------------------------------------------------------------------- /public/static/landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/landscape.jpg -------------------------------------------------------------------------------- /public/static/nl-chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/nl-chart.png -------------------------------------------------------------------------------- /public/static/profilePicture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/profilePicture1.png -------------------------------------------------------------------------------- /public/static/profilePicture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/profilePicture2.png -------------------------------------------------------------------------------- /public/static/rothaus_landscape.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/rothaus_landscape.jpg -------------------------------------------------------------------------------- /public/static/rothaus_portrait.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/rothaus_portrait.jpg -------------------------------------------------------------------------------- /public/static/sample.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/sample.mp3 -------------------------------------------------------------------------------- /public/static/top-story-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/top-story-badge.png -------------------------------------------------------------------------------- /public/static/tweet_preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/tweet_preview.jpg -------------------------------------------------------------------------------- /public/static/twitter_icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/twitter_icon.jpg -------------------------------------------------------------------------------- /public/static/video.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbiting/styleguide/c45510fba197fbc4f103184bbf060ab6ff2269be/public/static/video.jpg -------------------------------------------------------------------------------- /scripts/renderNewsletter.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Usage 4 | // cd ~/Code/styleguide 5 | // cat ~/Articles/newsletter-editorial-x/article.md | scripts/renderNewsletter.js > ~/Desktop/email.html 6 | 7 | require('dotenv').config() 8 | 9 | const { renderEmail } = require('mdast-react-render/lib/email') 10 | const { parse } = require('@orbiting/remark-preset') 11 | const rw = require('rw') 12 | const editorialNewsletterSchema = require('../lib/templates/EditorialNewsletter/email').default() 13 | 14 | const mdast = parse(rw.readFileSync('/dev/stdin', 'utf8')) 15 | 16 | rw.writeFileSync( 17 | '/dev/stdout', 18 | renderEmail(mdast, editorialNewsletterSchema), 19 | 'utf8' 20 | ) 21 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /src/catalogTheme.css: -------------------------------------------------------------------------------- 1 | /* tmp hack */ 2 | svg g[fill="#FFFFFF"] { 3 | fill: #000; 4 | } 5 | 6 | #root ul[class^="catalog"][class*="ul-Styled"] ul[class^="catalog"][class*="ul-Styled"] { 7 | margin-top: 0 !important; 8 | font-size: 18px; 9 | } 10 | 11 | /* code area of catalog */ 12 | pre.css-o6vvb1, pre.css-1xyfnhc { 13 | max-height: 60vh; 14 | overflow: auto; 15 | } 16 | 17 | .dark-mode svg g[fill="#FFFFFF"] { 18 | fill: #fff; 19 | } 20 | 21 | .dark-mode code, .dark-mode section, .dark-mode section > div:nth-child(2), .dark-mode section > div:nth-child(3) { 22 | border-color: #4C4D4C !important; 23 | border-radius: 0 !important; 24 | } 25 | 26 | .dark-mode pre { 27 | background: #191919 !important; 28 | } 29 | 30 | .dark-mode div.css-eplqeh, .dark-mode div.css-zty0sa { 31 | background: #1F1F1F !important; 32 | } 33 | 34 | .dark-mode div.css-invpxs { 35 | background: #292929 !important; 36 | } 37 | 38 | iframe.css-755oq1 { 39 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAAAAACoWZBhAAAAF0lEQVQI12P4BAI/QICBFCaYBPNJYQIAkUZftTbC4sIAAAAASUVORK5CYII=') !important; 40 | } 41 | 42 | [class*="ReactSpecimen"] { 43 | background-color: transparent !important; 44 | } 45 | -------------------------------------------------------------------------------- /src/chart.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default as Chart, 3 | ChartTitle, 4 | ChartLead, 5 | ChartLegend 6 | } from './components/Chart' 7 | -------------------------------------------------------------------------------- /src/components/AudioPlayer/index.js: -------------------------------------------------------------------------------- 1 | export { default as AudioPlayer } from './Player' 2 | -------------------------------------------------------------------------------- /src/components/BlockQuote/BlockQuote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | 5 | import { mUp } from '../../theme/mediaQueries' 6 | 7 | const styles = { 8 | container: css({ 9 | margin: '30px auto', 10 | [mUp]: { 11 | margin: '40px auto' 12 | }, 13 | '& figcaption': { 14 | marginLeft: 0, 15 | marginRight: 0 16 | } 17 | }) 18 | } 19 | 20 | const BlockQuote = ({ children, attributes }) => { 21 | return ( 22 |
23 | {children} 24 |
25 | ) 26 | } 27 | 28 | BlockQuote.propTypes = { 29 | children: PropTypes.node.isRequired, 30 | attributes: PropTypes.object 31 | } 32 | 33 | export default BlockQuote 34 | -------------------------------------------------------------------------------- /src/components/BlockQuote/BlockQuoteParagraph.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { fontRule } from '../Typography/Interaction' 7 | import { sansSerifRegular15, sansSerifRegular18 } from '../Typography/styles' 8 | import { convertStyleToRem } from '../Typography/utils' 9 | import { useColorContext } from '../Colors/useColorContext' 10 | 11 | const styles = { 12 | quote: css({ 13 | margin: 0, 14 | padding: '0 15px 12px 15px', 15 | fontSize: '15px', 16 | ...convertStyleToRem(sansSerifRegular15), 17 | [mUp]: { 18 | ...convertStyleToRem(sansSerifRegular18), 19 | padding: '0 25px 20px 25px', 20 | '&:first-child': { 21 | paddingTop: '20px' 22 | } 23 | }, 24 | '&:first-child': { 25 | paddingTop: '12px' 26 | } 27 | }) 28 | } 29 | 30 | const BlockQuoteParagraph = ({ children, attributes }) => { 31 | const [colorScheme] = useColorContext() 32 | return ( 33 |

40 | {children} 41 |

42 | ) 43 | } 44 | 45 | BlockQuoteParagraph.propTypes = { 46 | children: PropTypes.node.isRequired, 47 | attributes: PropTypes.object 48 | } 49 | 50 | export default BlockQuoteParagraph 51 | -------------------------------------------------------------------------------- /src/components/BlockQuote/docs.md: -------------------------------------------------------------------------------- 1 | ### `
` 2 | 3 | A `
` can contain an arbitrary number of ``. 4 | 5 | Optionally you can provide a `` to describe the quote source. 6 | 7 | 8 | ```react|responsive 9 |
10 | 11 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin auctor felis tortor, at cursus metus interdum nec. Praesent tristique ante ut est ultrices, sed condimentum ipsum ullamcorper. Ut tempus vulputate cursus. Cras vitae varius sem. Fusce quis diam odio. Suspendisse ultrices est orci, quis ornare risus tempus ut. Morbi quis orci faucibus, aliquam nisl quis, pellentesque magna. Curabitur pellentesque nisi id ultricies efficitur. Cras facilisis volutpat purus eu viverra. 12 | 13 | 14 | Nunc a eleifend nulla, non condimentum felis. Vestibulum eu efficitur ex. Nunc egestas ullamcorper euismod. In odio nulla, posuere gravida faucibus et, rhoncus non purus. Cras eget justo sed dolor auctor suscipit. Quisque consequat tempus nisl. Curabitur a metus id mi varius vulputate. Nulla quis diam vitae elit scelerisque dapibus. Aliquam tincidunt sem nec nulla semper pulvinar. In posuere accumsan nunc vitae lacinia. Praesent et mollis elit. Proin quis massa feugiat augue dapibus convallis. Mauris sollicitudin libero non varius varius. 15 | 16 | 17 | Dummy Text{' '} 18 | lipsum.org 19 | 20 |
21 | ``` 22 | -------------------------------------------------------------------------------- /src/components/BlockQuote/index.js: -------------------------------------------------------------------------------- 1 | export { default as BlockQuote } from './BlockQuote' 2 | export { default as BlockQuoteParagraph } from './BlockQuoteParagraph' 3 | -------------------------------------------------------------------------------- /src/components/Callout/docs.md: -------------------------------------------------------------------------------- 1 | ## Callout 2 | 3 | A fixed callout animating in from the bottom on mobile and a in place callout on desktop. 4 | 5 | - `initiallyOpen`: boolean, note: after a click outside of it, it hides again 6 | - `icon`: React element 7 | - `align`: `left` (default) or `right`, alignment on desktop 8 | - `contentPaddingMobile`: set padding of callout content. (Defaults to "safari safe": `'15px 15px 50px'`). 9 | 10 | ```react|responsive 11 |
12 | } initiallyOpen contentPadding={16}> 13 | Hello World 14 | 15 |
16 | ``` 17 | 18 | ### Right Aligned 19 | 20 | ```react 21 |
22 |
23 | } align='right' initiallyOpen> 24 | Hello World 25 | 26 |
27 |
28 | ``` 29 | -------------------------------------------------------------------------------- /src/components/Chart/ChartContext.utils.js: -------------------------------------------------------------------------------- 1 | import { isValuePresent, unsafeDatumFn } from './utils' 2 | 3 | export const normalizeData = (x, xNormalizer) => d => ({ 4 | datum: d, 5 | x: xNormalizer(d[x]), 6 | value: isValuePresent(d.value) ? +d.value : undefined 7 | }) 8 | 9 | export const getAnnotationsXValues = (annotations, xNormalizer) => 10 | annotations 11 | ? annotations 12 | .reduce( 13 | (years, annotation) => 14 | years.concat(annotation.x, annotation.x1, annotation.x2), 15 | [] 16 | ) 17 | .filter(Boolean) 18 | .map(xNormalizer) // ensure format 19 | : [] 20 | 21 | export const categorizeData = category => d => { 22 | if (category) { 23 | const categorize = unsafeDatumFn(category) 24 | return { 25 | ...d, 26 | category: categorize(d.datum) 27 | } 28 | } 29 | return d 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Chart/Csv.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Chart from './' 3 | import { csvParse } from 'd3-dsv' 4 | 5 | export default ({ values, ...rest }) => ( 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/AxisFormatDropdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import CustomValueDropdown from './CustomValueDropdown' 3 | 4 | import { numberFormats, timeFormats, timeParsing } from './utils' 5 | 6 | export const AxisFormatDropdown = props => { 7 | const { 8 | property, 9 | context, 10 | value, 11 | onChange, 12 | timeParse, 13 | xNumberFormat, 14 | parent, 15 | defaultProps 16 | } = props 17 | if (context === 'time') { 18 | return ( 19 | <> 20 | 26 | 32 | 33 | ) 34 | } 35 | if (context === 'number') { 36 | const isOnXAxis = parent.match(/^x/) 37 | return ( 38 | 44 | ) 45 | } 46 | return null 47 | } 48 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/ColorDropdownElement.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontStyles } from '../../Typography' 3 | 4 | export const ColorDropdownElement = props => { 5 | const { colorRange, name } = props 6 | return ( 7 |
8 |
9 | {name} 10 |
11 |
12 | {colorRange.map((d, i) => ( 13 | 21 | ))} 22 |
23 |
24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/ColorPickerCallout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BlockPicker as ColorPicker } from 'react-color' 3 | import { plainButtonRule } from '../../Button' 4 | import CalloutMenu from '../../Callout/CalloutMenu' 5 | import { 6 | useColorContext 7 | } from '../../Colors/ColorContext' 8 | 9 | const ColorPickerCallout = ({ mode, pickableColors, color, onChange }) => { 10 | const [colorScheme] = useColorContext() 11 | 12 | return ( 13 |
21 | ( 23 |
45 | ) 46 | } 47 | 48 | export default ColorPickerCallout 49 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/CustomValueDropdown.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Dropdown from '../../Form/Dropdown' 3 | 4 | const CustomValueDropdown = ({ value, items, ...props }) => { 5 | const itemsHasValue = items.find(item => item.value === value) 6 | const itemsWithValue = itemsHasValue 7 | ? items 8 | : items.concat({ 9 | value, 10 | text: `Spezial: ${value}` 11 | }) 12 | 13 | return 18 | } 19 | 20 | export default CustomValueDropdown 21 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/TickField.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Field from '../../Form/Field' 4 | import { useCommaField } from './utils' 5 | import { timeParse } from '../../../lib/timeFormat' 6 | 7 | export const TickField = props => { 8 | const { 9 | property, 10 | groupObject, 11 | createOnFieldChange, 12 | value, 13 | config, 14 | context, 15 | timeParseDefault 16 | } = props 17 | 18 | const timeFormatParser = timeParse( 19 | config.timeParse || config.timeFormat || timeParseDefault 20 | ) 21 | 22 | const parser = 23 | context === 'time' 24 | ? timeFormatParser 25 | : context === 'number' 26 | ? d => +d 27 | : undefined 28 | 29 | const [ticksField, onTicksChange] = useCommaField( 30 | value, 31 | createOnFieldChange(property), 32 | parser, 33 | context 34 | ) 35 | 36 | return ( 37 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/docs.data.js: -------------------------------------------------------------------------------- 1 | export const chartData = ` 2 | facet,facet_value,year,value,confidence95_lower,confidence95_upper 3 | Altersgruppen,unter 10 Jahre,2014,0.219,0.207,0.232 4 | Altersgruppen,10-17 Jahre,2014,0.201,0.183,0.219 5 | Altersgruppen,18-24 Jahre,2014,0.243,0.194,0.292 6 | Altersgruppen,25-34 Jahre,2014,0.208,0.189,0.226 7 | Altersgruppen,35-44 Jahre,2014,0.128,0.113,0.144 8 | Altersgruppen,45-54 Jahre,2014,0.106,0.083,0.130 9 | Altersgruppen,55-64 Jahre,2014,0.132,0.112,0.151 10 | Altersgruppen,65-74 Jahre,2014,0.141,0.110,0.171 11 | Altersgruppen,über 74 Jahre,2014,0.133,0.120,0.146`.trim() 12 | -------------------------------------------------------------------------------- /src/components/Chart/Editor/docs.md: -------------------------------------------------------------------------------- 1 | ```react 2 | state: { 3 | type: 'TimeBar', 4 | activeTab: 'basic', 5 | tabs: [ 6 | { value: 'basic', text: 'Grundeinstellungen' }, 7 | { 8 | value: 'advanced', 9 | text: 'Erweiterte Optionen' 10 | } 11 | ] 12 | } 13 | --- 14 |
15 | value === state.activeTab)}> 16 | {state.tabs.map(({ value, text }) => ( 17 | { 22 | setState({ activeTab: value }) 23 | }} 24 | /> 25 | ))} 26 | 27 |
28 | 33 | 37 | 40 | 41 |
42 | ``` 43 | -------------------------------------------------------------------------------- /src/components/Chart/Hemicycle.schema.js: -------------------------------------------------------------------------------- 1 | export const hemicycleEditorSchema = ({ 2 | optionalDataColumnEnum, 3 | defaults, 4 | colorDropdownItems, 5 | chartSizes 6 | }) => { 7 | return { 8 | defaultProps: defaults, 9 | properties: { 10 | basic: { 11 | color: { 12 | title: 'Farbe', 13 | properties: { 14 | color: { 15 | title: 'Spalte auswählen', 16 | type: 'string', 17 | enum: optionalDataColumnEnum 18 | }, 19 | colorRange: { 20 | title: 'Farbschema auswählen', 21 | type: 'string', 22 | enum: colorDropdownItems 23 | } 24 | } 25 | } 26 | }, 27 | advanced: { 28 | layout: { 29 | title: 'Layout', 30 | properties: { 31 | size: { 32 | title: 'Darstellung im Beitrag', 33 | type: 'string', 34 | enum: chartSizes 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Chart/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Bundesregierung 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /src/components/Chart/Layout.constants.js: -------------------------------------------------------------------------------- 1 | export const COLUMN_TITLE_HEIGHT = 24 2 | export const COLUMN_PADDING = 20 3 | 4 | export const AXIS_TOP_HEIGHT = 24 5 | export const AXIS_BOTTOM_HEIGHT = 24 6 | export const X_UNIT_PADDING = 30 7 | export const AXIS_BOTTOM_CUTOFF_HEIGHT = 40 8 | export const AXIS_BOTTOM_XUNIT_HEIGHT = 12 9 | 10 | export const PADDING_TOP = 24 11 | export const PADDING_SIDES = 20 12 | 13 | export const Y_CONNECTOR = 7 14 | export const Y_CONNECTOR_PADDING = 4 15 | export const Y_LABEL_HEIGHT = 14 16 | export const Y_END_LABEL_SPACE = 3 // width of space between label and value 17 | export const X_TICK_HEIGHT = 4 18 | export const Y_GROUP_MARGIN = 20 19 | -------------------------------------------------------------------------------- /src/components/Chart/ScatterPlotCanvas.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useEffect } from 'react' 2 | 3 | const ScatterPlotCanvas = ({ symbols, width, height, getColor, opacity }) => { 4 | const canvasRef = useRef() 5 | 6 | useEffect(() => { 7 | const canvas = canvasRef.current 8 | 9 | const devicePixelRatio = window.devicePixelRatio || 1 10 | canvas.width = width * devicePixelRatio 11 | canvas.height = height * devicePixelRatio 12 | 13 | const ctx = canvas.getContext('2d') 14 | ctx.save() 15 | ctx.scale(devicePixelRatio, devicePixelRatio) 16 | ctx.clearRect(0, 0, width, height) 17 | 18 | ctx.globalAlpha = opacity 19 | symbols.forEach(({ cx, cy, r, value }) => { 20 | ctx.beginPath() 21 | ctx.moveTo(cx + r, cy) 22 | ctx.arc(cx, cy, r, 0, 2 * Math.PI) 23 | 24 | ctx.fillStyle = getColor(value) 25 | ctx.fill() 26 | }) 27 | 28 | ctx.restore() 29 | }, [symbols, width, height, opacity, getColor]) 30 | 31 | return ( 32 | 42 | ) 43 | } 44 | 45 | export default ScatterPlotCanvas 46 | -------------------------------------------------------------------------------- /src/components/Chart/ScatterPlots.schema.js: -------------------------------------------------------------------------------- 1 | export const scatterPlotEditorSchema = ({ 2 | optionalDataColumnEnum, 3 | defaults, 4 | colorDropdownItems, 5 | chartSizes, 6 | columnAmount 7 | }) => { 8 | return { 9 | defaultProps: defaults, 10 | properties: { 11 | basic: { 12 | color: { 13 | title: 'Farbe', 14 | properties: { 15 | color: { 16 | title: 'Spalte auswählen', 17 | type: 'string', 18 | enum: optionalDataColumnEnum 19 | }, 20 | colorRange: { 21 | title: 'Farbschema auswählen', 22 | type: 'string', 23 | enum: colorDropdownItems 24 | } 25 | } 26 | }, 27 | layout: { 28 | title: 'Aufteilen in mehrere Charts', 29 | properties: { 30 | column: { 31 | title: 'Spalte auswählen', 32 | type: 'string', 33 | enum: optionalDataColumnEnum 34 | }, 35 | columns: { 36 | title: 'Anzahl Spalten pro Zeile', 37 | type: 'number', 38 | enum: columnAmount 39 | } 40 | } 41 | } 42 | }, 43 | advanced: { 44 | layout: { 45 | title: 'Layout', 46 | properties: { 47 | size: { 48 | title: 'Darstellung im Beitrag', 49 | type: 'string', 50 | enum: chartSizes 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/Chart/ScatterPlots.utils.js: -------------------------------------------------------------------------------- 1 | import { extent } from 'd3-array' 2 | import { scaleLinear, scaleLog } from 'd3-scale' 3 | 4 | export const scales = { 5 | linear: scaleLinear, 6 | log: scaleLog 7 | } 8 | 9 | export const tickAccessor = d => d.tick 10 | 11 | export const aggregateValues = (data, accessor, xTicks = [], xLines = []) => 12 | data 13 | .map(accessor) 14 | .concat(xTicks) 15 | .concat(xLines.map(tickAccessor)) 16 | 17 | const getNice = (nice, plotDimension) => 18 | nice === undefined 19 | ? Math.min(Math.max(Math.round(plotDimension / 150), 3), 5) 20 | : nice 21 | 22 | export const getPlot = (scale, values, range, nice, plotDimension) => { 23 | const plotScale = scales[scale]() 24 | .domain(extent(values)) 25 | .range(range) 26 | const niceValue = getNice(nice, plotDimension) 27 | if (niceValue) { 28 | plotScale.nice(niceValue) 29 | } 30 | return plotScale 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Chart/Table.schema.js: -------------------------------------------------------------------------------- 1 | export const tableEditorSchema = ({ defaults, chartSizes }) => { 2 | return { 3 | defaultProps: defaults, 4 | properties: { 5 | basic: {}, 6 | advanced: { 7 | layout: { 8 | title: 'Layout', 9 | properties: { 10 | size: { 11 | title: 'Darstellung im Beitrag', 12 | type: 'string', 13 | enum: chartSizes 14 | } 15 | } 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Collapsable/index.js: -------------------------------------------------------------------------------- 1 | export { default as Collapsable } from './Collapsable' 2 | -------------------------------------------------------------------------------- /src/components/Colors/useColorContext.js: -------------------------------------------------------------------------------- 1 | export { useColorContext } from './ColorContext' 2 | -------------------------------------------------------------------------------- /src/components/CommentBody/email/BlockCode.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { fontFamilies } from '../../../theme/fonts' 4 | 5 | export default ({ children }) => ( 6 |
 7 |     
17 |       {children}
18 |     
19 |   
20 | ) 21 | -------------------------------------------------------------------------------- /src/components/CommentBody/email/Blockquote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import colors from '../../../theme/colors' 4 | import { paragraphStyle } from './Paragraph' 5 | 6 | export const BlockQuoteParagraph = ({ children }) => ( 7 |

14 | {children} 15 |

16 | ) 17 | 18 | export const BlockQuoteNested = ({ children }) => ( 19 |
27 | {children} 28 |
29 | ) 30 | 31 | export default ({ children }) => ( 32 |
39 | {children} 40 |
41 | ) 42 | -------------------------------------------------------------------------------- /src/components/CommentBody/email/Container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { serifRegular16 } from '../../Typography/styles' 4 | 5 | // em and strong css is injected into email html head in the backend template 6 | // https://github.com/orbiting/backends/commit/ff8b6b35927f6a3402baec7f293ec7cdb25a90d8 7 | 8 | export default ({ children }) =>
{children}
9 | -------------------------------------------------------------------------------- /src/components/CommentBody/email/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { paragraphStyle } from './Paragraph' 4 | 5 | const listStyle = { 6 | marginLeft: '1em', 7 | paddingLeft: '1em' 8 | } 9 | 10 | export const ListItem = ({ children }) => ( 11 |
  • {children}
  • 12 | ) 13 | 14 | export default ({ children, data }) => 15 | data.ordered ? ( 16 |
      17 | {children} 18 |
    19 | ) : ( 20 |
      {children}
    21 | ) 22 | -------------------------------------------------------------------------------- /src/components/CommentBody/email/index.js: -------------------------------------------------------------------------------- 1 | export { default as BlockCode } from './BlockCode' 2 | 3 | export { 4 | default as BlockQuote, 5 | BlockQuoteNested, 6 | BlockQuoteParagraph 7 | } from './Blockquote' 8 | 9 | export { default as Container } from './Container' 10 | 11 | export { default as List, ListItem } from './List' 12 | 13 | export { 14 | Paragraph, 15 | Code, 16 | Definition, 17 | Heading, 18 | Link, 19 | StrikeThrough 20 | } from './Paragraph' 21 | -------------------------------------------------------------------------------- /src/components/CommentBody/web/BlockCode.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | 4 | import { mUp } from '../../../theme/mediaQueries' 5 | import { useColorContext } from '../../Colors/useColorContext' 6 | const styles = { 7 | pre: css({ 8 | margin: '20px auto', 9 | whiteSpace: 'pre-wrap' 10 | }), 11 | code: css({ 12 | display: 'block', 13 | fontSize: '90%', 14 | margin: 0, 15 | padding: '0 15px 12px 15px', 16 | [mUp]: { 17 | padding: '0 25px 20px 25px', 18 | '&:first-child': { 19 | paddingTop: '20px' 20 | } 21 | }, 22 | '&:first-child': { 23 | paddingTop: '12px' 24 | } 25 | }) 26 | } 27 | 28 | export default ({ children }) => { 29 | const [colorScheme] = useColorContext() 30 | return ( 31 |
    32 |       
    33 |         {children}
    34 |       
    35 |     
    36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/components/CommentBody/web/Container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | 4 | import { mUp } from '../../../theme/mediaQueries' 5 | import { serifRegular14, serifRegular16 } from '../../Typography/styles' 6 | import { Editorial } from '../../Typography' 7 | import { convertStyleToRem } from '../../Typography/utils' 8 | 9 | const styles = { 10 | container: css({ 11 | wordWrap: 'break-word', 12 | ...convertStyleToRem(serifRegular14), 13 | [mUp]: { 14 | ...convertStyleToRem(serifRegular16) 15 | }, 16 | '& > *:first-child': { 17 | marginTop: 0 18 | }, 19 | '& > *:last-child': { 20 | marginBottom: 0 21 | } 22 | }) 23 | } 24 | 25 | export default ({ children }) => ( 26 |
    27 | {children} 28 |
    29 | ) 30 | -------------------------------------------------------------------------------- /src/components/CommentBody/web/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { List, ListItem as CommentListItem } from '../../List' 4 | 5 | export const ListItem = ({ children }) => ( 6 | 12 | {children} 13 | 14 | ) 15 | 16 | export default List 17 | -------------------------------------------------------------------------------- /src/components/CommentBody/web/index.js: -------------------------------------------------------------------------------- 1 | export { default as CommentBodyBlockCode } from './BlockCode' 2 | 3 | export { 4 | default as CommentBodyBlockQuote, 5 | BlockQuoteNested as CommentBodyBlockQuoteNested, 6 | BlockQuoteParagraph as CommentBodyBlockQuoteParagraph 7 | } from './Blockquote' 8 | 9 | export { default as CommentBodyContainer } from './Container' 10 | 11 | export { 12 | default as CommentBodyList, 13 | ListItem as CommentBodyListItem 14 | } from './List' 15 | 16 | export { 17 | default as CommentBodyParagraph, 18 | Code as CommentBodyCode, 19 | Definition as CommentBodyDefinition, 20 | Heading as CommentBodyHeading, 21 | FeaturedText as CommentBodyFeaturedText 22 | } from './Paragraph' 23 | -------------------------------------------------------------------------------- /src/components/CommentTeaser/docs.imports.js: -------------------------------------------------------------------------------- 1 | export { default as CommentTeaser } from './CommentTeaser' 2 | -------------------------------------------------------------------------------- /src/components/CommentTeaser/index.js: -------------------------------------------------------------------------------- 1 | export { default as CommentTeaser } from './CommentTeaser' 2 | -------------------------------------------------------------------------------- /src/components/Discussion/Composer/docs.md: -------------------------------------------------------------------------------- 1 | ```remove-react-source 2 | ``` 3 | 4 | ## `` 5 | 6 | The `` is a stateful component which manages the input text and selected tag. The following callbacks are used to communicate with its controller: 7 | 8 | * `onClose(): void` 9 | * `onSubmit({ text: string, tags?: string[] }): Promise<{}>` 10 | * `onEditPreferences(): void` 11 | 12 | The `onSubmit()` function must return a Promise which is resolved when the backend accepts the comment, or is rejected with a string that describes the error. The error will then be shown below the form, the user can further edit the comment and try to submit again. 13 | 14 | ```react|noSource,plain 15 | 16 | ``` 17 | 18 | ## `` 19 | 20 | Same height as the `
    ` in the `` so that it can be used in its place and we can have a nice transition between the placeholder and the whole `` component. 21 | 22 | ```react|noSource,plain 23 | {}} 27 | /> 28 | ``` 29 | -------------------------------------------------------------------------------- /src/components/Discussion/Composer/index.js: -------------------------------------------------------------------------------- 1 | export * from './CommentComposer' 2 | export * from './CommentComposerPlaceholder' 3 | 4 | // To allow CommentHeader preview 5 | export { CommentHeaderProfile } from '../Internal/Composer/Header' 6 | 7 | export { readDraft as readDiscussionCommentDraft } from './CommentDraftHelper' 8 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Comment/RelativeTime.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { useCurrentMinute } from '../../../../lib/useCurrentMinute' 4 | import { formatTimeRelative } from '../../DiscussionContext' 5 | 6 | const RelativeTime = ({ date, t, isDesktop, direction = 'past' }) => { 7 | const now = useCurrentMinute() 8 | 9 | return ( 10 | 11 | {formatTimeRelative(new Date(date), { 12 | t, 13 | isDesktop, 14 | now, 15 | direction 16 | })} 17 | 18 | ) 19 | } 20 | 21 | export default RelativeTime 22 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Comment/index.js: -------------------------------------------------------------------------------- 1 | export * from './CommentActions' 2 | export * from './Body' 3 | export * from './Context' 4 | export * from './Header' 5 | export * from './IconLink' 6 | export * from './CommentEmbed' 7 | export * from './VoteButtons' 8 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Comment/render.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { renderMdast } from 'mdast-react-render' 3 | import createCommentSchema from '../../../../templates/Comment' 4 | import { useColorContext } from '../../../Colors/useColorContext' 5 | 6 | const schema = createCommentSchema() 7 | 8 | const MissingNode = ({ node, children }) => { 9 | const [colorScheme] = useColorContext() 10 | return ( 11 | 21 | {children || node.value || node.identifier || '[…]'} 22 | 23 | ) 24 | } 25 | 26 | export const renderCommentMdast = content => 27 | renderMdast(content, schema, { MissingNode }) 28 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Composer/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { sansSerifRegular16 } from '../../../Typography/styles' 4 | import { convertStyleToRem } from '../../../Typography/utils' 5 | import { useColorContext } from '../../../Colors/useColorContext' 6 | 7 | const styles = { 8 | root: css({ 9 | ...convertStyleToRem(sansSerifRegular16), 10 | marginTop: 12 11 | }) 12 | } 13 | 14 | export const Error = ({ children }) => { 15 | const [colorScheme] = useColorContext() 16 | return ( 17 |
    18 | {children} 19 |
    20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Composer/Tags.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import Radio from '../../../Form/Radio' 5 | import { mUp } from '../../../../theme/mediaQueries' 6 | 7 | const styles = { 8 | root: css({ 9 | padding: '12px', 10 | display: 'flex', 11 | flexDirection: 'column', 12 | flexWrap: 'wrap', 13 | [mUp]: { 14 | flexDirection: 'row' 15 | } 16 | }), 17 | tag: css({ 18 | marginRight: 24, 19 | '& ~ &': { 20 | marginTop: 5 21 | }, 22 | [mUp]: { 23 | '& ~ &': { 24 | marginTop: 0 25 | } 26 | } 27 | }) 28 | } 29 | 30 | export const Tags = ({ tags, value, onChange }) => { 31 | if (!tags || tags.length === 0) { 32 | return null 33 | } else { 34 | return ( 35 |
    36 | {tags.map(tag => ( 37 |
    38 | onChange(event.target.value)} 42 | > 43 | {tag} 44 | 45 |
    46 | ))} 47 |
    48 | ) 49 | } 50 | } 51 | 52 | Tags.propTypes = { 53 | tags: PropTypes.arrayOf(PropTypes.string), 54 | value: PropTypes.string, 55 | onChange: PropTypes.func.isRequired 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/Composer/index.js: -------------------------------------------------------------------------------- 1 | export * from './Actions' 2 | export * from './Error' 3 | export * from './Header' 4 | export * from './Tags' 5 | -------------------------------------------------------------------------------- /src/components/Discussion/Internal/PropTypes.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | export const DisplayAuthorPropType = PropTypes.shape({ 4 | name: PropTypes.string.isRequired, 5 | profilePicture: PropTypes.string, 6 | credential: PropTypes.shape({ 7 | description: PropTypes.string.isRequired, 8 | verified: PropTypes.bool 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /src/components/Discussion/Statements/helpers/ColorContextHelper.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react' 2 | import { getUniqueColorTagName } from './colorHelper' 3 | import { 4 | ColorContextLocalExtension, 5 | ColorContextProvider 6 | } from '../../../Colors/ColorContext' 7 | 8 | const ColorContextHelper = ({ children, tagMappings = [] }) => { 9 | const localColors = useMemo(() => { 10 | if (!tagMappings.length > 0) return null 11 | 12 | const colorsObject = { light: {}, dark: {} } 13 | 14 | if (!tagMappings) return colorsObject 15 | 16 | tagMappings.forEach(({ tag, color }) => { 17 | const colorName = getUniqueColorTagName(tag) 18 | colorsObject.light[colorName] = color.light 19 | colorsObject.dark[colorName] = color.dark 20 | }) 21 | 22 | return colorsObject 23 | }, [tagMappings]) 24 | 25 | if (!localColors) return children 26 | 27 | return ( 28 | 29 | 30 | {children} 31 | 32 | 33 | ) 34 | } 35 | 36 | export default ColorContextHelper 37 | -------------------------------------------------------------------------------- /src/components/Discussion/Statements/helpers/colorHelper.js: -------------------------------------------------------------------------------- 1 | import { stripTag } from './tagHelper' 2 | 3 | export function getUniqueColorTagName(tagName) { 4 | return `tag-${stripTag(tagName)}-color` 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Discussion/Statements/helpers/tagHelper.js: -------------------------------------------------------------------------------- 1 | export function stripTag(tag) { 2 | return tag.toLowerCase().trim() 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Discussion/Statements/index.js: -------------------------------------------------------------------------------- 1 | import StatementNode from './StatementNode' 2 | 3 | export { StatementNode } 4 | -------------------------------------------------------------------------------- /src/components/Discussion/Tree/BoardComment.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import CommentNode, { CommentProps } from './CommentNode' 4 | import { CommentEmbed } from '../Internal/Comment' 5 | import { mUp } from '../../../theme/mediaQueries' 6 | 7 | const styles = { 8 | wrapper: css({ 9 | marginTop: 10, 10 | marginBottom: 50, 11 | [mUp]: { 12 | display: 'flex', 13 | marginLeft: -10, 14 | marginRight: -10 15 | } 16 | }), 17 | item: css({ 18 | [mUp]: { 19 | padding: 10, 20 | width: '50%', 21 | flex: '1 0 auto' 22 | } 23 | }) 24 | } 25 | 26 | 27 | const BoardComment = (props: CommentProps) => ( 28 |
    29 |
    30 | 31 |
    32 | {props.comment?.embed && ( 33 |
    34 | 35 |
    36 | )} 37 |
    38 | ) 39 | 40 | export default BoardComment 41 | -------------------------------------------------------------------------------- /src/components/Discussion/Tree/docs.imports.js: -------------------------------------------------------------------------------- 1 | import * as allComments from '../__docs__/comments' 2 | 3 | export const comments = { ...allComments } 4 | -------------------------------------------------------------------------------- /src/components/Discussion/Tree/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CommentNode } from "./CommentNode"; 2 | export type { CommentProps } from './CommentNode' 3 | export { default as BoardComment } from './BoardComment' 4 | -------------------------------------------------------------------------------- /src/components/Discussion/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The size of each indent. Note that this is the total size including 3 | * the width of the vertical line. 4 | */ 5 | export const indentSizeS = 10 6 | export const indentSizeM = 16 7 | 8 | /** 9 | * The width of the vertical line. 10 | */ 11 | export const verticalLineWidth = 2 12 | 13 | /** 14 | * Limit how deep we nest comments using the standard layout/visual style. 15 | * Comments nested deeper than that are displayed differently. 16 | */ 17 | export const nestLimit = 4 18 | -------------------------------------------------------------------------------- /src/components/Discussion/docs.md: -------------------------------------------------------------------------------- 1 | We use React Context to make static properties and discussion-wide callbacks available to components. So make sure to wrap the components in `` before you render `` 2 | 3 | ```code|lang-js 4 | 5 | 6 | 7 | ``` 8 | 9 | ### Configuration 10 | 11 | Some config options are stored in `src/components/Discussion/config.js`. If you need to tweak the layout, first have a look there if there is already an option for it. 12 | 13 | ### Exports 14 | 15 | The following symbols are exported from the styleguide: 16 | 17 | * `DiscussionContext` 18 | * `` 19 | * `` 20 | * `` -------------------------------------------------------------------------------- /src/components/Dossier/Headline.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { mUp } from '../TeaserFront/mediaQueries' 4 | import { serifTitle38, serifTitle58 } from '../Typography/styles' 5 | 6 | const styles = { 7 | base: css({ 8 | margin: 0, 9 | ...serifTitle38, 10 | marginBottom: '25px', 11 | [mUp]: { 12 | ...serifTitle58, 13 | marginBottom: '35px' 14 | } 15 | }) 16 | } 17 | 18 | const Headline = ({ children, poster, large, medium }) => { 19 | return ( 20 |

    21 | {children} 22 |

    23 | ) 24 | } 25 | 26 | export default Headline 27 | -------------------------------------------------------------------------------- /src/components/Dossier/Lead.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp } from '../TeaserFront/mediaQueries' 5 | import { serifRegular17, serifRegular23 } from '../Typography/styles' 6 | 7 | const styles = { 8 | lead: css({ 9 | ...serifRegular17, 10 | margin: '0 0 10px 0', 11 | [mUp]: { 12 | ...serifRegular23, 13 | margin: '0 0 20px 0' 14 | } 15 | }) 16 | } 17 | 18 | const Lead = ({ children }) => { 19 | return

    {children}

    20 | } 21 | 22 | Lead.propTypes = { 23 | children: PropTypes.node 24 | } 25 | 26 | export default Lead 27 | -------------------------------------------------------------------------------- /src/components/Dossier/More.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import colors from '../../theme/colors' 4 | import { mUp } from '../TeaserFront/mediaQueries' 5 | import { sansSerifRegular15, sansSerifRegular21 } from '../Typography/styles' 6 | 7 | const styles = { 8 | more: css({ 9 | ...sansSerifRegular15, 10 | margin: 0, 11 | minHeight: '15px', 12 | textAlign: 'center', 13 | [mUp]: { 14 | ...sansSerifRegular21 15 | }, 16 | '& > a': { 17 | color: colors.text 18 | }, 19 | '@media (hover)': { 20 | '& > a:hover': { 21 | color: colors.lightText 22 | } 23 | } 24 | }) 25 | } 26 | 27 | const More = ({ children, attributes }) => { 28 | return ( 29 |

    30 | {children} 31 |

    32 | ) 33 | } 34 | 35 | export default More 36 | -------------------------------------------------------------------------------- /src/components/Dossier/Subheader.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { mUp } from '../TeaserFront/mediaQueries' 4 | import { sansSerifMedium16, sansSerifMedium20 } from '../Typography/styles' 5 | import { convertStyleToRem } from '../Typography/utils' 6 | import { useColorContext } from '../Colors/useColorContext' 7 | 8 | const styles = { 9 | subheader: css({ 10 | ...convertStyleToRem(sansSerifMedium16), 11 | [mUp]: { 12 | ...convertStyleToRem(sansSerifMedium20) 13 | } 14 | }), 15 | spaced: css({ 16 | width: '100%', 17 | margin: '70px 0 30px 0', 18 | textAlign: 'center', 19 | [mUp]: { 20 | margin: '100px 0 70px 0' 21 | } 22 | }) 23 | } 24 | 25 | const Subheader = ({ children, singleColumn }) => { 26 | const [colorScheme] = useColorContext() 27 | 28 | return ( 29 |

    34 | {children} 35 |

    36 | ) 37 | } 38 | 39 | export default Subheader 40 | -------------------------------------------------------------------------------- /src/components/Dossier/Tag.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { mUp } from '../TeaserFront/mediaQueries' 4 | import { sansSerifMedium14, sansSerifMedium20 } from '../Typography/styles' 5 | import { FolderIcon } from '../Icons' 6 | 7 | const styles = { 8 | tag: css({ 9 | display: 'inline-block', 10 | ...sansSerifMedium14, 11 | margin: '0 0 18px 0', 12 | [mUp]: { 13 | ...sansSerifMedium20, 14 | margin: '0 0 28px 0' 15 | } 16 | }), 17 | icon: css({ 18 | marginRight: '8px' 19 | }) 20 | } 21 | 22 | const Tag = ({ children, attributes }) => { 23 | return ( 24 |
    25 | 26 | {children} 27 |
    28 | ) 29 | } 30 | 31 | export default Tag 32 | -------------------------------------------------------------------------------- /src/components/Dossier/Teaser.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp } from '../TeaserFront/mediaQueries' 5 | 6 | const styles = { 7 | container: css({ 8 | backgroundColor: '#f5f5f5', 9 | position: 'relative', 10 | lineHeight: 0, 11 | margin: 0, 12 | padding: '30px 15px', 13 | [mUp]: { 14 | padding: '60px 0' 15 | } 16 | }) 17 | } 18 | 19 | const Teaser = ({ children, attributes, onClick }) => { 20 | return ( 21 |
    29 | {children} 30 |
    31 | ) 32 | } 33 | 34 | Teaser.propTypes = { 35 | children: PropTypes.node.isRequired, 36 | attributes: PropTypes.object, 37 | onClick: PropTypes.func 38 | } 39 | 40 | Teaser.defaultProps = {} 41 | 42 | export default Teaser 43 | -------------------------------------------------------------------------------- /src/components/Dossier/TileHeadline.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { tUp } from '../TeaserFront/mediaQueries' 4 | import { 5 | serifTitle26, 6 | serifTitle32, 7 | cursiveTitle26, 8 | cursiveTitle32, 9 | sansSerifMedium26, 10 | sansSerifMedium32 11 | } from '../Typography/styles' 12 | import { convertStyleToRem } from '../Typography/utils' 13 | 14 | const styles = { 15 | base: css({ 16 | margin: 0, 17 | marginBottom: '16px', 18 | [tUp]: { 19 | marginBottom: '25px' 20 | } 21 | }), 22 | editorial: css({ 23 | ...convertStyleToRem(serifTitle26), 24 | [tUp]: { 25 | ...convertStyleToRem(serifTitle32) 26 | } 27 | }), 28 | interaction: css({ 29 | ...convertStyleToRem(sansSerifMedium26), 30 | [tUp]: { 31 | ...convertStyleToRem(sansSerifMedium32) 32 | } 33 | }), 34 | scribble: css({ 35 | ...convertStyleToRem(cursiveTitle26), 36 | [tUp]: { 37 | ...convertStyleToRem(cursiveTitle32) 38 | } 39 | }) 40 | } 41 | 42 | export const Editorial = ({ children }) => { 43 | return ( 44 |

    45 | {children} 46 |

    47 | ) 48 | } 49 | 50 | export const Interaction = ({ children }) => { 51 | return ( 52 |

    53 | {children} 54 |

    55 | ) 56 | } 57 | 58 | export const Scribble = ({ children }) => { 59 | return ( 60 |

    61 | {children} 62 |

    63 | ) 64 | } 65 | -------------------------------------------------------------------------------- /src/components/Dossier/index.js: -------------------------------------------------------------------------------- 1 | import * as _DossierTileHeadline from './TileHeadline' 2 | 3 | export const DossierTileHeadline = { ..._DossierTileHeadline } 4 | 5 | export { default as TeaserFrontDossier } from './Teaser' 6 | export { default as TeaserFrontDossierIntro } from './TeaserIntro' 7 | export { default as TeaserFrontDossierHeadline } from './Headline' 8 | export { default as TeaserFrontDossierLead } from './Lead' 9 | export { default as TeaserFrontDossierMore } from './More' 10 | 11 | export { default as DossierTag } from './Tag' 12 | export { default as DossierSubheader } from './Subheader' 13 | -------------------------------------------------------------------------------- /src/components/ErrorBoundary/index.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent, Fragment } from 'react' 2 | import { P } from '../Typography/Interaction' 3 | import colors from '../../theme/colors' 4 | 5 | class ErrorBoundary extends PureComponent { 6 | constructor(...args) { 7 | super(...args) 8 | 9 | this.state = {} 10 | } 11 | componentDidCatch(error) { 12 | this.setState({ error }) 13 | } 14 | render() { 15 | const { children, failureMessage, showException } = this.props 16 | const { error } = this.state 17 | if (error) { 18 | return ( 19 | 20 |

    21 | {failureMessage} 22 |

    23 | {showException && ( 24 |

    25 | {error.toString()} 26 |

    27 | )} 28 |
    29 | ) 30 | } 31 | return children 32 | } 33 | } 34 | 35 | export default ErrorBoundary 36 | -------------------------------------------------------------------------------- /src/components/Figure/Caption.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifRegular12, sansSerifRegular15 } from '../Typography/styles' 4 | import { css, merge } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { PADDING } from '../Center' 7 | import { convertStyleToRem, pxToRem } from '../Typography/utils' 8 | import { useColorContext } from '../Colors/useColorContext' 9 | 10 | const styles = { 11 | caption: css({ 12 | margin: '5px auto 0 auto', 13 | width: '100%', 14 | maxWidth: `calc(100vw - ${PADDING * 2}px)`, 15 | ...convertStyleToRem(sansSerifRegular12), 16 | [mUp]: { 17 | ...convertStyleToRem(sansSerifRegular15), 18 | lineHeight: pxToRem('18px') 19 | } 20 | }), 21 | groupCaption: css({ 22 | marginTop: -10, 23 | marginBottom: 15 24 | }) 25 | } 26 | 27 | export const Caption = ({ children, attributes, groupCaption }) => { 28 | const [colorScheme] = useColorContext() 29 | 30 | return ( 31 |
    36 | {children} 37 |
    38 | ) 39 | } 40 | 41 | Caption.propTypes = { 42 | children: PropTypes.node.isRequired, 43 | attributes: PropTypes.object, 44 | groupCaption: PropTypes.bool 45 | } 46 | 47 | export default Caption 48 | -------------------------------------------------------------------------------- /src/components/Figure/SwitchImage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useColorContext } from '../Colors/useColorContext' 3 | 4 | const SwitchImage = ({ src, srcSet, dark, alt, ...props }) => { 5 | const [colorScheme] = useColorContext() 6 | 7 | return ( 8 | <> 9 | {alt} 16 | {dark && ( 17 | {alt} 24 | )} 25 | 26 | ) 27 | } 28 | 29 | export default SwitchImage 30 | -------------------------------------------------------------------------------- /src/components/Form/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import NativeDropdown from './NativeDropdown' 5 | import VirtualDropdown from './VirtualDropdown' 6 | 7 | const getUseNative = () => 8 | typeof navigator === 'undefined' || 9 | /iPad|iPhone|iPod|android/i.test(navigator.userAgent) 10 | 11 | let defaultUseNative = true 12 | 13 | const Dropdown = (props: DropdownProps) => { 14 | const [useNative, setUseNative] = useState(defaultUseNative) 15 | useEffect(() => { 16 | defaultUseNative = getUseNative() 17 | setUseNative(defaultUseNative) 18 | }, []) 19 | return useNative ? ( 20 | 21 | ) : ( 22 | 23 | ) 24 | } 25 | 26 | Dropdown.Native = NativeDropdown 27 | Dropdown.Virtual = VirtualDropdown 28 | 29 | type ItemType = { 30 | value: string 31 | text: string 32 | element?: React.ReactNode 33 | } 34 | 35 | export type DropdownProps = { 36 | label?: string 37 | items: ItemType[] 38 | value?: string 39 | onChange?: (item: ItemType) => void 40 | } 41 | 42 | export default Dropdown 43 | -------------------------------------------------------------------------------- /src/components/Form/Field.docs.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const RefComponent = ({ children, ...props }) => { 4 | const ref = React.useRef() 5 | 6 | return children(ref) 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Form/NativeDropdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { Label } from './DropdownLabel' 3 | import { DropdownProps } from './Dropdown' 4 | 5 | const NativeDropdown = (props: DropdownProps) => { 6 | const { label, items, value, onChange } = props 7 | const [focus, setFocus] = useState(false) 8 | const selectedItem = items.find(item => item.value === value) 9 | 10 | return ( 11 | 38 | ) 39 | } 40 | 41 | export default React.memo(NativeDropdown) 42 | -------------------------------------------------------------------------------- /src/components/Form/Slider.docs.md: -------------------------------------------------------------------------------- 1 | ```react|plain 2 | state: {example1: 3} 3 | --- 4 |
    5 | setState({example1})} /> 11 |
    12 | ``` 13 | 14 | ```react|plain 15 |
    16 | e.preventDefault()} 22 | title='I am inactive' /> 23 |
    24 | ``` 25 | 26 | ```react|plain 27 | state: {example3: 1} 28 | --- 29 |
    30 | setState({example3})} /> 37 |
    38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /src/components/Form/constants.ts: -------------------------------------------------------------------------------- 1 | export const X_PADDING = 0 2 | export const Y_PADDING = 9 3 | export const BORDER_WIDTH = 1 4 | export const LINE_HEIGHT = 20 5 | export const FIELD_HEIGHT = 40 6 | export const LABEL_HEIGHT = 20 7 | -------------------------------------------------------------------------------- /src/components/Format/Tag.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | 5 | import { 6 | sansSerifMedium14, 7 | sansSerifMedium16, 8 | sansSerifMedium18, 9 | sansSerifMedium20 10 | } from '../Typography/styles' 11 | import { mUp } from '../../theme/mediaQueries' 12 | import { useColorContext } from '../Colors/useColorContext' 13 | 14 | const styles = { 15 | container: css({ 16 | ...sansSerifMedium18, 17 | display: 'inline-block', 18 | padding: 0, 19 | margin: '0 10px 5px 0', 20 | [mUp]: { 21 | ...sansSerifMedium20, 22 | margin: '0 20px 5px 0' 23 | } 24 | }), 25 | count: css({ 26 | ...sansSerifMedium14, 27 | marginLeft: 5, 28 | [mUp]: { 29 | ...sansSerifMedium16 30 | } 31 | }) 32 | } 33 | 34 | const FormatTag = ({ label, count, color }) => { 35 | const [colorScheme] = useColorContext() 36 | return ( 37 |
    38 | {label} 39 | {count !== undefined && ( 40 | 41 | {count} 42 | 43 | )} 44 |
    45 | ) 46 | } 47 | 48 | FormatTag.propTypes = { 49 | label: PropTypes.string.isRequired, 50 | count: PropTypes.number, 51 | color: PropTypes.string 52 | } 53 | 54 | export default FormatTag 55 | -------------------------------------------------------------------------------- /src/components/Format/docs.md: -------------------------------------------------------------------------------- 1 | Props: 2 | - `label`: string, the label of the tag. 3 | - `count`: optional number, the count. 4 | - `color`: optional string, the color of the label. 5 | 6 | ```react 7 | 12 | ``` 13 | 14 | ```react 15 | 19 | ``` 20 | 21 | ```react 22 | 25 | ``` 26 | 27 | ```react 28 |
    29 | 34 | 39 | 44 | 49 | 54 |
    55 | ``` 56 | -------------------------------------------------------------------------------- /src/components/Format/index.js: -------------------------------------------------------------------------------- 1 | export { default as FormatTag } from './Tag' 2 | -------------------------------------------------------------------------------- /src/components/Grid/docs.md: -------------------------------------------------------------------------------- 1 | Containers define the max width and ensure a horizontal padding. 2 | 3 | ```react 4 | 5 |
    6 | 7 | ``` 8 | 9 | ```react 10 | 11 |
    12 | 13 | ``` 14 | -------------------------------------------------------------------------------- /src/components/Grid/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | 5 | export const GUTTER = 42 6 | export const CONTENT_PADDING = 15 7 | 8 | export const NARROW_CONTENT_MAX_WIDTH = 680 9 | export const CONTENT_MAX_WIDTH = 1230 10 | 11 | const styles = { 12 | container: css({ 13 | boxSizing: 'border-box', 14 | width: '100%', 15 | padding: `0 ${CONTENT_PADDING}px`, 16 | maxWidth: CONTENT_MAX_WIDTH, 17 | marginLeft: 'auto', 18 | marginRight: 'auto' 19 | }), 20 | narrowContainer: css({ 21 | boxSizing: 'border-box', 22 | width: '100%', 23 | padding: `0 ${CONTENT_PADDING}px`, 24 | maxWidth: NARROW_CONTENT_MAX_WIDTH, 25 | marginLeft: 'auto', 26 | marginRight: 'auto' 27 | }) 28 | } 29 | 30 | export const Container = ({ children, ...props }) => ( 31 |
    32 | {children} 33 |
    34 | ) 35 | 36 | Container.propTypes = { 37 | children: PropTypes.node, 38 | className: PropTypes.any 39 | } 40 | 41 | export const NarrowContainer = ({ children, ...props }) => ( 42 |
    43 | {children} 44 |
    45 | ) 46 | 47 | NarrowContainer.propTypes = { 48 | children: PropTypes.node 49 | } 50 | -------------------------------------------------------------------------------- /src/components/IconButton/docs.md: -------------------------------------------------------------------------------- 1 | ## IconButton 2 | 3 | A clickable component that accepts icons and lables and renders them. If href is provided, the component renders an `` element, else it renders a `
    `} /> 10 | ``` 11 | -------------------------------------------------------------------------------- /src/components/InfoBox/ListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifRegular15, sansSerifRegular18 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { textAttributes } from './InfoBox' 7 | import { convertStyleToRem } from '../Typography/utils' 8 | import { useColorContext } from '../Colors/useColorContext' 9 | 10 | const WIDTH = 22 11 | 12 | const styles = { 13 | li: css({ 14 | paddingLeft: `${WIDTH}px`, 15 | position: 'relative', 16 | ...convertStyleToRem(sansSerifRegular15), 17 | [mUp]: { 18 | ...convertStyleToRem(sansSerifRegular18) 19 | }, 20 | '& p:last-child': { 21 | marginBottom: 0 22 | } 23 | }) 24 | } 25 | 26 | const ListItem = ({ children, attributes = {}, style = {} }) => { 27 | const [colorScheme] = useColorContext() 28 | return ( 29 |
  • 36 | {children} 37 |
  • 38 | ) 39 | } 40 | 41 | ListItem.propTypes = { 42 | children: PropTypes.node.isRequired, 43 | attributes: PropTypes.object, 44 | style: PropTypes.object 45 | } 46 | 47 | export default ListItem 48 | -------------------------------------------------------------------------------- /src/components/InfoBox/Subhead.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifMedium15, sansSerifMedium18 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { textAttributes } from './InfoBox' 7 | import { convertStyleToRem } from '../Typography/utils' 8 | import { useColorContext } from '../Colors/useColorContext' 9 | 10 | const styles = { 11 | text: css({ 12 | ...convertStyleToRem(sansSerifMedium15), 13 | marginBottom: '-12px', 14 | [mUp]: { 15 | ...convertStyleToRem(sansSerifMedium18), 16 | marginBottom: '-14px' 17 | } 18 | }) 19 | } 20 | 21 | export const Subhead = ({ children, attributes }) => { 22 | const [colorScheme] = useColorContext() 23 | return ( 24 |

    30 | {children} 31 |

    32 | ) 33 | } 34 | 35 | Subhead.propTypes = { 36 | children: PropTypes.node.isRequired, 37 | attributes: PropTypes.object 38 | } 39 | 40 | export default Subhead 41 | -------------------------------------------------------------------------------- /src/components/InfoBox/Text.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { fontRule } from '../Typography/Interaction' 4 | import { sansSerifRegular15, sansSerifRegular18 } from '../Typography/styles' 5 | import { css } from 'glamor' 6 | import { mUp } from '../../theme/mediaQueries' 7 | import { textAttributes } from './InfoBox' 8 | import { convertStyleToRem, pxToRem } from '../Typography/utils' 9 | import { useColorContext } from '../Colors/useColorContext' 10 | 11 | const styles = { 12 | text: css({ 13 | ...convertStyleToRem(sansSerifRegular15), 14 | lineHeight: pxToRem('24px'), 15 | [mUp]: { 16 | ...convertStyleToRem(sansSerifRegular18) 17 | }, 18 | ':nth-of-type(2)': { 19 | marginTop: 0 20 | } 21 | }) 22 | } 23 | 24 | export const Text = ({ children, attributes }) => { 25 | const [colorScheme] = useColorContext() 26 | return ( 27 |

    34 | {children} 35 |

    36 | ) 37 | } 38 | 39 | Text.propTypes = { 40 | children: PropTypes.node.isRequired, 41 | attributes: PropTypes.object 42 | } 43 | 44 | export default Text 45 | -------------------------------------------------------------------------------- /src/components/InfoBox/Title.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifMedium16, sansSerifMedium19 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { textAttributes } from './InfoBox' 7 | import { convertStyleToRem } from '../Typography/utils' 8 | import { useColorContext } from '../Colors/useColorContext' 9 | 10 | const styles = { 11 | text: css({ 12 | margin: '0 0 8px 0', 13 | borderTopWidth: 1, 14 | borderTopStyle: 'solid', 15 | ...convertStyleToRem(sansSerifMedium16), 16 | [mUp]: { 17 | ...convertStyleToRem(sansSerifMedium19), 18 | margin: '0 0 12px 0' 19 | } 20 | }) 21 | } 22 | 23 | export const Title = ({ children, attributes }) => { 24 | const [colorScheme] = useColorContext() 25 | return ( 26 |

    33 | {children} 34 |

    35 | ) 36 | } 37 | 38 | Title.propTypes = { 39 | children: PropTypes.node.isRequired, 40 | attributes: PropTypes.object 41 | } 42 | 43 | export default Title 44 | -------------------------------------------------------------------------------- /src/components/InfoBox/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | default as InfoBox, 3 | IMAGE_SIZES as INFOBOX_IMAGE_SIZES, 4 | DEFAULT_IMAGE_SIZE as INFOBOX_DEFAULT_IMAGE_SIZE 5 | } from './InfoBox' 6 | export { default as InfoBoxText } from './Text' 7 | export { default as InfoBoxTitle } from './Title' 8 | export { default as InfoBoxListItem } from './ListItem' 9 | export { default as InfoBoxSubhead } from './Subhead' 10 | -------------------------------------------------------------------------------- /src/components/List/index.js: -------------------------------------------------------------------------------- 1 | export { List, UnorderedList, OrderedList, ListItem } from './List' 2 | -------------------------------------------------------------------------------- /src/components/Logo/docs.md: -------------------------------------------------------------------------------- 1 | ```react 2 | 3 | ``` 4 | 5 | The logo is responsive by default and fills the full width. 6 | 7 | ## Inverted 8 | 9 | ```react|dark 10 | 11 | ``` 12 | 13 | ## Explicit Sizes 14 | 15 | ```react 16 | 17 | ``` 18 | 19 | ```react 20 | 21 | ``` 22 | 23 | ## Brand Mark 24 | 25 | The responsive brand mark. 26 | 27 | ```react 28 |
    29 | 30 |
    31 | ``` 32 | 33 | ```react 34 | 35 | ``` -------------------------------------------------------------------------------- /src/components/Overlay/OverlayBody.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { mUp } from '../../theme/mediaQueries' 4 | import { height } from './OverlayToolbar' 5 | 6 | const PADDING = 20 7 | 8 | const overlayBodyStyle = css({ 9 | padding: `${height + PADDING}px ${PADDING}px`, 10 | // The iPhone X Space 11 | // - thank you Apple for overlaying websites 12 | paddingBottom: 120, 13 | [mUp]: { 14 | padding: `${height + PADDING}px ${PADDING}px`, 15 | paddingBottom: PADDING 16 | } 17 | }) 18 | 19 | const OverlayBody = props =>
    20 | 21 | OverlayBody.PADDING = PADDING 22 | 23 | export default OverlayBody 24 | -------------------------------------------------------------------------------- /src/components/Overlay/docs.imports.js: -------------------------------------------------------------------------------- 1 | export { default as Button } from '../Button' 2 | export { default as Overlay, OverlayRenderer } from './Overlay' 3 | export { OverlayToolbar } from './OverlayToolbar' 4 | export { default as OverlayBody } from './OverlayBody' 5 | export { default as Field } from '../Form/Field' 6 | export { default as Checkbox } from '../Form/Checkbox' 7 | export { Interaction } from '../Typography' 8 | -------------------------------------------------------------------------------- /src/components/Overlay/index.js: -------------------------------------------------------------------------------- 1 | export { default as Overlay } from './Overlay' 2 | export { OverlayToolbar } from './OverlayToolbar' 3 | export { default as OverlayBody } from './OverlayBody' 4 | -------------------------------------------------------------------------------- /src/components/Progress/index.js: -------------------------------------------------------------------------------- 1 | export { default as ProgressCircle } from './Circle' 2 | -------------------------------------------------------------------------------- /src/components/PullQuote/Source.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifRegular14, sansSerifRegular15 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | 8 | const styles = { 9 | cite: css({ 10 | display: 'block', 11 | ...convertStyleToRem(sansSerifRegular14), 12 | marginTop: '18px', 13 | [mUp]: { 14 | ...convertStyleToRem(sansSerifRegular15), 15 | marginTop: '21px' 16 | }, 17 | fontStyle: 'normal' 18 | }) 19 | } 20 | 21 | export const Source = ({ children, attributes }) => { 22 | return ( 23 | 24 | {children} 25 | 26 | ) 27 | } 28 | 29 | Source.propTypes = { 30 | children: PropTypes.node.isRequired, 31 | attributes: PropTypes.object 32 | } 33 | 34 | export default Source 35 | -------------------------------------------------------------------------------- /src/components/PullQuote/Text.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { serifBold24, serifBold28, serifBold42 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | import { useColorContext } from '../Colors/useColorContext' 8 | 9 | const baseStyle = { 10 | ...convertStyleToRem(serifBold24) 11 | } 12 | 13 | const styles = { 14 | default: css({ 15 | ...baseStyle, 16 | [mUp]: { 17 | ...convertStyleToRem(serifBold28) 18 | } 19 | }), 20 | large: css({ 21 | ...baseStyle, 22 | [mUp]: { 23 | ...convertStyleToRem(serifBold42) 24 | } 25 | }) 26 | } 27 | 28 | export const Text = ({ children, attributes, size }) => { 29 | const [colorScheme] = useColorContext() 30 | return ( 31 |
    36 | {children} 37 |
    38 | ) 39 | } 40 | 41 | Text.propTypes = { 42 | children: PropTypes.node.isRequired, 43 | attributes: PropTypes.object, 44 | size: PropTypes.oneOf(['default', 'large']) 45 | } 46 | 47 | Text.defaultProps = { 48 | size: 'default' 49 | } 50 | 51 | export default Text 52 | -------------------------------------------------------------------------------- /src/components/PullQuote/index.js: -------------------------------------------------------------------------------- 1 | export { 2 | default as PullQuote, 3 | IMAGE_SIZE as PULLQUOTE_IMAGE_SIZE 4 | } from './PullQuote' 5 | export { default as PullQuoteSource } from './Source' 6 | export { default as PullQuoteText } from './Text' 7 | -------------------------------------------------------------------------------- /src/components/RawHtml/docs.md: -------------------------------------------------------------------------------- 1 | The `RawHtml` component allows to (dangerously) render raw HTML with styleguide-compliant typography. Typical use cases are links and `
    ` tags inside translation strings. 2 | 3 | By default the HTML is rendered inside a `` element. The `type` property can be either a tag name string (such as 'div' or 'h1'), or a React component type. 4 | 5 | ```react|span-6 6 | Bold und
    Link' 9 | }} 10 | /> 11 | ``` 12 | 13 | ```react|span-6 14 | Bold und Link' 18 | }} 19 | /> 20 | ``` 21 | 22 | ```react|span-6 23 | Bold und Link' 27 | }} 28 | /> 29 | ``` 30 | 31 | ```react|span-6 32 | Link' 37 | }} 38 | /> 39 | ``` 40 | -------------------------------------------------------------------------------- /src/components/RawHtml/index.js: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from 'react' 2 | import { css } from 'glamor' 3 | import PropTypes from 'prop-types' 4 | import { useColorContext } from '../Colors/useColorContext' 5 | import { underline } from '../../lib/styleMixins' 6 | 7 | const styles = { 8 | default: css({ 9 | '& ul, & ol': { 10 | overflow: 'hidden' 11 | } 12 | }) 13 | } 14 | 15 | const RawHtml = ({ type: Type, dangerouslySetInnerHTML, error }) => { 16 | const [colorScheme] = useColorContext() 17 | const colorRule = useMemo( 18 | () => 19 | css({ 20 | color: colorScheme.getCSSColor(error ? 'error' : 'text'), 21 | '& a': { 22 | ...underline, 23 | color: colorScheme.getCSSColor(error ? 'error' : 'text'), 24 | '@media (hover)': { 25 | ':hover': { 26 | color: colorScheme.getCSSColor(error ? 'error' : 'textSoft') 27 | } 28 | } 29 | } 30 | }), 31 | [colorScheme, error] 32 | ) 33 | 34 | return ( 35 | 40 | ) 41 | } 42 | 43 | RawHtml.defaultProps = { 44 | type: 'span' 45 | } 46 | 47 | RawHtml.propTypes = { 48 | type: PropTypes.oneOfType([PropTypes.string, PropTypes.func]) 49 | } 50 | 51 | export default RawHtml 52 | -------------------------------------------------------------------------------- /src/components/SeriesNav/__docs__/index.js: -------------------------------------------------------------------------------- 1 | export * from './seriesNavExample' 2 | -------------------------------------------------------------------------------- /src/components/SeriesNav/docs.md: -------------------------------------------------------------------------------- 1 | ```react 2 | ( 6 | 7 | 8 | 9 | } 11 | label="23%" 12 | labelShort="23%" 13 | /> 14 | 15 | 16 | ) 17 | } 18 | PayNote={TestPayNote} 19 | seriesDescription={true} 20 | t={t} 21 | /> 22 | ``` 23 | 24 | ## Inline 25 | 26 | ```react 27 | 34 | ``` 35 | -------------------------------------------------------------------------------- /src/components/SeriesNav/index.js: -------------------------------------------------------------------------------- 1 | export { default as SeriesNav } from './SeriesNav' 2 | -------------------------------------------------------------------------------- /src/components/ShareImage/SharePreviewFacebook.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | 4 | export const FACEBOOK_CARD_PREVIEW_WIDTH = 590 5 | 6 | const styles = { 7 | container: css({ 8 | backgroundColor: 'rgb(240, 242, 245)', 9 | color: 'rgb(28, 30, 33)', 10 | width: FACEBOOK_CARD_PREVIEW_WIDTH, 11 | padding: '10px 12px', 12 | maxHeight: 120, 13 | overflow: 'hidden', 14 | fontFamily: 15 | 'system-ui, -apple-system, system-ui, ".SFNSText-Regular", sans-serif' 16 | }), 17 | title: css({ 18 | fontSize: 16, 19 | fontWeight: 600, 20 | lineHeight: '1.17em', 21 | maxHeight: 110, 22 | overflow: 'hidden', 23 | marginBottom: 3, 24 | wordWrap: 'break-word', 25 | color: 'rgb(5, 5, 5)' 26 | }), 27 | description: css({ 28 | fontFamily: 'sans-serif', 29 | fontSize: 14, 30 | lineHeight: '1.33em', 31 | textOverflow: 'ellipsis', 32 | overflow: 'hidden', 33 | whiteSpace: 'nowrap', 34 | color: 'rgb(101, 103, 107)' 35 | }), 36 | domain: css({ 37 | fontFamily: 'sans-serif', 38 | fontSize: 12, 39 | lineHeight: '1.23em', 40 | textTransform: 'uppercase', 41 | color: 'rgb(101, 103, 107)', 42 | paddingBottom: 4 43 | }) 44 | } 45 | 46 | const SharePreviewFacebook = ({ title, description }) => ( 47 |
    48 |
    republik.ch
    49 |
    {title}
    50 |
    {description}
    51 |
    52 | ) 53 | 54 | export default SharePreviewFacebook 55 | -------------------------------------------------------------------------------- /src/components/Social/index.js: -------------------------------------------------------------------------------- 1 | export { default as Tweet } from './Tweet' 2 | -------------------------------------------------------------------------------- /src/components/Spinner/docs.md: -------------------------------------------------------------------------------- 1 | ## Spinner 2 | 3 | The `Spinner` has `position:absolute` at 50% / 50%. It is therefore taken out of the page flow, so make sure to put an appropriately-sized container around it. 4 | 5 | The only props the spinner accepts is `size` – the width/height of the spinner. 6 | 7 | ```react|span-3 8 |
    9 | 10 |
    11 | ``` 12 | ```react|span-3 13 |
    14 | 15 |
    16 | ``` 17 | 18 | ## InlineSpinner 19 | 20 | The `InlineSpinner` wraps a `Spinner` in a correctly-sized `inline-block` element. 21 | 22 | It accepts the same props as `Spinner`. 23 | 24 | ```react|span-3 25 | 26 | ``` 27 | 28 | ```react|span-3 29 | 30 | ``` 31 | -------------------------------------------------------------------------------- /src/components/Spinner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | 4 | const containerStyle = css({ 5 | display: 'block', 6 | position: 'absolute', 7 | top: '50%', 8 | left: '50%' 9 | }) 10 | const spin = css.keyframes({ 11 | '0%': { opacity: 1 }, 12 | '100%': { opacity: 0.15 } 13 | }) 14 | const barStyle = css({ 15 | display: 'block', 16 | animation: `${spin} 1.2s linear infinite`, 17 | borderRadius: 5, 18 | backgroundColor: '#999', 19 | position: 'absolute', 20 | width: '20%', 21 | height: '7.8%', 22 | top: '-3.9%', 23 | left: '-10%' 24 | }) 25 | 26 | export const Spinner = ({ size }) => { 27 | let bars = [] 28 | for (let i = 0; i < 12; i++) { 29 | let style = {} 30 | style.WebkitAnimationDelay = style.animationDelay = (i - 12) / 10 + 's' 31 | style.WebkitTransform = style.transform = 32 | 'rotate(' + i * 30 + 'deg) translate(146%)' 33 | 34 | bars.push() 35 | } 36 | 37 | return ( 38 | 39 | {bars} 40 | 41 | ) 42 | } 43 | 44 | const inlineBlock = css({ 45 | position: 'relative', 46 | display: 'inline-block' 47 | }) 48 | 49 | export const InlineSpinner = ({ size }) => ( 50 | 51 | 52 | 53 | ) 54 | 55 | Spinner.defaultProps = InlineSpinner.defaultProps = { 56 | size: 50 57 | } 58 | 59 | export default Spinner 60 | -------------------------------------------------------------------------------- /src/components/TeaserActiveDebates/DebateHeader.js: -------------------------------------------------------------------------------- 1 | import { css } from 'glamor' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | import { IconLink } from '../Discussion/Internal/Comment' 5 | import { sansSerifMedium16 } from '../Typography/styles' 6 | import { useColorContext } from '../Colors/ColorContext' 7 | 8 | const styles = { 9 | header: css({ 10 | display: 'flex', 11 | justifyContent: 'space-between', 12 | alignItems: 'center' 13 | }), 14 | title: css({ 15 | ...sansSerifMedium16, 16 | marginRight: 10, 17 | textDecoration: 'none' 18 | }) 19 | } 20 | 21 | const DebateHeader = React.forwardRef( 22 | ({ title, commentCount, href, onClick }, ref) => { 23 | const [colorScheme] = useColorContext() 24 | return ( 25 |
    26 | {title && ( 27 | 33 | {title} 34 | 35 | )} 36 | 42 |
    43 | ) 44 | } 45 | ) 46 | 47 | export default DebateHeader 48 | 49 | DebateHeader.propTypes = { 50 | title: PropTypes.string, 51 | commentCount: PropTypes.number, 52 | href: PropTypes.string 53 | } 54 | -------------------------------------------------------------------------------- /src/components/TeaserActiveDebates/__docs__/index.js: -------------------------------------------------------------------------------- 1 | export * from './activeDebatesExample' 2 | -------------------------------------------------------------------------------- /src/components/TeaserActiveDebates/index.js: -------------------------------------------------------------------------------- 1 | export { default as TeaserActiveDebates } from './ActiveDebates' 2 | export { default as ActiveDebateHeader } from './DebateHeader' 3 | export { default as ActiveDebateTeaser } from './DebateTeaser' 4 | export { default as ActiveDebateComment } from './DebateComment' 5 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/Context.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const defaultValue = { 4 | bgColor: 'default', 5 | color: 'text' 6 | } 7 | 8 | const CarouselContext = React.createContext(defaultValue) 9 | 10 | export default CarouselContext 11 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/Format.js: -------------------------------------------------------------------------------- 1 | import { css } from 'glamor' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | import { sansSerifMedium14 } from '../Typography/styles' 5 | import { useColorContext } from '../Colors/useColorContext' 6 | import CarouselContext, { defaultValue } from './Context' 7 | 8 | const styles = css({ 9 | ...sansSerifMedium14, 10 | margin: '0 0 10px 0' 11 | }) 12 | 13 | const Format = ({ children, color }) => { 14 | const context = React.useContext(CarouselContext) 15 | const mapping = context.color === defaultValue.color ? 'format' : undefined 16 | const textColor = color || context.color 17 | const [colorScheme] = useColorContext() 18 | return ( 19 |
    20 | {children} 21 |
    22 | ) 23 | } 24 | 25 | export default Format 26 | 27 | Format.propTypes = { 28 | children: PropTypes.node, 29 | color: PropTypes.string 30 | } 31 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/Grid.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | 4 | import { TILE_GRID_PADDING } from './constants' 5 | 6 | const styles = { 7 | container: css({ 8 | display: 'flex', 9 | flexWrap: 'wrap', 10 | marginLeft: -TILE_GRID_PADDING, 11 | marginRight: -TILE_GRID_PADDING, 12 | width: `calc(100% + ${TILE_GRID_PADDING * 2}px)` 13 | }) 14 | } 15 | 16 | const Grid = ({ children }) => { 17 | return ( 18 |
    19 | {children} 20 |
    21 | ) 22 | } 23 | 24 | export default Grid 25 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/Lead.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | import React from 'react' 3 | 4 | const Lead = ({ children }) => { 5 | return {children} 6 | } 7 | 8 | Lead.propTypes = { 9 | children: PropTypes.node 10 | } 11 | 12 | export default Lead 13 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/Subject.js: -------------------------------------------------------------------------------- 1 | import { css } from 'glamor' 2 | import PropTypes from 'prop-types' 3 | import React from 'react' 4 | import { mUp } from '../TeaserFront/mediaQueries' 5 | import { sansSerifRegular16, sansSerifRegular18 } from '../Typography/styles' 6 | import { useColorContext } from '../Colors/useColorContext' 7 | 8 | const styles = css({ 9 | ...sansSerifRegular16, 10 | lineHeight: '22px', 11 | 12 | [mUp]: { 13 | ...sansSerifRegular18, 14 | lineHeight: '24px' 15 | } 16 | }) 17 | 18 | const Subject = ({ children }) => { 19 | const [colorScheme] = useColorContext() 20 | const customStyles = css(styles, { 21 | '&::after': { 22 | content: children.length ? ' ' : undefined 23 | } 24 | }) 25 | return ( 26 | 27 | {children} 28 | 29 | ) 30 | } 31 | 32 | export default Subject 33 | 34 | Subject.propTypes = { 35 | children: PropTypes.node 36 | } 37 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/TileContainer.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | import CarouselContext from './Context' 4 | import Grid from './Grid' 5 | import Row from './Row' 6 | 7 | const Container = ({ initialScrollTileIndex, children, isSeriesNav }) => { 8 | const context = useContext(CarouselContext) 9 | if (context.grid) { 10 | return {children} 11 | } 12 | return ( 13 | 17 | {children} 18 | 19 | ) 20 | } 21 | 22 | export default Container 23 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/constants.js: -------------------------------------------------------------------------------- 1 | export const PADDING = 15 2 | export const TILE_MAX_WIDTH = 400 3 | export const TILE_MARGIN_RIGHT = 7 4 | export const TILE_GRID_PADDING = 7 5 | -------------------------------------------------------------------------------- /src/components/TeaserCarousel/index.js: -------------------------------------------------------------------------------- 1 | import * as _TeaserCarouselHeadline from './Headline' 2 | 3 | export const TeaserCarouselHeadline = { ..._TeaserCarouselHeadline } 4 | 5 | export { default as TeaserCarousel } from './Carousel' 6 | export { default as TeaserCarouselTile } from './Tile' 7 | export { default as TeaserCarouselTileContainer } from './TileContainer' 8 | export { default as TeaserCarouselSubject } from './Subject' 9 | export { default as TeaserCarouselLead } from './Lead' 10 | export { default as TeaserCarouselFormat } from './Format' 11 | export { default as TeaserCarouselArticleCount } from './ArticleCount' 12 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/Credit.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp } from '../../theme/mediaQueries' 5 | import { sansSerifRegular14, sansSerifRegular15 } from '../Typography/styles' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | import { useColorContext } from '../Colors/useColorContext' 8 | 9 | const styles = { 10 | main: css({ 11 | margin: 0, 12 | ...convertStyleToRem(sansSerifRegular14), 13 | [mUp]: { 14 | ...convertStyleToRem(sansSerifRegular15) 15 | } 16 | }) 17 | } 18 | 19 | const Credit = ({ children }) => { 20 | const [colorScheme] = useColorContext() 21 | return ( 22 |

    23 | {children} 24 |

    25 | ) 26 | } 27 | 28 | Credit.propTypes = { 29 | children: PropTypes.node.isRequired 30 | } 31 | 32 | export default Credit 33 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/Format.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { sansSerifMedium14, sansSerifMedium16 } from '../Typography/styles' 4 | import { css } from 'glamor' 5 | import { mUp } from '../../theme/mediaQueries' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | import { useColorContext } from '../Colors/useColorContext' 8 | 9 | const styles = { 10 | main: css({ 11 | ...convertStyleToRem(sansSerifMedium14), 12 | margin: '0 0 6px 0', 13 | [mUp]: { 14 | ...convertStyleToRem(sansSerifMedium16), 15 | margin: '-5px 0 8px 0' 16 | } 17 | }) 18 | } 19 | 20 | export const Format = ({ children, color }) => { 21 | const [colorScheme] = useColorContext() 22 | 23 | return ( 24 |

    28 | {children} 29 |

    30 | ) 31 | } 32 | 33 | Format.propTypes = { 34 | children: PropTypes.node.isRequired, 35 | color: PropTypes.string 36 | } 37 | 38 | export default Format 39 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/Highlight.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp } from '../../theme/mediaQueries' 5 | import { convertStyleToRem } from '../Typography/utils' 6 | import { 7 | sansSerifMedium14, 8 | sansSerifMedium16, 9 | sansSerifRegular16, 10 | sansSerifRegular18 11 | } from '../Typography/styles' 12 | import { useColorContext } from '../Colors/useColorContext' 13 | 14 | const styles = { 15 | main: css({ 16 | ...convertStyleToRem(sansSerifRegular16), 17 | margin: '10px 0 5px 0', 18 | [mUp]: { 19 | ...convertStyleToRem(sansSerifRegular18) 20 | } 21 | }), 22 | label: css({ 23 | ...convertStyleToRem(sansSerifMedium14), 24 | [mUp]: { 25 | ...convertStyleToRem(sansSerifMedium16) 26 | } 27 | }) 28 | } 29 | 30 | const Highlight = ({ children, label }) => { 31 | const [colorScheme] = useColorContext() 32 | return ( 33 |

    34 | {!!label && ( 35 | 36 | {label}: 37 | 38 | )}{' '} 39 | {children} 40 |

    41 | ) 42 | } 43 | 44 | Highlight.propTypes = { 45 | children: PropTypes.node.isRequired 46 | } 47 | 48 | export default Highlight 49 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/InternalOnlyTag.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import colors from '../../theme/colors' 4 | import { mUp } from '../../theme/mediaQueries' 5 | import { sansSerifRegular12, sansSerifRegular13 } from '../Typography/styles' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | 8 | const styles = { 9 | label: css({ 10 | ...convertStyleToRem(sansSerifRegular12), 11 | color: colors.disabled, 12 | position: 'absolute', 13 | top: '-20px', 14 | right: 0, 15 | [mUp]: { 16 | ...convertStyleToRem(sansSerifRegular13) 17 | } 18 | }) 19 | } 20 | 21 | const InternalOnlyTag = ({ t }) => { 22 | return ( 23 |
    24 | {t ? t('styleguide/TeaserFeed/InternalOnlyTag') : 'Internal'} 25 |
    26 | ) 27 | } 28 | 29 | export default InternalOnlyTag 30 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/Lead.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp } from '../../theme/mediaQueries' 5 | import { serifRegular17, serifRegular19 } from '../Typography/styles' 6 | import { convertStyleToRem } from '../Typography/utils' 7 | 8 | const styles = { 9 | main: css({ 10 | ...convertStyleToRem(serifRegular17), 11 | margin: '0 0 5px 0', 12 | [mUp]: { 13 | ...convertStyleToRem(serifRegular19) 14 | } 15 | }) 16 | } 17 | 18 | const Lead = ({ children }) => { 19 | return

    {children}

    20 | } 21 | 22 | Lead.propTypes = { 23 | children: PropTypes.node.isRequired 24 | } 25 | 26 | export default Lead 27 | -------------------------------------------------------------------------------- /src/components/TeaserFeed/utils.js: -------------------------------------------------------------------------------- 1 | import colors from '../../theme/colors' 2 | 3 | export const getFormatLine = ({ title, format, series, repoId, path }) => { 4 | if (format?.meta) { 5 | return { 6 | title: format.meta.title, 7 | color: format.meta.color || colors[format.meta.kind], 8 | path: format.meta.path 9 | } 10 | } 11 | // if (series) { 12 | // const currentEpisode = series.episodes.find( 13 | // episode => 14 | // (path && path === episode.document?.meta.path) || 15 | // (repoId && repoId === episode.document?.repoId) 16 | // ) 17 | // const starterEpisode = series.episodes.find(episode => 18 | // episode.label?.match(/Auftakt/i) 19 | // ) 20 | 21 | // if (currentEpisode && currentEpisode === starterEpisode) { 22 | // return {} 23 | // } 24 | // // back off if title already contain series title to avoid doubling 25 | // if ( 26 | // typeof title === 'string' && 27 | // title.toLowerCase().indexOf(series.title.toLowerCase()) !== -1 28 | // ) { 29 | // return {} 30 | // } 31 | 32 | // return { 33 | // title: currentEpisode?.label 34 | // ? `${series.title}${series.title.match(/\?$/) ? '' : ','} ${ 35 | // currentEpisode.label 36 | // }` 37 | // : series.title, 38 | // path: (starterEpisode || series.episodes[0])?.document?.meta.path, 39 | // logo: series.logo, 40 | // logoDark: series.logoDark 41 | // } 42 | // } 43 | return {} 44 | } 45 | -------------------------------------------------------------------------------- /src/components/TeaserFront/Credit.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Editorial } from '../Typography' 4 | 5 | const Credit = ({ children }) => { 6 | return ( 7 | {children} 8 | ) 9 | } 10 | 11 | Credit.propTypes = { 12 | children: PropTypes.node.isRequired 13 | } 14 | 15 | export default Credit 16 | -------------------------------------------------------------------------------- /src/components/TeaserFront/Format.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { lab } from 'd3-color' 5 | import { mUp, tUp } from './mediaQueries' 6 | import { sansSerifMedium16, sansSerifMedium20 } from '../Typography/styles' 7 | import { convertStyleToRem } from '../Typography/utils' 8 | 9 | const format = css({ 10 | ...convertStyleToRem(sansSerifMedium16), 11 | margin: '0 0 18px 0', 12 | [mUp]: { 13 | ...convertStyleToRem(sansSerifMedium20), 14 | margin: '0 0 28px 0' 15 | } 16 | }) 17 | 18 | const Format = ({ children, color, collapsedColor }) => { 19 | const labColor = lab(color) 20 | const labCollapsedColor = lab(collapsedColor || color) 21 | const mixColorStyle = 22 | collapsedColor && 23 | css({ 24 | color: 25 | labCollapsedColor.l > 50 26 | ? labCollapsedColor.darker(0.6) 27 | : labCollapsedColor.brighter(3.0), 28 | [tUp]: { 29 | color: labColor.l > 50 ? labColor.darker(2.0) : labColor.brighter(3.0) 30 | } 31 | }) 32 | 33 | return ( 34 |

    39 | {children} 40 |

    41 | ) 42 | } 43 | 44 | Format.propTypes = { 45 | children: PropTypes.node.isRequired, 46 | color: PropTypes.string, 47 | collapsedColor: PropTypes.string 48 | } 49 | 50 | export default Format 51 | -------------------------------------------------------------------------------- /src/components/TeaserFront/Lead.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import colors from '../../theme/colors' 4 | import { css } from 'glamor' 5 | import { mUp } from './mediaQueries' 6 | import { 7 | serifRegular18, 8 | serifRegular19, 9 | serifRegular23 10 | } from '../Typography/styles' 11 | import { convertStyleToRem, pxToRem } from '../Typography/utils' 12 | 13 | const leadStyle = { 14 | ...convertStyleToRem(serifRegular19), 15 | lineHeight: pxToRem('27px'), 16 | margin: '0 0 10px 0', 17 | color: colors.text 18 | } 19 | 20 | const lead = css({ 21 | ...leadStyle, 22 | [mUp]: { 23 | ...convertStyleToRem(serifRegular23), 24 | margin: '0 0 20px 0' 25 | } 26 | }) 27 | 28 | const leadSmall = css({ 29 | ...leadStyle, 30 | [mUp]: { 31 | ...convertStyleToRem(serifRegular18), 32 | margin: '0 0 20px 0' 33 | } 34 | }) 35 | 36 | const Lead = ({ children, columns, attributes }) => ( 37 | 42 | {children} 43 | 44 | ) 45 | 46 | Lead.propTypes = { 47 | children: PropTypes.node.isRequired, 48 | columns: PropTypes.number 49 | } 50 | 51 | export default Lead 52 | -------------------------------------------------------------------------------- /src/components/TeaserFront/Typo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { css } from 'glamor' 4 | import { mUp, tUp } from './mediaQueries' 5 | import Text from './Text' 6 | 7 | export const MAX_WIDTH_PERCENT = 70 8 | 9 | const styles = { 10 | root: css({ 11 | margin: 0 12 | }), 13 | textContainer: css({ 14 | margin: '0 auto', 15 | padding: '15px 15px 40px 15px', 16 | [mUp]: { 17 | maxWidth: `${MAX_WIDTH_PERCENT}%`, 18 | padding: '60px 0 80px 0' 19 | }, 20 | [tUp]: { 21 | padding: '80px 0 100px 0' 22 | } 23 | }) 24 | } 25 | 26 | const TypoBlock = ({ children, attributes, onClick, color, bgColor }) => { 27 | const background = bgColor || '' 28 | return ( 29 |
    38 |
    39 | 40 | {children} 41 | 42 |
    43 |
    44 | ) 45 | } 46 | 47 | TypoBlock.propTypes = { 48 | children: PropTypes.node.isRequired, 49 | attributes: PropTypes.object, 50 | color: PropTypes.string, 51 | bgColor: PropTypes.string 52 | } 53 | 54 | export default TypoBlock 55 | -------------------------------------------------------------------------------- /src/components/TeaserFront/index.js: -------------------------------------------------------------------------------- 1 | import * as _ImageHeadline from './ImageHeadline' 2 | import * as _SplitHeadline from './SplitHeadline' 3 | import * as _TypoHeadline from './TypoHeadline' 4 | import * as _TileHeadline from './TileHeadline' 5 | 6 | export const TeaserFrontImageHeadline = { ..._ImageHeadline } 7 | export const TeaserFrontSplitHeadline = { ..._SplitHeadline } 8 | export const TeaserFrontTypoHeadline = { ..._TypoHeadline } 9 | export const TeaserFrontTileHeadline = { ..._TileHeadline } 10 | 11 | export { default as TeaserFrontImage } from './Image' 12 | export { default as TeaserFrontTypo } from './Typo' 13 | export { default as TeaserFrontSplit } from './Split' 14 | export { default as TeaserFrontTileRow } from './TileRow' 15 | export { default as TeaserFrontTile } from './Tile' 16 | 17 | export { default as TeaserFrontFormat } from './Format' 18 | export { default as TeaserFrontLead } from './Lead' 19 | export { default as TeaserFrontSubject } from './Subject' 20 | export { default as TeaserFrontCredit } from './Credit' 21 | export { default as TeaserFrontCreditLink } from './CreditLink' 22 | export { default as TeaserFrontLogo } from './Logo' 23 | -------------------------------------------------------------------------------- /src/components/TeaserFront/mediaQueries.js: -------------------------------------------------------------------------------- 1 | // These media queries are specific to the front teasers. 2 | 3 | export const BreakPoints = { 4 | mobile: 640, 5 | tablet: 1174, 6 | desktop: 1400 7 | } 8 | 9 | export const mUp = `@media only screen and (min-width: ${BreakPoints.mobile}px)` 10 | export const tUp = `@media only screen and (min-width: ${BreakPoints.tablet}px)` 11 | export const dUp = `@media only screen and (min-width: ${BreakPoints.desktop}px)` 12 | -------------------------------------------------------------------------------- /src/components/TeaserMyMagazine/__docs__/index.js: -------------------------------------------------------------------------------- 1 | export * from './myMagazineExamples' 2 | -------------------------------------------------------------------------------- /src/components/TeaserMyMagazine/docs.md: -------------------------------------------------------------------------------- 1 | Supported props: 2 | 3 | - `latestSubscribedArticles` (array), required: 4 | - `latestProgressOrBookmarkedArticles` (array), required: 5 | - `ActionBar` (Component): 6 | 7 | ```react|responsive 8 | ( 17 | 18 | 19 | } 21 | label="23%" 22 | labelShort="23%" 23 | fill="black" 24 | /> 25 | 26 | ) 27 | } 28 | /> 29 | ``` 30 | -------------------------------------------------------------------------------- /src/components/TeaserMyMagazine/index.js: -------------------------------------------------------------------------------- 1 | export { default as TeaserMyMagazine } from './TeaserMyMagazine' 2 | -------------------------------------------------------------------------------- /src/components/TeaserShared/SectionTitle.js: -------------------------------------------------------------------------------- 1 | import { css } from 'glamor' 2 | import React from 'react' 3 | import { ChevronRightIcon } from '../Icons' 4 | import { mUp } from '../TeaserFront/mediaQueries' 5 | import { 6 | sansSerifMedium16, 7 | sansSerifMedium22, 8 | sansSerifRegular30 9 | } from '../Typography/styles' 10 | 11 | const styles = { 12 | container: css({ 13 | display: 'block', 14 | marginBottom: 15, 15 | ...sansSerifMedium22, 16 | textDecoration: 'none', 17 | '& svg': { 18 | width: 22, 19 | height: 22, 20 | marginLeft: 8 21 | }, 22 | [mUp]: { 23 | ...sansSerifRegular30, 24 | '& svg': { 25 | width: 30, 26 | height: 30 27 | }, 28 | marginBottom: 25 29 | }, 30 | color: 'inherit' 31 | }), 32 | small: css({ 33 | color: 'inherit', 34 | ...sansSerifMedium16, 35 | textDecoration: 'none', 36 | '& svg': { 37 | marginTop: -1, 38 | width: 16, 39 | height: 16, 40 | marginLeft: 4 41 | } 42 | }) 43 | } 44 | 45 | const SectionTitle = React.forwardRef( 46 | ({ children, small, onClick, href }, ref) => { 47 | const style = small ? styles.small : styles.container 48 | return href ? ( 49 | 50 | {children} 51 | {} 52 | 53 | ) : ( 54 | 55 | {children} 56 | 57 | ) 58 | } 59 | ) 60 | 61 | export default SectionTitle 62 | -------------------------------------------------------------------------------- /src/components/TeaserShared/docs.md: -------------------------------------------------------------------------------- 1 | ## ``. 2 | 3 | A section title. If `href` is defined, an icon with a chevron right will be displayed on the right of the section title. 4 | 5 | Supported props: 6 | - `onClick` (function): function triggered on click. 7 | - `href` (string): link. 8 | - `children`: children component (section title). 9 | 10 | ```react|span-3 11 | {}} 13 | > 14 | Alle Serien 15 | 16 | ``` 17 | 18 | ```react|span-3 19 | {}} 22 | > 23 | Aktive Debatten 24 | 25 | ``` 26 | 27 | ```react 28 | {}} 32 | > 33 | Alle Beiträge im Feed 34 | 35 | ``` -------------------------------------------------------------------------------- /src/components/TeaserShared/index.js: -------------------------------------------------------------------------------- 1 | export { default as TeaserSectionTitle } from './SectionTitle' 2 | -------------------------------------------------------------------------------- /src/components/TitleBlock/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { 4 | PADDED_MAX_WIDTH, 5 | PADDED_MAX_WIDTH_BREAKOUT, 6 | MAX_WIDTH, 7 | PADDING, 8 | BREAKOUT 9 | } from '../Center' 10 | import { css, merge } from 'glamor' 11 | import { mUp } from '../../theme/mediaQueries' 12 | 13 | const styles = { 14 | container: css({ 15 | maxWidth: PADDED_MAX_WIDTH, 16 | margin: '0 auto', 17 | paddingTop: 30, 18 | paddingLeft: PADDING, 19 | paddingRight: PADDING, 20 | [mUp]: { 21 | paddingTop: 40, 22 | margin: '0 auto' 23 | } 24 | }), 25 | containerMargin: css({ 26 | marginBottom: 40, 27 | [mUp]: { 28 | marginBottom: 70 29 | } 30 | }) 31 | } 32 | 33 | const TitleBlock = ({ children, attributes, center, margin, breakout }) => { 34 | return ( 35 |
    48 | {children} 49 |
    50 | ) 51 | } 52 | 53 | TitleBlock.propTypes = { 54 | children: PropTypes.node.isRequired, 55 | attributes: PropTypes.object, 56 | center: PropTypes.bool 57 | } 58 | 59 | export default TitleBlock 60 | -------------------------------------------------------------------------------- /src/components/Typography/Scribble.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import * as styles from './styles' 3 | import { css } from 'glamor' 4 | import { mUp } from '../../theme/mediaQueries' 5 | import { convertStyleToRem } from './utils' 6 | import { useColorContext } from '../Colors/useColorContext' 7 | 8 | export { 9 | List, 10 | UnorderedList as UL, 11 | OrderedList as OL, 12 | ListItem as LI 13 | } from '../List' 14 | 15 | const headline = css({ 16 | ...convertStyleToRem(styles.cursiveTitle30), 17 | margin: '0 0 12px 0', 18 | [mUp]: { 19 | ...convertStyleToRem(styles.cursiveTitle58), 20 | margin: '0 0 12px 0' 21 | }, 22 | ':first-child': { 23 | marginTop: 0 24 | }, 25 | ':last-child': { 26 | marginBottom: 0 27 | } 28 | }) 29 | 30 | export const Headline = ({ children, attributes, ...props }) => { 31 | const [colorScheme] = useColorContext() 32 | return ( 33 |

    39 | {children} 40 |

    41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Typography/fontRules.js: -------------------------------------------------------------------------------- 1 | import { css } from 'glamor' 2 | import { fontStyles } from '../../theme/fonts' 3 | import colors from '../../theme/colors' 4 | 5 | export const editorialFontRule = css({ 6 | ...fontStyles.serifRegular, 7 | '& em, & i': fontStyles.serifItalic, 8 | '& strong, & b': fontStyles.serifBold, 9 | '& strong em, & em strong, & b i, & i b': fontStyles.serifBoldItalic 10 | }) 11 | 12 | export const interactionFontRule = css({ 13 | ...fontStyles.sansSerifRegular, 14 | '& em, & i': fontStyles.sansSerifItalic, 15 | '& strong, & b': fontStyles.sansSerifMedium, 16 | '& strong em, & em strong, & b i, & i b': { 17 | textDecoration: `underline wavy ${colors.error}` 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /src/components/Typography/utils.js: -------------------------------------------------------------------------------- 1 | export const DEFAULT_FONT_SIZE = 16 2 | 3 | export const pxToRem = pxSize => { 4 | return pxSize && `${parseInt(pxSize, 10) / DEFAULT_FONT_SIZE}rem` 5 | } 6 | 7 | export const convertStyleToRem = cssRules => { 8 | return { 9 | ...cssRules, 10 | fontSize: pxToRem(cssRules.fontSize), 11 | lineHeight: pxToRem(cssRules.lineHeight) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Variables/index.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, Children } from 'react' 2 | 3 | export const VariableContext = React.createContext({}) 4 | 5 | export const Variable = ({ variable, fallback }) => { 6 | const vars = useContext(VariableContext) 7 | if (vars._mergeTags) { 8 | return fallback 9 | ? `*|IF:${vars[variable]}|* *|${vars[variable]}|* *|ELSE:|* ${fallback} *|END:IF|*` 10 | : `*|${vars[variable]}|*` 11 | } 12 | return vars[variable] || fallback || null 13 | } 14 | 15 | export const If = ({ present, children }) => { 16 | const vars = useContext(VariableContext) 17 | if (vars._mergeTags) { 18 | return ( 19 | <> 20 | {`*|IF:${vars[present] || present}|*`} 21 | {children} 22 | {'*|END:IF|*'} 23 | 24 | ) 25 | } 26 | 27 | const childs = Children.toArray(children) 28 | if (vars[present]) { 29 | return <>{childs.filter(child => child.type !== Else)} 30 | } 31 | return <>{childs.filter(child => child.type === Else)} 32 | } 33 | 34 | export const Else = ({ children }) => { 35 | const vars = useContext(VariableContext) 36 | return ( 37 | <> 38 | {vars._mergeTags && '*|ELSE:|*'} 39 | {children} 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Video/index.js: -------------------------------------------------------------------------------- 1 | export { default as Video } from './Video' 2 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/Icons/Fullscreen.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FullscreenIcon, FullscreenExitIcon } from '../../Icons' 3 | 4 | const Icon = ({ size, fill, off }) => { 5 | if (off) { 6 | return ( 7 | 13 | ) 14 | } else { 15 | return ( 16 | 22 | ) 23 | } 24 | } 25 | 26 | Icon.defaultProps = { 27 | size: 24, 28 | fill: '#fff', 29 | off: true 30 | } 31 | 32 | export default Icon 33 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/Icons/Play.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const ratio = 1.385 4 | 5 | const Play = ({ width, fill }) => ( 6 | 7 | 8 | 9 | ) 10 | 11 | Play.defaultProps = { 12 | width: 26, 13 | fill: '#fff' 14 | } 15 | 16 | export default Play 17 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/Icons/Rewind.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Rewind = ({ size, fill, disabled }) => ( 4 | 5 | 10 | 11 | ) 12 | 13 | Rewind.defaultProps = { 14 | size: 24, 15 | fill: '#fff', 16 | disabled: true 17 | } 18 | 19 | export default Rewind 20 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/Icons/Subtitles.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Icon = ({ size, fill, off }) => ( 4 | 5 | {off ? ( 6 | 7 | 12 | 19 | 20 | ) : ( 21 | 27 | )} 28 | 29 | ) 30 | 31 | Icon.defaultProps = { 32 | size: 24, 33 | fill: '#fff', 34 | off: false 35 | } 36 | 37 | export default Icon 38 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/Icons/Volume.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Icon = ({ size, fill, off }) => ( 4 | 5 | 13 | 14 | ) 15 | 16 | Icon.defaultProps = { 17 | size: 24, 18 | fill: '#fff', 19 | off: false 20 | } 21 | 22 | export default Icon 23 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/index.js: -------------------------------------------------------------------------------- 1 | export { default as VideoPlayer } from './Player' 2 | -------------------------------------------------------------------------------- /src/components/globalMediaState.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | playingRef: undefined, 3 | muted: false, // video only 4 | subtitles: false, // currently video only 5 | instances: [], 6 | setTime: time => { 7 | state.instances.forEach(setter => { 8 | setter.setTime && setter.setTime(time) 9 | }) 10 | } 11 | } 12 | 13 | export const parseTimeHash = hash => { 14 | const matches = hash && hash.match(/t=(\d+)/) 15 | const time = matches && +matches[1] 16 | if (time && time > -1) { 17 | return time 18 | } 19 | return false 20 | } 21 | 22 | if (typeof window !== 'undefined') { 23 | window.addEventListener('hashchange', () => { 24 | const time = parseTimeHash(window.location.hash) 25 | if (time !== false) { 26 | state.setTime(time) 27 | } 28 | }) 29 | } 30 | 31 | export default state 32 | -------------------------------------------------------------------------------- /src/development/process.docs.md: -------------------------------------------------------------------------------- 1 | ## Goals 2 | 3 | - empower the developers to be fast 4 | - document the available components 5 | - keep styles and interaction behaviour consistent 6 | - even between different code bases 7 | - enable division of labor 8 | - isolate the implementation and documentation of components 9 | - e.g. a new teaser can be implemented without knowing all the inner workings of the CMS 10 | 11 | ## General 12 | 13 | 1. All development should happen in branches and merged via a pull request. 14 | 2. Follow the [commit message format](/#commit-message-format) for proper versioning 15 | 16 | ## Adding a New Component 17 | 18 | ### Criteria for new components 19 | 20 | - Generically useable 21 | - Has been designed 22 | - Has been used in more than one place 23 | + Same app, two places can count too 24 | 25 | ### General flow 26 | 27 | 1. Start a new branch 28 | 2. Develop the component 29 | - Document the public API with/in a catalog page 30 | - Export in `src/lib.ts` 31 | 3. Create a pull request with screenshots 32 | 4. Review by another developer 33 | 5. Merge into master 34 | - Will be auto released 35 | -------------------------------------------------------------------------------- /src/editor.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default as ChartEditor 3 | } from './components/Chart/Editor' 4 | -------------------------------------------------------------------------------- /src/global.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; 3 | } 4 | * { 5 | -webkit-tap-highlight-color: transparent; 6 | } 7 | html { 8 | box-sizing: border-box; 9 | } -------------------------------------------------------------------------------- /src/lib/globalMediaState.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | playingRef: undefined, 3 | muted: false, // video only 4 | subtitles: false, // currently video only 5 | instances: [], 6 | setTime: time => { 7 | state.instances.forEach(setter => { 8 | setter.setTime && setter.setTime(time) 9 | }) 10 | } 11 | } 12 | 13 | export const parseTimeHash = hash => { 14 | const matches = hash && hash.match(/t=(\d+)/) 15 | const time = matches && +matches[1] 16 | if (time && time > -1) { 17 | return time 18 | } 19 | return false 20 | } 21 | 22 | if (typeof window !== 'undefined') { 23 | window.addEventListener('hashchange', () => { 24 | const time = parseTimeHash(window.location.hash) 25 | if (time !== false) { 26 | state.setTime(time) 27 | } 28 | }) 29 | } 30 | 31 | export default state 32 | -------------------------------------------------------------------------------- /src/lib/helpers.js: -------------------------------------------------------------------------------- 1 | export const intersperse = (list, separator) => { 2 | if (list.length === 0) { 3 | return [] 4 | } 5 | 6 | return list.slice(1).reduce( 7 | (items, item, i) => { 8 | return items.concat([separator(item, i), item]) 9 | }, 10 | [list[0]] 11 | ) 12 | } 13 | 14 | export const rafDebounce = fn => { 15 | let next 16 | 17 | return (...args) => { 18 | if (!next) { 19 | next = window.requestAnimationFrame(() => { 20 | next = undefined 21 | fn(...args) 22 | }) 23 | } 24 | return next 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/inQuotes.docs.md: -------------------------------------------------------------------------------- 1 | Utility function for wrapping a string in quotation marks and avoiding double quoting. Ignores already quoted strings of the same quotation marks pattern and replaces nested quotation marks. No support for third level nested quotation marks or transformations between different quotation mark patterns. 2 | 3 | ```code|lang-js 4 | import {inQuotes} from '@project-r/styleguide' 5 | 6 | const quotedTitle = inQuotes('My title') // '«My title»' 7 | ``` 8 | 9 | 10 | ```react 11 | {inQuotes('An der Bar')} 12 | ``` 13 | 14 | ```react 15 | {inQuotes('«An der Bar»')} 16 | ``` 17 | 18 | ```react 19 | {inQuotes('«An der Bar» mit Carla Del Ponte')} 20 | ``` 21 | 22 | ```react 23 | {inQuotes('An der «Republik-Bar» mit Carla Del Ponte')} 24 | ``` 25 | 26 | ```react 27 | {inQuotes('An der «Republik-Bar» und «zu Hause» mit Carla Del Ponte')} 28 | ``` 29 | 30 | ```react 31 | 32 | {inQuotes( 33 | 'An der „Republik-Bar“ mit Carla Del Ponte', 34 | { 35 | outerOpening: '„', 36 | outerClosing: '“', 37 | innerOpening: '‚', 38 | innerClosing: '‘' 39 | } 40 | )} 41 | 42 | ``` 43 | -------------------------------------------------------------------------------- /src/lib/inQuotes.js: -------------------------------------------------------------------------------- 1 | const defaultMarks = { 2 | outerOpening: '«', 3 | outerClosing: '»', 4 | innerOpening: '‹', 5 | innerClosing: '›' 6 | } 7 | 8 | export const inQuotes = (str, marks = defaultMarks) => { 9 | let quotedStr = str.trim() 10 | if ( 11 | quotedStr.startsWith(marks.outerOpening) && 12 | quotedStr.endsWith(marks.outerClosing) 13 | ) { 14 | return quotedStr 15 | } 16 | quotedStr = quotedStr 17 | .replace(new RegExp(marks.outerOpening, 'g'), marks.innerOpening) 18 | .replace(new RegExp(marks.outerClosing, 'g'), marks.innerClosing) 19 | return marks.outerOpening + quotedStr + marks.outerClosing 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/slug.docs.md: -------------------------------------------------------------------------------- 1 | Utility function for converting a string to a slug. 2 | 3 | ```code|lang-js 4 | import {slug} from '@project-r/styleguide' 5 | 6 | const nameSlug = slug('Johnny Weißmüller') // 'johnny-weissmueller' 7 | ``` 8 | -------------------------------------------------------------------------------- /src/lib/slug.js: -------------------------------------------------------------------------------- 1 | const diacritics = [ 2 | { base: 'a', letters: ['â', 'à'] }, 3 | { base: 'c', letters: ['ç'] }, 4 | { base: 'e', letters: ['é', 'ê', 'è', 'ë'] }, 5 | { base: 'i', letters: ['î', 'ï'] }, 6 | { base: 'o', letters: ['ô'] }, 7 | { base: 'u', letters: ['ù', 'û'] }, 8 | { base: 'ss', letters: ['ß'] }, 9 | { base: 'ae', letters: ['ä'] }, 10 | { base: 'ue', letters: ['ü'] }, 11 | { base: 'oe', letters: ['ö'] } 12 | ] 13 | 14 | const diacriticsMap = diacritics.reduce((map, diacritic) => { 15 | diacritic.letters.forEach(letter => { 16 | map[letter] = diacritic.base 17 | }) 18 | return map 19 | }, {}) 20 | 21 | export const slug = string => 22 | string 23 | .toLowerCase() 24 | // eslint-disable-next-line no-control-regex 25 | .replace(/[^\u0000-\u007E]/g, a => diacriticsMap[a] || a) 26 | .replace(/\u00ad/g, '') 27 | .replace(/[^0-9a-z]+/g, ' ') 28 | .trim() 29 | .replace(/\s+/g, '-') 30 | -------------------------------------------------------------------------------- /src/lib/slug.test.js: -------------------------------------------------------------------------------- 1 | import { slug } from './slug' 2 | 3 | describe('lib.utils.slug - test suite', function() { 4 | test('lower case and no spaces', function() { 5 | expect(slug('John Doe')).toEqual('john-doe') 6 | }) 7 | test('trim', function() { 8 | expect(slug(' John Doe ')).toEqual('john-doe') 9 | }) 10 | 11 | test('double space', function() { 12 | expect(slug('John Doe')).toEqual('john-doe') 13 | }) 14 | 15 | test('invalid chars', function() { 16 | expect(slug('@~John,.?-+=|/Doe!')).toEqual('john-doe') 17 | }) 18 | 19 | test('umlaut german', function() { 20 | expect(slug('äüöß')).toEqual('aeueoess') 21 | }) 22 | 23 | test('umlaut french', function() { 24 | expect(slug('âàçéêèëîïôùû')).toEqual('aaceeeeiiouu') 25 | }) 26 | 27 | test('soft hyphen', function() { 28 | expect(slug('um\u00adwelt\u00adschmerz')).toEqual('umweltschmerz') 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/lib/styleMixins.js: -------------------------------------------------------------------------------- 1 | export const ellipsize = { 2 | whiteSpace: 'nowrap', 3 | overflow: 'hidden', 4 | textOverflow: 'ellipsis' 5 | } 6 | 7 | export const underline = { 8 | textDecoration: 'underline', 9 | textDecorationSkip: 'ink' 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/textGauger.js: -------------------------------------------------------------------------------- 1 | import memoize from 'lodash/memoize' 2 | 3 | const measurementDiv = memoize( 4 | () => { 5 | const div = document.createElement('div') 6 | div.className = 'DOMMEASUREMENTOUTLET' 7 | div.style.position = 'fixed' 8 | div.style.top = '-100%' 9 | div.style.visibility = 'hidden' 10 | div.style.pointerEvents = 'none' 11 | document.body.appendChild(div) 12 | return div 13 | }, 14 | () => '' 15 | ) 16 | 17 | export const createTextGauger = memoize( 18 | ({ fontFamily, fontSize, lineHeight }, { dimension, html }) => { 19 | if (typeof document === 'undefined') { 20 | return text => { 21 | // SSR approximation 22 | return fontSize * 0.6 * String(text).length 23 | } 24 | } 25 | const element = document.createElement('span') 26 | element.style.fontFamily = fontFamily 27 | element.style.fontSize = `${fontSize}px` 28 | element.style.lineHeight = lineHeight 29 | measurementDiv().appendChild(element) 30 | if (html) { 31 | return memoize(text => { 32 | element.innerHTML = text 33 | return element.getBoundingClientRect()[dimension] 34 | }) 35 | } 36 | return memoize(text => { 37 | element.textContent = text 38 | return element.getBoundingClientRect()[dimension] 39 | }) 40 | }, 41 | ({ fontFamily, fontSize, lineHeight }, { dimension, html }) => 42 | [fontFamily, fontSize, lineHeight, dimension, html].join() 43 | ) 44 | -------------------------------------------------------------------------------- /src/lib/timeFormat.js: -------------------------------------------------------------------------------- 1 | import { timeFormatLocale } from 'd3-time-format' 2 | import timeDefinition from 'd3-time-format/locale/de-CH' 3 | 4 | const locale = timeFormatLocale(timeDefinition) 5 | 6 | export const timeFormat = locale.format 7 | export const timeParse = locale.parse 8 | -------------------------------------------------------------------------------- /src/lib/timeago.js: -------------------------------------------------------------------------------- 1 | const specs = [ 2 | { n: 60, key: 'timeago/justNow' }, 3 | { n: 60, key: 'timeago/minutes' }, 4 | { n: 24, key: 'timeago/hours' }, 5 | { n: 7, key: 'timeago/days' }, 6 | { n: 365 / 7 / 12, key: 'timeago/weeks' }, 7 | { n: 12, key: 'timeago/months', fn: 'round' }, 8 | { n: 1, key: 'timeago/years' } 9 | ] 10 | 11 | // diff is in seconds, positive. 12 | export default (t, diff) => { 13 | let i = 0 14 | for (; i < specs.length && diff >= specs[i].n; i++) { 15 | diff /= specs[i].n 16 | } 17 | 18 | const spec = specs[Math.min(i, specs.length - 1)] 19 | return t.pluralize(spec.key, { 20 | count: Math[spec.fn || 'floor'](diff) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/timeahead.js: -------------------------------------------------------------------------------- 1 | const specs = [ 2 | { n: 60, key: 'timeAhead/justNow' }, 3 | { n: 60, key: 'timeAhead/minutes' }, 4 | { n: 24, key: 'timeAhead/hours' }, 5 | { n: 7, key: 'timeAhead/days' }, 6 | { n: 365 / 7 / 12, key: 'timeAhead/weeks' }, 7 | { n: 12, key: 'timeAhead/months', fn: 'round' }, 8 | { n: 1, key: 'timeAhead/years' } 9 | ] 10 | 11 | // diff is in seconds. 12 | export default (t, diff) => { 13 | diff = Math.abs(diff) 14 | let i = 0 15 | for (; i < specs.length && diff >= specs[i].n; i++) { 16 | diff /= specs[i].n 17 | } 18 | 19 | const spec = specs[Math.min(i, specs.length - 1)] 20 | return t.pluralize(spec.key, { 21 | count: Math[spec.fn || 'floor'](diff) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/timeduration.js: -------------------------------------------------------------------------------- 1 | const specs = [ 2 | { n: 60, key: 'timeduration/seconds' }, 3 | { n: 60, key: 'timeduration/minutes' }, 4 | { n: 24, key: 'timeduration/hours' }, 5 | { n: 7, key: 'timeduration/days' }, 6 | { n: 365 / 7, key: 'timeduration/weeks' }, 7 | { n: 52, key: 'timeduration/years' } 8 | ] 9 | 10 | // diff is in seconds, positive. 11 | export default (t, diff) => { 12 | let i = 0 13 | for (; i < specs.length && diff >= specs[i].n; i++) { 14 | diff /= specs[i].n 15 | } 16 | 17 | const spec = specs[Math.min(i, specs.length - 1)] 18 | return t(spec.key, { 19 | count: Math[spec.fn || 'floor'](diff) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/useBoundingClientRect.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * const [ref, { width, height }] = useBoundingClientRect([text]) 5 | *
    {text}
    6 | * 7 | * Make sure to pass any dependencies to the hook which may change the bounding 8 | * client rect. Without the deps this hook will only measure the initial size. 9 | */ 10 | export const useBoundingClientRect = (deps = []) => { 11 | const [rect, setRect] = React.useState({}) 12 | 13 | return [ 14 | React.useCallback(el => { 15 | if (el) { 16 | setRect(el.getBoundingClientRect()) 17 | } 18 | }, deps), // eslint-disable-line react-hooks/exhaustive-deps 19 | rect 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/useCurrentMinute.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react' 2 | 3 | let setters = [] 4 | 5 | let timeout = null 6 | const startTimer = () => { 7 | const currentTime = new Date() 8 | const msLeft = 1000 - currentTime.getMilliseconds() 9 | const msToNextMinute = (60 - currentTime.getSeconds()) * 1000 + msLeft 10 | // ensure timer runs in new minute and with at least 5 seconds in between runs 11 | const msToNextRun = Math.max(msToNextMinute + 500, 5 * 1000) 12 | clearTimeout(timeout) 13 | timeout = setTimeout(() => { 14 | const now = Date.now() 15 | setters.forEach(setter => setter(now)) 16 | startTimer() 17 | }, msToNextRun) 18 | } 19 | 20 | const addSetter = setter => { 21 | setters.push(setter) 22 | if (timeout === null) { 23 | startTimer() 24 | } 25 | } 26 | const rmSetter = setter => { 27 | setters = setters.filter(s => s !== setter) 28 | if (!setter.length) { 29 | clearTimeout(timeout) 30 | timeout = null 31 | } 32 | } 33 | 34 | export const useCurrentMinute = () => { 35 | const [now, setNow] = useState(() => Date.now()) 36 | useEffect(() => { 37 | addSetter(setNow) 38 | return () => { 39 | rmSetter(setNow) 40 | } 41 | }, []) 42 | return now 43 | } 44 | -------------------------------------------------------------------------------- /src/lib/useDebounce.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const useDebounce = (fastValue, ms = 400) => { 4 | const [slowValue, setSlowValue] = React.useState(fastValue) 5 | 6 | React.useEffect(() => { 7 | if (slowValue === fastValue) { 8 | return 9 | } 10 | const timer = setTimeout(() => setSlowValue(fastValue), ms) 11 | return () => { 12 | clearTimeout(timer) 13 | } 14 | }, [slowValue, fastValue, ms]) 15 | 16 | return [slowValue] 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/useHeaderHeight.docs.md: -------------------------------------------------------------------------------- 1 | Utility hook passing the height of the header to its children by way of a context payload. Additionally, a list of media queries is provided to generate CSS rules. 2 | 3 | The hook requires a `HeaderHeightProvider` instance configured with a list of breakpoints (`minWidth`) and and height values (`headerHeight`) . 4 | 5 | Example using the `headerHeight` value programmatically: 6 | 7 | ```react 8 | const Child = () => { 9 | 10 | const [ headerHeight ] = useHeaderHeight() 11 | 12 | return ( 13 |
    14 | Lorem ipsum dolor sit amet. 15 |
    16 | ) 17 | } 18 | 19 | 20 | 21 | 22 | ``` 23 | 24 | Example using the `mediaQuery`/`value` pairs to generate CSS: 25 | 26 | ```react 27 | const Child = () => { 28 | 29 | const [ _, rules ] = useHeaderHeight() 30 | 31 | const style = css(rules.reduce((acc, { mediaQuery, headerHeight }) => 32 | Object.assign( 33 | acc, 34 | {[mediaQuery]: {paddingTop: headerHeight}} 35 | ), {}) 36 | ) 37 | 38 | return ( 39 |
    40 | Lorem ipsum dolor sit amet. 41 |
    42 | ) 43 | } 44 | 45 | 46 | 47 | 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /src/lib/useMediaQuery.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * const matches = useMediaQuery("(min-width: 700px)") 5 | * 6 | * @param {string} queryInput 7 | * @returns {boolean} 8 | */ 9 | export const useMediaQuery = queryInput => { 10 | /** 11 | * This is purely for convenience, so we can pass strings that start 12 | * with "@media ". 13 | */ 14 | const query = queryInput.replace(/^@media /, '') 15 | 16 | const [matches, setMatches] = React.useState(() => { 17 | if (typeof window !== 'undefined') { 18 | return window.matchMedia(query).matches 19 | } else { 20 | return undefined 21 | } 22 | }) 23 | 24 | React.useEffect(() => { 25 | const mql = window.matchMedia(query) 26 | setMatches(mql.matches) 27 | 28 | const onChange = ({ matches }) => { 29 | setMatches(matches) 30 | } 31 | mql.addListener(onChange) 32 | return () => { 33 | mql.removeListener(onChange) 34 | } 35 | }, [query, setMatches]) 36 | 37 | return matches 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/usePrevious.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * Gives you access to the previous value, used during the previous rendering of 5 | * the component. 6 | * 7 | * const MyComponent = ({ value }) => { 8 | * const previousValue = usePrevious(value) 9 | * return
    {value} - {previousValue}
    10 | * } 11 | */ 12 | export const usePrevious = value => { 13 | const ref = React.useRef() 14 | 15 | React.useEffect(() => { 16 | ref.current = value 17 | }, [value]) 18 | 19 | return ref.current 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/warn.js: -------------------------------------------------------------------------------- 1 | export default (...args) => { 2 | try { 3 | console.warn(...args) 4 | } catch (e) {} 5 | } 6 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/templates.ts: -------------------------------------------------------------------------------- 1 | // Export article-schema 2 | export { default as createArticleSchema } from './templates/Article' 3 | export { extractImages, matchImagesParagraph } from './templates/Article/utils' 4 | 5 | // Export comment-schema 6 | export { default as createCommentWebSchema } from './templates/Comment/web' 7 | export { default as createCommentEmailSchema } from './templates/Comment/email' 8 | 9 | // Export dossier-schema 10 | export { default as createDossierSchema } from './templates/Dossier' 11 | 12 | // Export discussion-schema 13 | export { default as createDiscussionSchema } from './templates/Discussion' 14 | 15 | // Export editorial-newsletter-schema 16 | export { default as createNewsletterWebSchema } from './templates/EditorialNewsletter/web' 17 | export { default as createNewsletterEmailSchema } from './templates/EditorialNewsletter/email' 18 | 19 | // Export format-schema 20 | export { default as createFormatSchema } from './templates/Format' 21 | 22 | // Export front-schema 23 | export { default as createFrontSchema } from './templates/Front' 24 | 25 | // Export page-schema 26 | export { default as createPageSchema } from './templates/Page' 27 | 28 | // Export section-schema 29 | export { default as createSectionSchema } from './templates/Section' 30 | 31 | // Export Teaser components 32 | export { TeaserActiveDebates } from './components/TeaserActiveDebates' 33 | export { TeaserMyMagazine } from './components/TeaserMyMagazine' 34 | -------------------------------------------------------------------------------- /src/templates/Article/Container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useColorContext } from '../../components/Colors/useColorContext' 3 | 4 | const ArticleContainer = ({ children }) => { 5 | const [colorScheme] = useColorContext() 6 | return ( 7 |
    {children}
    8 | ) 9 | } 10 | 11 | export default ArticleContainer 12 | -------------------------------------------------------------------------------- /src/templates/Article/email/docs.md: -------------------------------------------------------------------------------- 1 | ```react|noSource 2 |
    3 | 15 | {fixtures.BaB153Stub} 16 | 17 | 18 | 19 | {fixtures.ArticleStub} 20 | 21 |
    22 | ``` -------------------------------------------------------------------------------- /src/templates/Article/email/index.js: -------------------------------------------------------------------------------- 1 | import { matchType } from 'mdast-react-render/lib/utils' 2 | import Container from '../../EditorialNewsletter/email/Container' 3 | import { editorialParagraphRule } from '../../shared/email/rules/paragraphRule' 4 | import centerRule from '../../shared/email/rules/centerRule' 5 | import { 6 | coverRule, 7 | edgeToEdgeFigureRule 8 | } from '../../shared/email/rules/figureRule' 9 | import titleBlockRule from '../../shared/email/rules/titleBlockRule' 10 | 11 | const articleEmailSchema = { 12 | rules: [ 13 | { 14 | matchMdast: matchType('root'), 15 | component: Container, 16 | props: node => ({ 17 | meta: node.meta || {}, 18 | variableContext: { 19 | firstName: 'FNAME', 20 | lastName: 'LNAME', 21 | _mergeTags: true 22 | } 23 | }), 24 | rules: [ 25 | editorialParagraphRule, 26 | titleBlockRule, 27 | centerRule, 28 | coverRule, 29 | edgeToEdgeFigureRule 30 | ] 31 | } 32 | ] 33 | } 34 | 35 | export default articleEmailSchema 36 | -------------------------------------------------------------------------------- /src/templates/Article/test/render.test.js: -------------------------------------------------------------------------------- 1 | import { BaB153Stub, ArticleStub } from './article.stub' 2 | import { renderEmail } from 'mdast-react-render/lib/email' 3 | import { renderMdast } from 'mdast-react-render' 4 | import { parse } from '@orbiting/remark-preset' 5 | import articleEmailSchema from '../email' 6 | import createSchema from '../index' 7 | 8 | const babStub = parse(BaB153Stub) 9 | const articleStub = parse(ArticleStub) 10 | 11 | describe('chart utils test-suite', () => { 12 | it('templates/article: render web', () => { 13 | expect(() => { 14 | renderMdast(babStub, createSchema(), { MissingNode: false }) 15 | }).not.toThrow() 16 | }) 17 | 18 | it('templates/article: render bab as email', () => { 19 | expect(() => { 20 | renderEmail(babStub, articleEmailSchema, { MissingNode: false }) 21 | }).not.toThrow() 22 | }) 23 | 24 | it('templates/article: render article as email', () => { 25 | expect(() => { 26 | renderEmail(articleStub, articleEmailSchema, { MissingNode: false }) 27 | }).not.toThrow() 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /src/templates/Comment/email.js: -------------------------------------------------------------------------------- 1 | import createCommentSchema from './schema' 2 | 3 | import { 4 | BlockCode, 5 | BlockQuote, 6 | BlockQuoteNested, 7 | BlockQuoteParagraph, 8 | Code, 9 | Container, 10 | Definition, 11 | Heading, 12 | Link, 13 | List, 14 | ListItem, 15 | Paragraph, 16 | StrikeThrough 17 | } from '../../components/CommentBody/email' 18 | 19 | const createCommentEmailSchema = ({ ...args } = {}) => { 20 | return createCommentSchema({ 21 | BlockCode, 22 | BlockQuote, 23 | BlockQuoteNested, 24 | BlockQuoteParagraph, 25 | Code, 26 | Container, 27 | Definition, 28 | Heading, 29 | Link, 30 | List, 31 | ListItem, 32 | Paragraph, 33 | StrikeThrough, 34 | ...args 35 | }) 36 | } 37 | 38 | export default createCommentEmailSchema 39 | -------------------------------------------------------------------------------- /src/templates/Comment/index.js: -------------------------------------------------------------------------------- 1 | import schema from './web' 2 | 3 | export default schema 4 | -------------------------------------------------------------------------------- /src/templates/Comment/web.js: -------------------------------------------------------------------------------- 1 | import createCommentSchema from './schema' 2 | 3 | import { 4 | CommentBodyBlockCode, 5 | CommentBodyBlockQuote, 6 | CommentBodyBlockQuoteNested, 7 | CommentBodyBlockQuoteParagraph, 8 | CommentBodyCode, 9 | CommentBodyDefinition, 10 | CommentBodyContainer, 11 | CommentBodyHeading, 12 | CommentBodyList, 13 | CommentBodyListItem, 14 | CommentBodyParagraph 15 | } from '../../components/CommentBody/web' 16 | import { Editorial } from '../../components/Typography' 17 | 18 | const createCommentWebSchema = ({ ...args } = {}) => { 19 | return createCommentSchema({ 20 | BlockCode: CommentBodyBlockCode, 21 | BlockQuote: CommentBodyBlockQuote, 22 | BlockQuoteNested: CommentBodyBlockQuoteNested, 23 | BlockQuoteParagraph: CommentBodyBlockQuoteParagraph, 24 | Code: CommentBodyCode, 25 | Container: CommentBodyContainer, 26 | Definition: CommentBodyDefinition, 27 | Heading: CommentBodyHeading, 28 | Link: Editorial.A, 29 | List: CommentBodyList, 30 | ListItem: CommentBodyListItem, 31 | Paragraph: CommentBodyParagraph, 32 | StrikeThrough: Editorial.StrikeThrough, 33 | ...args 34 | }) 35 | } 36 | 37 | export default createCommentWebSchema 38 | -------------------------------------------------------------------------------- /src/templates/Discussion/docs.md: -------------------------------------------------------------------------------- 1 | A preconfigured article template for discussion pages. 2 | 3 | ```code|lang-jsx 4 | import createDiscussionSchema from '@project-r/styleguide/lib/templates/Discussion' 5 | 6 | const schema = createDiscussionSchema() 7 | ``` 8 | 9 | `createDiscussionSchema` take the same keys as the article template. 10 | 11 | Defaults: 12 | - `repoPrefix`, `discussion-` 13 | - `getPath`, `/YYYY/MM/DD/:slug/diskussion` 14 | - `customMetaFields`, repo refs for `format`, `dossier` and `discussion` settings with a `commentsMaxLength`, `commentsMinInterval` and `discussionAnonymity` field. 15 | -------------------------------------------------------------------------------- /src/templates/Dossier/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { DossierTag } from '../../components/Dossier' 4 | 5 | import createArticleSchema from '../Article' 6 | 7 | const DefaultLink = ({ children }) => children 8 | 9 | const createDossierSchema = ({ 10 | dossierLabel = 'Dossier', 11 | dossierHref = '/dossier', 12 | customMetaFields = [], 13 | series = false, 14 | darkMode, 15 | paynotes = true, 16 | Link = DefaultLink, 17 | titleBlockPrepend = null, 18 | getPath = ({ slug }) => `/dossier/${(slug || '').split('/').pop()}`, 19 | hasEmailTemplate = false, 20 | ...args 21 | } = {}) => { 22 | return createArticleSchema({ 23 | repoPrefix: 'dossier-', 24 | getPath, 25 | hasEmailTemplate, 26 | titleBlockPrepend: [ 27 | titleBlockPrepend, 28 | 29 | {dossierLabel} 30 | 31 | ], 32 | customMetaFields: [ 33 | { 34 | label: 'Diskussion', 35 | key: 'discussion', 36 | ref: 'repo' 37 | }, 38 | ...customMetaFields 39 | ], 40 | series, 41 | darkMode, 42 | paynotes, 43 | Link, 44 | ...args 45 | }) 46 | } 47 | 48 | export default createDossierSchema 49 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Blockquote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../theme/fonts' 3 | 4 | const blockquoteStyle = { 5 | marginBottom: '30px' 6 | } 7 | 8 | const textStyle = { 9 | color: '#000', 10 | fontSize: '28px', 11 | lineHeight: '33px', 12 | fontFamily: fontFamilies.serifBold 13 | } 14 | 15 | const sourceStyle = { 16 | color: '#000', 17 | fontSize: '15px', 18 | lineHeight: '158%', 19 | fontFamily: fontFamilies.sansSerifRegular, 20 | fontStyle: 'normal' 21 | } 22 | 23 | export default ({ children }) => ( 24 |
    {children}
    25 | ) 26 | 27 | export const BlockquoteText = ({ children }) => ( 28 |

    {children}

    29 | ) 30 | 31 | export const BlockquoteSource = ({ children }) => ( 32 | {children} 33 | ) 34 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Center.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../theme/fonts' 3 | import { Mso } from 'mdast-react-render/lib/email' 4 | 5 | export default ({ children }) => { 6 | return ( 7 | 8 | 9 | 10 | {` 11 | 12 | 13 | 40 | 41 |
    14 | `} 15 | 16 | 29 | 30 | 31 | 34 | 35 | 36 |
    32 | {children} 33 |
    37 | 38 | {` 39 |
    42 | `} 43 |
    44 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Cover.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const CoverImage = ({ src, alt }) => { 4 | // skip rendering empty cover images 5 | // - some email clients show a prominent error when rendering an img tag without a src 6 | // - this happens for covers all the time because they currently can't be removed in publikator-frontend 7 | if (!src && !alt) { 8 | return null 9 | } 10 | 11 | return ( 12 | {alt} 24 | ) 25 | } 26 | 27 | export default ({ children }) => ( 28 | 29 | 30 | {children} 31 | 32 | 33 | ) 34 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Figure.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../theme/fonts' 3 | import { imageResizeUrl } from 'mdast-react-render/lib/utils' 4 | 5 | export const Image = ({ src, alt, plain }) => ( 6 | {alt} 21 | ) 22 | 23 | export const Caption = ({ children, data }) => ( 24 |

    34 | {children} 35 |

    36 | ) 37 | 38 | export const Byline = ({ children, data }) => ( 39 | 46 | {children} 47 | 48 | ) 49 | 50 | export default ({ children }) => {children} 51 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/HR.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import colors from '../../../theme/colors' 3 | 4 | const hrStyle = { 5 | border: 0, 6 | height: 1, 7 | color: colors.divider, 8 | backgroundColor: colors.divider, 9 | marginTop: 30, 10 | marginBottom: 30 11 | } 12 | 13 | export default () =>
    14 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Headlines.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../theme/fonts' 3 | 4 | const h2Style = { 5 | fontFamily: fontFamilies.serifBold, 6 | fontSize: '23px', 7 | lineHeight: '130%', 8 | marginTop: '60px' 9 | } 10 | 11 | export const H2 = ({ children }) =>

    {children}

    12 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const ListItem = ({ children }) =>
  • {children}
  • 4 | 5 | export default ({ children, data }) => 6 | data.ordered ?
      {children}
    :
      {children}
    7 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/Paragraph.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../theme/fonts' 3 | import { Editorial } from '../../../components/Typography' 4 | import colors from '../../../theme/colors' 5 | 6 | export const paragraphStyle = { 7 | color: colors.text, 8 | fontSize: '19px', 9 | lineHeight: '158%', 10 | fontFamily: fontFamilies.serifRegular 11 | } 12 | 13 | export const linkStyle = { 14 | color: colors.text, 15 | textDecoration: 'underline', 16 | textDecorationSkip: 'ink' 17 | } 18 | 19 | export const Br = () =>
    20 | export const A = ({ children, href, title }) => ( 21 | 22 | {children} 23 | 24 | ) 25 | 26 | // emails normally don't do glamor but 27 | // Editorial.fontRule is manually injected in ./Container 28 | export default ({ children }) => ( 29 |

    30 | {children} 31 |

    32 | ) 33 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/SubSup.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const Sub = ({ children }) => ( 4 | {children} 5 | ) 6 | export const Sup = ({ children }) => ( 7 | {children} 8 | ) 9 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/email/index.js: -------------------------------------------------------------------------------- 1 | import createNewsletterSchema from '../schema' 2 | 3 | import { H2 } from './Headlines' 4 | import Paragraph, { A } from './Paragraph' 5 | import Container from './Container' 6 | import Cover, { CoverImage } from './Cover' 7 | import Center from './Center' 8 | import Figure, { Image, Caption, Byline } from './Figure' 9 | import { Sub, Sup } from './SubSup' 10 | import { Button } from './Button' 11 | import List, { ListItem } from './List' 12 | 13 | const createNewsletterEmailSchema = ({ ...args } = {}) => { 14 | return createNewsletterSchema({ 15 | H2, 16 | Paragraph, 17 | Container, 18 | Cover, 19 | CoverImage, 20 | Center, 21 | Figure, 22 | Image, 23 | Caption, 24 | Byline, 25 | Sub, 26 | Sup, 27 | Button, 28 | List, 29 | ListItem, 30 | A, 31 | ...args, 32 | variableContext: args.variableContext || { 33 | firstName: 'FNAME', 34 | lastName: 'LNAME', 35 | _mergeTags: true 36 | } 37 | }) 38 | } 39 | 40 | export default createNewsletterEmailSchema 41 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/index.js: -------------------------------------------------------------------------------- 1 | import schema from './web' 2 | 3 | export default schema 4 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/web/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Button from '../../../components/Button' 3 | 4 | export default ({ 5 | children, 6 | href, 7 | title, 8 | target, 9 | primary, 10 | block, 11 | attributes 12 | }) => ( 13 | <> 14 | 25 | {!block &&
    } 26 | 27 | ) 28 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/web/Container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { css } from 'glamor' 3 | import { fontStyles } from '../../../theme/fonts' 4 | import { useColorContext } from '../../../components/Colors/useColorContext' 5 | 6 | const styles = { 7 | container: { 8 | ...fontStyles.serifRegular, 9 | fontSize: 18, 10 | WebkitFontSmoothing: 'antialiased', 11 | width: '100%', 12 | margin: 0, 13 | padding: 0 14 | } 15 | } 16 | 17 | export default ({ children, attributes = {} }) => { 18 | const [colorScheme] = useColorContext() 19 | return ( 20 |
    26 | {children} 27 |
    28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/web/Figure.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Figure } from '../../../components/Figure' 3 | 4 | const StyledFigure = ({ children, plain }) => ( 5 |
    14 | {children} 15 |
    16 | ) 17 | 18 | export default StyledFigure 19 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/web/ListP.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default ({ children, attributes = {} }) => ( 4 |

    {children}

    5 | ) 6 | -------------------------------------------------------------------------------- /src/templates/EditorialNewsletter/web/index.js: -------------------------------------------------------------------------------- 1 | import createNewsletterSchema from '../schema' 2 | 3 | import Container from './Container' 4 | import StyledFigure from './Figure' 5 | import Button from './Button' 6 | import ListP from './ListP' 7 | 8 | import Center from '../../../components/Center' 9 | import { 10 | FigureCover, 11 | FigureImage, 12 | FigureCaption, 13 | FigureByline 14 | } from '../../../components/Figure' 15 | import { Sub, Sup } from '../../../components/Typography' 16 | import { P, Subhead, A } from '../../../components/Typography/Editorial' 17 | import { List, ListItem } from '../../../components/List' 18 | 19 | const createNewsletterWebSchema = ({ ...args } = {}) => { 20 | return createNewsletterSchema({ 21 | H2: Subhead, 22 | Paragraph: P, 23 | Container, 24 | Cover: FigureCover, 25 | CoverImage: FigureImage, 26 | Center, 27 | Figure: StyledFigure, 28 | Image: FigureImage, 29 | Caption: FigureCaption, 30 | Byline: FigureByline, 31 | Sub, 32 | Sup, 33 | Button: Button, 34 | List, 35 | ListItem, 36 | ListP, 37 | A, 38 | ...args 39 | }) 40 | } 41 | 42 | export default createNewsletterWebSchema 43 | -------------------------------------------------------------------------------- /src/templates/Format/docs.md: -------------------------------------------------------------------------------- 1 | A preconfigured article template for format pages. 2 | 3 | ```code|lang-jsx 4 | import createFormatSchema from '@project-r/styleguide/lib/templates/Format' 5 | 6 | const schema = createFormatSchema() 7 | ``` 8 | 9 | `createFormatSchema` take the same keys as the article template. 10 | 11 | Defaults: 12 | - `repoPrefix`, `format-` 13 | - `getPath`, `/format/:slug` 14 | - `customMetaFields`, always adds repo refs for `discussion`, `dossier` and `format` settings with a `kind` (font) and `color` field. 15 | - `series`, false 16 | - `darkMode`, false 17 | - `metaBody`, true 18 | 19 | # Example 20 | 21 | ```react|noSource 22 | {` 23 | 24 |
    FIGURE
    25 | 26 | \`\`\` 27 | { 28 | "size": "tiny" 29 | } 30 | \`\`\` 31 | 32 | ![](/static/dada.jpg) ![](/static/dada_dark.png) 33 | 34 |
    35 | 36 |
    TITLE
    37 | 38 | # Dada 39 | 40 |
    41 | 42 |
    CENTER
    43 | 44 | Er hörte leise Schritte hinter sich. Das bedeutete nichts Gutes. Wer würde ihm schon folgen, spät in der Nacht und dazu noch in dieser engen Gasse mitten im übel beleumundeten Hafenviertel? 45 | 46 | Gerade jetzt, wo er das Ding seines Lebens gedreht hatte und mit der Beute verschwinden wollte! Hatte einer seiner zahllosen Kollegen dieselbe Idee gehabt, ihn beobachtet und abgewartet, um ihn nun um die Früchte seiner Arbeit zu erleichtern? 47 | 48 |
    49 | 50 | `}
    51 | ``` 52 | -------------------------------------------------------------------------------- /src/templates/Page/index.js: -------------------------------------------------------------------------------- 1 | import createArticleSchema from '../Article' 2 | 3 | const DefaultLink = ({ children }) => children 4 | 5 | const createPageSchema = ({ 6 | documentEditorOptions, 7 | customMetaFields = [], 8 | series = false, 9 | darkMode, 10 | paynotes = true, 11 | Link = DefaultLink, 12 | titleBlockPrepend = null, 13 | getPath = ({ slug }) => `/${(slug || '').split('/').pop()}`, 14 | metaHeadlines = true, 15 | skipContainer = false, 16 | skipCenter = false, 17 | hasEmailTemplate = false, 18 | ...args 19 | } = {}) => { 20 | return createArticleSchema({ 21 | documentEditorOptions: { 22 | skipCredits: true, 23 | titleCenter: true 24 | }, 25 | repoPrefix: 'page-', 26 | getPath, 27 | hasEmailTemplate, 28 | customMetaFields: [ 29 | { 30 | label: 'Diskussion', 31 | key: 'discussion', 32 | ref: 'repo' 33 | }, 34 | { 35 | label: 'Action Bar ausblenden', 36 | key: 'disableActionBar', 37 | ref: 'bool' 38 | }, 39 | ...customMetaFields 40 | ], 41 | series, 42 | darkMode, 43 | paynotes, 44 | Link, 45 | metaHeadlines, 46 | skipContainer, 47 | skipCenter, 48 | ...args 49 | }) 50 | } 51 | 52 | export default createPageSchema 53 | -------------------------------------------------------------------------------- /src/templates/Section/docs.md: -------------------------------------------------------------------------------- 1 | A preconfigured article template for section pages. 2 | 3 | ```code|lang-jsx 4 | import createSectionSchema from '@project-r/styleguide/lib/templates/Section' 5 | 6 | const schema = createSectionSchema() 7 | ``` 8 | 9 | `createSectionSchema` take the same keys as the article template. 10 | 11 | Defaults: 12 | - `repoPrefix`, `section-` 13 | - `getPath`, `/:slug` 14 | - `customMetaFields`, always adds repo refs for `discussion`, `dossier` and `format` settings with a `kind` (font) and `color` field. 15 | - `series`, false 16 | - `darkMode`, false 17 | - `metaBody`, true 18 | 19 | # Example 20 | 21 | ```react|noSource 22 | {` 23 | 24 |
    TITLE
    25 | 26 | # Dada 27 | 28 |
    29 | 30 |
    CENTER
    31 | 32 | Er hörte leise Schritte hinter sich. Das bedeutete nichts Gutes. Wer würde ihm schon folgen, spät in der Nacht und dazu noch in dieser engen Gasse mitten im übel beleumundeten Hafenviertel? 33 | 34 | Gerade jetzt, wo er das Ding seines Lebens gedreht hatte und mit der Beute verschwinden wollte! Hatte einer seiner zahllosen Kollegen dieselbe Idee gehabt, ihn beobachtet und abgewartet, um ihn nun um die Früchte seiner Arbeit zu erleichtern? 35 | 36 |
    37 | 38 | `}
    39 | ``` 40 | -------------------------------------------------------------------------------- /src/templates/docs.js: -------------------------------------------------------------------------------- 1 | import { renderMdast } from 'mdast-react-render' 2 | import { parse } from '@orbiting/remark-preset' 3 | 4 | export const Markdown = ({ children, schema, rootData }) => { 5 | return renderMdast( 6 | { 7 | ...parse(children), 8 | ...rootData 9 | }, 10 | schema 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/BlockQuote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontStyles } from '../../../../theme/fonts' 3 | 4 | const BlockQuote = ({ children }) =>
    {children}
    5 | 6 | export default BlockQuote 7 | 8 | export const ParagraphWrapper = ({ children }) => ( 9 |
    14 | {children} 15 |
    16 | ) 17 | 18 | export const Paragraph = ({ children }) => ( 19 |

    30 | {children} 31 |

    32 | ) 33 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Caption.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontFamilies } from '../../../../theme/fonts' 3 | 4 | export const Caption = ({ children }) => ( 5 |

    15 | {children} 16 |

    17 | ) 18 | 19 | export const Byline = ({ children }) => ( 20 | 28 | {children} 29 | 30 | ) 31 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Center.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Mso } from 'mdast-react-render/lib/email' 3 | import { fontFamilies } from '../../../../theme/fonts' 4 | 5 | const Center = ({ children }) => { 6 | return ( 7 | 8 | 9 | 10 | {` 11 | 12 | 13 | 40 | 41 |
    14 | `} 15 | 16 | 29 | 30 | 31 | 34 | 35 | 36 |
    32 | {children} 33 |
    37 | 38 | {` 39 |
    42 | `} 43 |
    44 | 45 | 46 | ) 47 | } 48 | 49 | export default Center 50 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Figure.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { imageResizeUrl } from 'mdast-react-render/lib/utils' 3 | 4 | export const Figure = ({ children }) => ( 5 | {children} 6 | ) 7 | 8 | export const Image = ({ src, alt, width, resize }) => ( 9 | {alt} 26 | ) 27 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Heading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontStyles } from '../../../../theme/fonts' 3 | 4 | export const Heading2 = ({ children }) => ( 5 |

    13 | {children} 14 |

    15 | ) 16 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/HorizontalRule.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const HorizontalRule = () => ( 4 |
    13 | ) 14 | 15 | export default HorizontalRule 16 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Link.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import colors from '../../../../theme/colors' 3 | 4 | export const Link = ({ children, href, title }) => ( 5 | 14 | {children} 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/List.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fontStyles } from '../../../../theme/fonts' 3 | 4 | const List = ({ children, ordered, start }) => { 5 | if (ordered) return
      {children}
    6 | return
      {children}
    7 | } 8 | 9 | export default List 10 | 11 | export const ListItem = ({ children }) =>
  • {children}
  • 12 | 13 | export const ListParagraph = ({ children }) => ( 14 |

    22 | {children} 23 |

    24 | ) 25 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Note.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Interaction } from '../../../../components/Typography' 3 | import colors from '../../../../theme/colors' 4 | import { fontStyles } from '../../../../theme/fonts' 5 | 6 | export const Note = ({ children }) => <>{children} 7 | 8 | export const NoteParagraph = ({ children, attributes, ...props }) => ( 9 |

    21 | {children} 22 |

    23 | ) 24 | 25 | export default NoteParagraph 26 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Paragraph.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import colors from '../../../../theme/colors' 3 | import { fontStyles } from '../../../../theme/fonts' 4 | import { Editorial, Interaction } from '../../../../components/Typography' 5 | 6 | const baseParagraphStyle = { 7 | color: colors.text, 8 | fontSize: '19px', 9 | lineHeight: '30px', 10 | margin: '30px 0' 11 | } 12 | 13 | export const EditorialParagraph = ({ children }) => ( 14 |

    18 | {children} 19 |

    20 | ) 21 | 22 | export const InteractionParagraph = ({ children }) => ( 23 |

    27 | {children} 28 |

    29 | ) 30 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Sub.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Sub = ({ children }) => ( 4 | {children} 5 | ) 6 | 7 | export default Sub 8 | -------------------------------------------------------------------------------- /src/templates/shared/email/components/Sup.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Sup = ({ children }) => ( 4 | {children} 5 | ) 6 | 7 | export default Sup 8 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/articleCollectionRule.js: -------------------------------------------------------------------------------- 1 | import { matchZone } from 'mdast-react-render/lib/utils' 2 | 3 | const articleCollectionRule = { 4 | matchMdast: matchZone('ARTICLECOLLECTION'), 5 | component: () => null, 6 | isVoid: true 7 | } 8 | 9 | export default articleCollectionRule 10 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/blockQuoteRule.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import BlockQuote, { 3 | Paragraph, 4 | ParagraphWrapper 5 | } from '../components/BlockQuote' 6 | import { 7 | matchParagraph, 8 | matchType, 9 | matchZone 10 | } from 'mdast-react-render/lib/utils' 11 | import legendRule from './legendRules' 12 | import { inlineInteractionParagraphRules } from './paragraphRule' 13 | 14 | const blockQuoteRule = { 15 | matchMdast: matchZone('BLOCKQUOTE'), 16 | props: node => { 17 | return { 18 | isEmpty: 19 | node.children && 20 | node.children.length === 1 && 21 | !node.children[0].children 22 | } 23 | }, 24 | component: ({ children, isEmpty }) => { 25 | if (isEmpty) return null 26 | return
    {children}
    27 | }, 28 | rules: [ 29 | { 30 | matchMdast: matchType('blockquote'), 31 | component: ParagraphWrapper, 32 | rules: [ 33 | { 34 | matchMdast: matchParagraph, 35 | component: Paragraph, 36 | rules: inlineInteractionParagraphRules 37 | } 38 | ] 39 | }, 40 | legendRule 41 | ] 42 | } 43 | 44 | export default blockQuoteRule 45 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/centerRule.js: -------------------------------------------------------------------------------- 1 | import { matchZone } from 'mdast-react-render/lib/utils' 2 | import { editorialParagraphRule } from './paragraphRule' 3 | import inlineHeadingsRules from './inlineHeadingsRule' 4 | import blockQuoteRule from './blockQuoteRule' 5 | import hrRule from './hrRule' 6 | import Center from '../components/Center' 7 | import { figureRule } from './figureRule' 8 | import figureGroupRule from './figureGroupRule' 9 | import noteRule from './noteRule' 10 | import articleCollectionRule from './articleCollectionRule' 11 | import listRule from './listRule' 12 | import pullQuoteRule from './pullQuoteRule' 13 | import infoBoxRule from './infoBoxRule' 14 | 15 | const centerRule = { 16 | matchMdast: matchZone('CENTER'), 17 | component: Center, 18 | rules: [ 19 | editorialParagraphRule, 20 | ...inlineHeadingsRules, 21 | hrRule, 22 | figureRule, 23 | figureGroupRule, 24 | listRule, 25 | blockQuoteRule, 26 | pullQuoteRule, 27 | noteRule, 28 | infoBoxRule, 29 | articleCollectionRule 30 | ] 31 | } 32 | 33 | export default centerRule 34 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/figureGroupRule.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { matchZone } from 'mdast-react-render/lib/utils' 3 | import { figureRule } from './figureRule' 4 | import legendRule from './legendRules' 5 | 6 | const figureGroupRule = { 7 | matchMdast: matchZone('FIGUREGROUP'), 8 | component: ({ children }) =>
    {children}
    , 9 | rules: [figureRule, legendRule] 10 | } 11 | 12 | export default figureGroupRule 13 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/hrRule.js: -------------------------------------------------------------------------------- 1 | import { matchType } from 'mdast-react-render/lib/utils' 2 | import HorizontalRule from '../components/HorizontalRule' 3 | 4 | const hrRule = { 5 | matchMdast: matchType('thematicBreak'), 6 | component: HorizontalRule 7 | } 8 | 9 | export default hrRule 10 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/inlineHeadingsRule.js: -------------------------------------------------------------------------------- 1 | import { matchHeading } from 'mdast-react-render/lib/utils' 2 | import { Heading2 } from '../components/Heading' 3 | import inlineRules from './inlineRules' 4 | 5 | const inlineHeadingsRules = [ 6 | { 7 | matchMdast: matchHeading(2), 8 | component: Heading2, 9 | rules: inlineRules 10 | } 11 | ] 12 | 13 | export default inlineHeadingsRules 14 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/inlineRules.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { matchType } from 'mdast-react-render/lib/utils' 3 | import Sup from '../components/Sup' 4 | import Sub from '../components/Sub' 5 | 6 | const inlineRules = [ 7 | { 8 | matchMdast: matchType('sup'), 9 | component: Sup 10 | }, 11 | { 12 | matchMdast: matchType('sub'), 13 | component: Sub 14 | }, 15 | { 16 | matchMdast: matchType('break'), 17 | component: () =>
    , 18 | isVoid: true 19 | } 20 | ] 21 | 22 | export default inlineRules 23 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/legendRules.js: -------------------------------------------------------------------------------- 1 | import { matchParagraph, matchType } from 'mdast-react-render/lib/utils' 2 | import { Byline, Caption } from '../components/Caption' 3 | import { linkRule } from './linkRule' 4 | import inlineRules from './inlineRules' 5 | 6 | const legendRule = { 7 | matchMdast: matchParagraph, 8 | component: Caption, 9 | rules: [ 10 | { 11 | matchMdast: matchType('emphasis'), 12 | component: Byline 13 | }, 14 | linkRule, 15 | inlineRules 16 | ] 17 | } 18 | 19 | export default legendRule 20 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/linkRule.js: -------------------------------------------------------------------------------- 1 | import { matchType } from 'mdast-react-render/lib/utils' 2 | import { Link } from '../components/Link' 3 | 4 | export const linkRule = { 5 | matchMdast: matchType('link'), 6 | component: Link, 7 | props: node => ({ 8 | title: node.title, 9 | href: node.url 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/listRule.js: -------------------------------------------------------------------------------- 1 | import { matchParagraph, matchType } from 'mdast-react-render/lib/utils' 2 | import List, { ListItem, ListParagraph } from '../components/List' 3 | import { inlineEditorialParagraphRules } from './paragraphRule' 4 | 5 | const listRule = { 6 | matchMdast: matchType('list'), 7 | component: List, 8 | props: node => { 9 | return { 10 | ordered: node.ordered, 11 | start: node.start 12 | } 13 | }, 14 | rules: [ 15 | { 16 | matchMdast: matchType('listItem'), 17 | component: ListItem, 18 | rules: [ 19 | { 20 | matchMdast: matchParagraph, 21 | // Custom paragraph required as the margin defers from the default variant 22 | component: ListParagraph, 23 | rules: inlineEditorialParagraphRules 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | 30 | export default listRule 31 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/noteRule.js: -------------------------------------------------------------------------------- 1 | import { matchParagraph, matchZone } from 'mdast-react-render/lib/utils' 2 | import { Note, NoteParagraph } from '../components/Note' 3 | import inlineRules from './inlineRules' 4 | import { linkRule } from './linkRule' 5 | 6 | const noteRule = { 7 | matchMdast: matchZone('NOTE'), 8 | component: Note, 9 | rules: [ 10 | { 11 | matchMdast: matchParagraph, 12 | component: NoteParagraph, 13 | rules: [inlineRules, linkRule] 14 | } 15 | ] 16 | } 17 | 18 | export default noteRule 19 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/pullQuoteRule.js: -------------------------------------------------------------------------------- 1 | import { matchParagraph, matchZone } from 'mdast-react-render/lib/utils' 2 | import { matchFigure, matchLast } from '../../../Article/utils' 3 | import inlineRules from './inlineRules' 4 | import { linkRule } from './linkRule' 5 | import { getImageRules } from './figureRule' 6 | import { 7 | PullQuote, 8 | PullQuoteSource, 9 | PullQuoteText 10 | } from '../components/PullQuote' 11 | import { Figure } from '../components/Figure' 12 | 13 | const pullQuoteRule = { 14 | matchMdast: matchZone('QUOTE'), 15 | component: PullQuote, 16 | props: node => ({ 17 | hasFigure: !!node.children.find(matchZone('FIGURE')) 18 | }), 19 | rules: [ 20 | { 21 | matchMdast: matchZone('FIGURE'), 22 | component: Figure, 23 | rules: getImageRules({ forceWidth: '155px' }) 24 | }, 25 | { 26 | // PullQuote text 27 | matchMdast: (node, index, parent) => 28 | matchParagraph(node) && 29 | (index === 0 || 30 | (index === 1 && matchFigure(parent.children[0])) || 31 | !matchLast(node, index, parent)), 32 | component: PullQuoteText, 33 | rules: [...inlineRules, linkRule] 34 | }, 35 | { 36 | // PullQuote Source 37 | matchMdast: (node, index, parent) => 38 | matchParagraph(node) && matchLast(node, index, parent), 39 | component: PullQuoteSource, 40 | rules: [...inlineRules, linkRule] 41 | } 42 | ] 43 | } 44 | 45 | export default pullQuoteRule 46 | -------------------------------------------------------------------------------- /src/templates/shared/email/rules/teaserGroupRule.js: -------------------------------------------------------------------------------- 1 | import { matchZone } from 'mdast-react-render/lib/utils' 2 | 3 | const teaserGroupRule = { 4 | matchMdast: matchZone('TEASERGROUP'), 5 | component: () => null, 6 | isVoid: true 7 | } 8 | 9 | export default teaserGroupRule 10 | -------------------------------------------------------------------------------- /src/templates/shared/email/util/ImageSizingUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the width of an image based on it's size and the displayWidth 3 | * @param size 4 | * @param displayWidth 5 | * @param fill 6 | * @returns {string} 7 | */ 8 | export function getImageSize({ size = '', displayWidth, fill }) { 9 | switch (size) { 10 | case 'tiny': 11 | return '325px' 12 | default: 13 | if (fill) return '100%' 14 | return displayWidth 15 | } 16 | } 17 | 18 | export function isPixelSize(value) { 19 | return /^([0-9]+)px$/.test(value) 20 | } 21 | 22 | export function isRelativeSize(value) { 23 | return /^([0-9]+)%$/.test(value) 24 | } 25 | -------------------------------------------------------------------------------- /src/templates/shared/email/util/NodeDepthUtil.js: -------------------------------------------------------------------------------- 1 | import { matchType } from 'mdast-react-render/lib/utils' 2 | 3 | /** 4 | * Get the depth relative to closest ancestor that matches the given matcher 5 | * 6 | * If node is a direct child -> 0 7 | * Node is a nested child -> >= 1 8 | * If no parent matches -> -1 9 | * 10 | * @param ancestors 11 | * @param matcher 12 | * @returns {number} 13 | */ 14 | export function getNodeDepth(ancestors, matcher = matchType('root')) { 15 | let depth = 0 16 | for (let i = 0; i < ancestors.length; i++) { 17 | const node = ancestors[i] 18 | if (matcher(node)) return depth 19 | depth += 1 20 | } 21 | return -1 22 | } 23 | -------------------------------------------------------------------------------- /src/theme/env.js: -------------------------------------------------------------------------------- 1 | const ENV = 2 | (typeof window !== 'undefined' && 3 | (window.ENV || (window.__NEXT_DATA__ && window.__NEXT_DATA__.env))) || 4 | process.env || 5 | {} 6 | 7 | const SG_ENV = {} 8 | 9 | const SG_PREFIX = /^(REACT_APP_)?SG_(.+)$/ 10 | 11 | Object.keys(ENV).forEach(key => { 12 | const matches = key.match(SG_PREFIX) 13 | if (matches) { 14 | SG_ENV[matches[2]] = ENV[key] 15 | } 16 | }) 17 | 18 | export default SG_ENV 19 | 20 | export const getJson = key => (SG_ENV[key] && JSON.parse(SG_ENV[key])) || {} 21 | -------------------------------------------------------------------------------- /src/theme/mediaQueries.js: -------------------------------------------------------------------------------- 1 | export const mBreakPoint = 768 2 | export const lBreakPoint = 1025 3 | 4 | export const onlyS = `@media only screen and (max-width: ${mBreakPoint - 1}px)` 5 | export const mUp = `@media only screen and (min-width: ${mBreakPoint}px)` 6 | export const lUp = `@media only screen and (min-width: ${lBreakPoint}px)` 7 | -------------------------------------------------------------------------------- /src/theme/zIndex.docs.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```code|lang-js 4 | import {zIndex} from '@project-r/styleguide' 5 | 6 | const style = css({ 7 | zIndex: zIndex.overlay 8 | }) 9 | ``` 10 | 11 | ## Values 12 | 13 | ```table 14 | rows: 15 | - Name: dropdown 16 | Value: 5 17 | - Name: overlay 18 | Value: 50 19 | ``` 20 | -------------------------------------------------------------------------------- /src/theme/zIndex.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dropdown: 5, 3 | frontImage: 1, 4 | callout: 20, 5 | overlay: 50, 6 | foreground: 100 7 | } 8 | -------------------------------------------------------------------------------- /static.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "build/", 3 | "https_only": true, 4 | "routes": { 5 | "/**": "index.html" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "strict": false, 13 | "forceConsistentCasingInFileNames": true, 14 | "noEmit": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve", 21 | "allowSyntheticDefaultImports": true, 22 | "declaration": true, 23 | "declarationDir": "dist/types" 24 | }, 25 | "include": [ 26 | "src" 27 | ], 28 | "exclude": [ 29 | "node_modules", 30 | "dist" 31 | ] 32 | } 33 | --------------------------------------------------------------------------------