Linted the whole project with eslint
This commit was merged in pull request #3.
This commit is contained in:
503
.eslintrc
Normal file
503
.eslintrc
Normal file
@@ -0,0 +1,503 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"jsx": true,
|
||||
"useJSXTextNode": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "react-hooks", "react"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-inferrable-types": 0,
|
||||
"@typescript-eslint/no-empty-function": 0,
|
||||
"@typescript-eslint/no-var-requires": 0,
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
//
|
||||
//Possible Errors
|
||||
//
|
||||
// The following rules point out areas where you might have made mistakes.
|
||||
//
|
||||
"comma-dangle": 2,
|
||||
// disallow or enforce trailing commas
|
||||
"no-cond-assign": 2,
|
||||
// disallow assignment in conditional expressions
|
||||
"no-console": [
|
||||
"warn",
|
||||
{
|
||||
"allow": [
|
||||
"warn",
|
||||
"error"
|
||||
]
|
||||
}
|
||||
],
|
||||
// disallow use of console (off by default in the node environment)
|
||||
"no-constant-condition": 2,
|
||||
// disallow use of constant expressions in conditions
|
||||
"no-control-regex": 2,
|
||||
// disallow control characters in regular expressions
|
||||
"no-debugger": 2,
|
||||
// disallow use of debugger
|
||||
"no-dupe-args": 2,
|
||||
// disallow duplicate arguments in functions
|
||||
"no-dupe-keys": 2,
|
||||
// disallow duplicate keys when creating object literals
|
||||
"no-duplicate-case": 2,
|
||||
// disallow a duplicate case label.
|
||||
"no-empty": 2,
|
||||
// disallow empty statements
|
||||
"no-empty-character-class": 2,
|
||||
// disallow the use of empty character classes in regular expressions
|
||||
"no-ex-assign": 2,
|
||||
// disallow assigning to the exception in a catch block
|
||||
"no-extra-boolean-cast": 2,
|
||||
// disallow double-negation boolean casts in a boolean context
|
||||
"no-extra-parens": 0,
|
||||
// disallow unnecessary parentheses (off by default)
|
||||
"no-extra-semi": 2,
|
||||
// disallow unnecessary semicolons
|
||||
"no-func-assign": 2,
|
||||
// disallow overwriting functions written as function declarations
|
||||
"no-inner-declarations": 2,
|
||||
// disallow function or variable declarations in nested blocks
|
||||
"no-invalid-regexp": 2,
|
||||
// disallow invalid regular expression strings in the RegExp constructor
|
||||
"no-irregular-whitespace": 2,
|
||||
// disallow irregular whitespace outside of strings and comments
|
||||
"no-negated-in-lhs": 2,
|
||||
// disallow negation of the left operand of an in expression
|
||||
"no-obj-calls": 2,
|
||||
// disallow the use of object properties of the global object (Math and JSON) as functions
|
||||
"no-regex-spaces": 2,
|
||||
// disallow multiple spaces in a regular expression literal
|
||||
"no-sparse-arrays": 2,
|
||||
// disallow sparse arrays
|
||||
"no-unreachable": 2,
|
||||
// disallow unreachable statements after a return, throw, continue, or break statement
|
||||
"use-isnan": 2,
|
||||
// disallow comparisons with the value NaN
|
||||
"valid-jsdoc": 2,
|
||||
// Ensure JSDoc comments are valid (off by default)
|
||||
"valid-typeof": 2,
|
||||
// Ensure that the results of typeof are compared against a valid string
|
||||
//
|
||||
// Best Practices
|
||||
//
|
||||
// These are rules designed to prevent you from making mistakes.
|
||||
// They either prescribe a better way of doing something or help you avoid footguns.
|
||||
//
|
||||
"prefer-arrow-callback": 2,
|
||||
"block-scoped-var": 0,
|
||||
// treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863
|
||||
"complexity": 0,
|
||||
// specify the maximum cyclomatic complexity allowed in a program (off by default)
|
||||
"consistent-return": 2,
|
||||
// require return statements to either always or never specify values
|
||||
"curly": 2,
|
||||
// specify curly brace conventions for all control statements
|
||||
"default-case": 2,
|
||||
// require default case in switch statements (off by default)
|
||||
"dot-notation": 2,
|
||||
// encourages use of dot notation whenever possible
|
||||
"eqeqeq": 0,
|
||||
// require the use of === and !==
|
||||
"guard-for-in": 2,
|
||||
// make sure for-in loops have an if statement (off by default)
|
||||
"no-alert": 2,
|
||||
// disallow the use of alert, confirm, and prompt
|
||||
"no-caller": 2,
|
||||
// disallow use of arguments.caller or arguments.callee
|
||||
"no-div-regex": 2,
|
||||
// disallow division operators explicitly at beginning of regular expression (off by default)
|
||||
"no-else-return": 2,
|
||||
// disallow else after a return in an if (off by default)
|
||||
"no-eq-null": 2,
|
||||
// disallow comparisons to null without a type-checking operator (off by default)
|
||||
"no-eval": 1,
|
||||
// disallow use of eval()
|
||||
"no-extend-native": 2,
|
||||
// disallow adding to native types
|
||||
"no-extra-bind": 2,
|
||||
// disallow unnecessary function binding
|
||||
"no-fallthrough": 2,
|
||||
// disallow fallthrough of case statements
|
||||
"no-floating-decimal": 2,
|
||||
// disallow the use of leading or trailing decimal points in numeric literals (off by default)
|
||||
"no-implied-eval": 2,
|
||||
// disallow use of eval()-like methods
|
||||
"no-iterator": 2,
|
||||
// disallow usage of __iterator__ property
|
||||
"no-labels": 2,
|
||||
// disallow use of labeled statements
|
||||
"no-lone-blocks": 2,
|
||||
// disallow unnecessary nested blocks
|
||||
"no-loop-func": 2,
|
||||
// disallow creation of functions within loops
|
||||
"no-multi-spaces": 2,
|
||||
// disallow use of multiple spaces
|
||||
"no-multi-str": 2,
|
||||
// disallow use of multiline strings
|
||||
"no-native-reassign": 2,
|
||||
// disallow reassignments of native objects
|
||||
"no-new": 0,
|
||||
// disallow use of new operator when not part of the assignment or comparison
|
||||
"no-new-func": 2,
|
||||
// disallow use of new operator for Function object
|
||||
"no-new-wrappers": 2,
|
||||
// disallows creating new instances of String,Number, and Boolean
|
||||
"no-octal": 2,
|
||||
// disallow use of octal literals
|
||||
"no-octal-escape": 2,
|
||||
// disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
|
||||
"no-param-reassign": 2,
|
||||
// disallow reassignment of function parameters (off by default)
|
||||
"no-process-env": 0,
|
||||
// disallow use of import.meta.env (off by default)
|
||||
"no-proto": 2,
|
||||
// disallow usage of __proto__ property
|
||||
"no-redeclare": 2,
|
||||
// disallow declaring the same variable more then once
|
||||
"no-return-assign": 0,
|
||||
// disallow use of assignment in return statement
|
||||
"no-script-url": 2,
|
||||
// disallow use of javascript: urls.
|
||||
"no-self-compare": 2,
|
||||
// disallow comparisons where both sides are exactly the same (off by default)
|
||||
"no-sequences": 2,
|
||||
// disallow use of comma operator
|
||||
"no-throw-literal": 2,
|
||||
// restrict what can be thrown as an exception (off by default)
|
||||
"no-unused-expressions": 0,
|
||||
// disallow usage of expressions in statement position
|
||||
"no-void": 2,
|
||||
// disallow use of void operator (off by default)
|
||||
"no-warning-comments": [
|
||||
0,
|
||||
{
|
||||
"terms": [
|
||||
"todo",
|
||||
"fixme"
|
||||
],
|
||||
"location": "start"
|
||||
}
|
||||
],
|
||||
// disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default)
|
||||
"no-with": 2,
|
||||
// disallow use of the with statement
|
||||
"radix": 0,
|
||||
// require use of the second argument for parseInt() (off by default)
|
||||
"vars-on-top": 2,
|
||||
// requires to declare all vars on top of their containing scope (off by default)
|
||||
"wrap-iife": 2,
|
||||
// require immediate function invocation to be wrapped in parentheses (off by default)
|
||||
"yoda": 2,
|
||||
// require or disallow Yoda conditions
|
||||
//
|
||||
// Strict Mode
|
||||
//
|
||||
// These rules relate to using strict mode.
|
||||
//
|
||||
"strict": 0,
|
||||
// controls location of Use Strict Directives. 0: required by `babel-eslint`
|
||||
//
|
||||
// Variables
|
||||
//
|
||||
// These rules have to do with variable declarations.
|
||||
//
|
||||
"no-catch-shadow": 2,
|
||||
// disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment)
|
||||
"no-delete-var": 2,
|
||||
// disallow deletion of variables
|
||||
"no-label-var": 2,
|
||||
// disallow labels that share a name with a variable
|
||||
"no-shadow": 2,
|
||||
// disallow declaration of variables already declared in the outer scope
|
||||
"no-shadow-restricted-names": 2,
|
||||
// disallow shadowing of names such as arguments
|
||||
"no-undef-init": 2,
|
||||
// disallow use of undefined when initializing variables
|
||||
"no-undefined": 0,
|
||||
// disallow use of undefined variable (off by default)
|
||||
"no-unused-vars": 0,
|
||||
// disallow declaration of variables that are not used in the code
|
||||
"no-use-before-define": 0,
|
||||
// disallow use of variables before they are defined
|
||||
"init-declarations": 2,
|
||||
//
|
||||
// Stylistic Issues
|
||||
//
|
||||
// These rules are purely matters of style and are quite subjective.
|
||||
//
|
||||
"indent": [
|
||||
2,
|
||||
4,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
// this option sets a specific tab width for your code (off by default)
|
||||
"brace-style": 0,
|
||||
// enforce one true brace style (off by default)
|
||||
"camelcase": 0,
|
||||
// require camel case names
|
||||
"comma-spacing": [
|
||||
1,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
// enforce spacing before and after comma
|
||||
"comma-style": [
|
||||
1,
|
||||
"last"
|
||||
],
|
||||
// enforce one true comma style (off by default)
|
||||
"consistent-this": [
|
||||
1,
|
||||
"_this"
|
||||
],
|
||||
// enforces consistent naming when capturing the current execution context (off by default)
|
||||
"eol-last": 0,
|
||||
// enforce newline at the end of file, with no multiple empty lines
|
||||
"func-names": 0,
|
||||
// require function expressions to have a name (off by default)
|
||||
"func-style": 0,
|
||||
// enforces use of function declarations or expressions (off by default)
|
||||
"key-spacing": [
|
||||
1,
|
||||
{
|
||||
"beforeColon": false,
|
||||
"afterColon": true
|
||||
}
|
||||
],
|
||||
// enforces spacing between keys and values in object literal properties
|
||||
"max-nested-callbacks": [ 1, 5 ],
|
||||
// specify the maximum depth callbacks can be nested (off by default)
|
||||
"new-cap": [
|
||||
1,
|
||||
{
|
||||
"newIsCap": true,
|
||||
"capIsNew": false
|
||||
}
|
||||
],
|
||||
// require a capital letter for constructors
|
||||
"new-parens": 1,
|
||||
// disallow the omission of parentheses when invoking a constructor with no arguments
|
||||
"newline-after-var": 0,
|
||||
// allow/disallow an empty newline after var statement (off by default)
|
||||
"no-array-constructor": 1,
|
||||
// disallow use of the Array constructor
|
||||
"no-inline-comments": 0,
|
||||
// disallow comments inline after code (off by default)
|
||||
"no-lonely-if": 1,
|
||||
// disallow if as the only statement in an else block (off by default)
|
||||
"no-mixed-spaces-and-tabs": 1,
|
||||
// disallow mixed spaces and tabs for indentation
|
||||
"no-multiple-empty-lines": [
|
||||
1,
|
||||
{
|
||||
"max": 2
|
||||
}
|
||||
],
|
||||
// disallow multiple empty lines (off by default)
|
||||
"no-nested-ternary": 2,
|
||||
// disallow nested ternary expressions (off by default)
|
||||
"no-new-object": 1,
|
||||
// disallow use of the Object constructor
|
||||
"no-spaced-func": 1,
|
||||
// disallow space between function identifier and application
|
||||
"no-ternary": 0,
|
||||
// disallow the use of ternary operators (off by default)
|
||||
"no-unneeded-ternary": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
// disallow trailing whitespace at the end of lines
|
||||
"no-underscore-dangle": 0,
|
||||
// disallow dangling underscores in identifiers
|
||||
"prefer-reflect": 0,
|
||||
"one-var": [
|
||||
0,
|
||||
"never"
|
||||
],
|
||||
// allow just one var statement per function (off by default)
|
||||
"operator-assignment": [
|
||||
1,
|
||||
"never"
|
||||
],
|
||||
// require assignment operator shorthand where possible or prohibit it entirely (off by default)
|
||||
"padded-blocks": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
// enforce padding within blocks (off by default)
|
||||
"quote-props": [
|
||||
1,
|
||||
"as-needed"
|
||||
],
|
||||
// require quotes around object literal property names (off by default)
|
||||
"quotes": [
|
||||
2,
|
||||
"double"
|
||||
],
|
||||
// specify whether double or single quotes should be used
|
||||
"semi": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
// require or disallow use of semicolons instead of ASI
|
||||
"semi-spacing": [
|
||||
1,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
// enforce spacing before and after semicolons
|
||||
"sort-vars": 0,
|
||||
// sort variables within the same declaration block (off by default)
|
||||
"sort-imports": 0,
|
||||
"keyword-spacing": [
|
||||
1
|
||||
],
|
||||
// require a space after certain keywords (off by default)
|
||||
"space-before-blocks": [
|
||||
1,
|
||||
"always"
|
||||
],
|
||||
// require or disallow space before blocks (off by default)
|
||||
"space-before-function-paren": [
|
||||
0
|
||||
],
|
||||
// require or disallow space before function opening parenthesis (off by default)
|
||||
"object-curly-spacing": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"template-curly-spacing": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
0
|
||||
],
|
||||
"space-in-parens": [
|
||||
1,
|
||||
"never"
|
||||
],
|
||||
// require or disallow spaces inside parentheses (off by default)
|
||||
"space-infix-ops": 0,
|
||||
// require spaces around operators
|
||||
"space-unary-ops": [
|
||||
1,
|
||||
{
|
||||
"words": true,
|
||||
"nonwords": false
|
||||
}
|
||||
],
|
||||
// Require or disallow spaces before/after unary operators (words on by default, nonwords off by default)
|
||||
"spaced-comment": [
|
||||
1,
|
||||
"always"
|
||||
],
|
||||
// require or disallow a space immediately following the // in a line comment (off by default)
|
||||
"wrap-regex": 0,
|
||||
// require regex literals to be wrapped in parentheses (off by default)
|
||||
//
|
||||
// ECMAScript 6
|
||||
//
|
||||
// These rules are only relevant to ES6 environments and are off by default.
|
||||
//
|
||||
"no-var": 2,
|
||||
// require let or const instead of var (off by default)
|
||||
"generator-star-spacing": [
|
||||
2,
|
||||
"before"
|
||||
],
|
||||
// enforce the spacing around the * in generator functions (off by default)
|
||||
//
|
||||
// Legacy
|
||||
//
|
||||
// The following rules are included for compatibility with JSHint and JSLint.
|
||||
// While the names of the rules may not match up with the JSHint/JSLint counterpart,
|
||||
// the functionality is the same.
|
||||
//
|
||||
"max-depth": [
|
||||
2,
|
||||
3
|
||||
],
|
||||
// specify the maximum depth that blocks can be nested (off by default)
|
||||
"max-len": [
|
||||
0,
|
||||
180,
|
||||
2
|
||||
],
|
||||
// specify the maximum length of a line in your program (off by default)
|
||||
"max-params": [
|
||||
0
|
||||
],
|
||||
// limits the number of parameters that can be used in the function declaration. (off by default)
|
||||
"max-statements": 0,
|
||||
// specify the maximum number of statement allowed in a function (off by default)
|
||||
"no-bitwise": 0,
|
||||
// disallow use of bitwise operators (off by default)
|
||||
"no-plusplus": 0,
|
||||
// disallow use of unary operators, ++ and -- (off by default)
|
||||
//
|
||||
// eslint-plugin-react
|
||||
//
|
||||
// React specific linting rules for ESLint
|
||||
//
|
||||
"react/display-name": 0,
|
||||
// Prevent missing displayName in a React component definition
|
||||
"jsx-quotes": [
|
||||
2,
|
||||
"prefer-double"
|
||||
],
|
||||
"react/jsx-curly-spacing": [
|
||||
2,
|
||||
{
|
||||
"when": "always",
|
||||
"allowMultiline": false,
|
||||
"children": true
|
||||
}
|
||||
],
|
||||
// Enforce quote style for JSX attributes
|
||||
"react/jsx-no-undef": 2,
|
||||
// Disallow undeclared variables in JSX
|
||||
"react/jsx-sort-props": 0,
|
||||
// Enforce props alphabetical sorting
|
||||
"react/sort-comp": [
|
||||
2,
|
||||
{
|
||||
"order": [
|
||||
"constructor",
|
||||
"lifecycle",
|
||||
"everything-else",
|
||||
"render"
|
||||
]
|
||||
}
|
||||
],
|
||||
"react/jsx-uses-react": 2,
|
||||
// Prevent React to be incorrectly marked as unused
|
||||
"react/jsx-uses-vars": 2,
|
||||
// Prevent variables used in JSX to be incorrectly marked as unused
|
||||
//"react/no-did-mount-set-state": 2,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
// Prevent usage of setState in componentDidMount
|
||||
"react/no-did-update-set-state": 1,
|
||||
// Prevent usage of setState in componentDidUpdate
|
||||
"react/no-multi-comp": 0,
|
||||
// Prevent multiple component definition per file
|
||||
"react/no-unknown-property": 2,
|
||||
// Prevent usage of unknown DOM property
|
||||
"react/self-closing-comp": 2,
|
||||
// Prevent extra closing tags for components without children
|
||||
"react/jsx-wrap-multilines": 2,
|
||||
// Prevent missing parentheses around multilines JSX
|
||||
"react/forbid-prop-types": 0,
|
||||
"react-hooks/exhaustive-deps": 0
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,20 @@ import { Features } from "./blocks/features";
|
||||
import { Hero } from "./blocks/hero";
|
||||
import { Testimonial } from "./blocks/testimonial";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
import {Carousel} from "./blocks/carousel";
|
||||
import { Carousel } from "./blocks/carousel";
|
||||
|
||||
export const Blocks = (props: Omit<Page, "id" | "_sys" | "_values">) => {
|
||||
return (
|
||||
<>
|
||||
{props.blocks
|
||||
? props.blocks.map(function (block, i) {
|
||||
{ props.blocks
|
||||
? props.blocks.map((block, i) => {
|
||||
return (
|
||||
<div key={i} data-tina-field={tinaField(block)}>
|
||||
<Block {...block} />
|
||||
<div key={ i } data-tina-field={ tinaField(block) }>
|
||||
<Block { ...block } />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
: null }
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -25,15 +25,15 @@ export const Blocks = (props: Omit<Page, "id" | "_sys" | "_values">) => {
|
||||
const Block = (block: PageBlocks) => {
|
||||
switch (block.__typename) {
|
||||
case "PageBlocksContent":
|
||||
return <Content data={block} />;
|
||||
return <Content data={ block } />;
|
||||
case "PageBlocksHero":
|
||||
return <Hero data={block} />;
|
||||
return <Hero data={ block } />;
|
||||
case "PageBlocksFeatures":
|
||||
return <Features data={block} />;
|
||||
return <Features data={ block } />;
|
||||
case "PageBlocksTestimonial":
|
||||
return <Testimonial data={block} />;
|
||||
return <Testimonial data={ block } />;
|
||||
case "PageBlocksCarousel":
|
||||
return <Carousel data={block} />;
|
||||
return <Carousel data={ block } />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from "react";
|
||||
import { Section } from "../util/section";
|
||||
import {PageBlocksCarousel} from "../../tina/__generated__/types";
|
||||
import {Anchoring, anchoringSchema} from "../util/anchoring";
|
||||
import {Template} from "tinacms";
|
||||
import { PageBlocksCarousel } from "../../tina/__generated__/types";
|
||||
import { Anchoring, anchoringSchema } from "../util/anchoring";
|
||||
import { Template } from "tinacms";
|
||||
|
||||
export const Carousel = ({ data }: { data: PageBlocksCarousel }) => {
|
||||
return (
|
||||
@@ -20,17 +20,17 @@ export const carouselBlockSchema: Template = {
|
||||
label: "Carousel",
|
||||
ui: {
|
||||
previewSrc: "/blocks/features.png",
|
||||
defaultItem: [defaultCarousel, defaultCarousel, defaultCarousel],
|
||||
defaultItem: [defaultCarousel, defaultCarousel, defaultCarousel]
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "image",
|
||||
label: "Images du carousel",
|
||||
name: "images",
|
||||
list: true,
|
||||
list: true
|
||||
},
|
||||
{
|
||||
...anchoringSchema
|
||||
}
|
||||
],
|
||||
]
|
||||
};
|
||||
|
||||
@@ -2,22 +2,22 @@ import React from "react";
|
||||
import { Container } from "../util/container";
|
||||
import { Section } from "../util/section";
|
||||
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
||||
import type {Template, TinaTemplate} from "tinacms";
|
||||
import type { Template, TinaTemplate } from "tinacms";
|
||||
import { PageBlocksContent } from "../../tina/__generated__/types";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
|
||||
export const Content = ({ data }: { data: PageBlocksContent }) => {
|
||||
return (
|
||||
<Section color={data.color}>
|
||||
<Section color={ data.color }>
|
||||
<Container
|
||||
className={`prose prose-lg ${
|
||||
data.color === "primary" ? `prose-primary` : `dark:prose-dark`
|
||||
}`}
|
||||
data-tina-field={tinaField(data, "body")}
|
||||
className={ `prose prose-lg ${
|
||||
data.color === "primary" ? "prose-primary" : "dark:prose-dark"
|
||||
}` }
|
||||
data-tina-field={ tinaField(data, "body") }
|
||||
size="large"
|
||||
width="medium"
|
||||
>
|
||||
<TinaMarkdown content={data.body} />
|
||||
<TinaMarkdown content={ data.body } />
|
||||
</Container>
|
||||
</Section>
|
||||
);
|
||||
@@ -29,14 +29,14 @@ export const contentBlockSchema: Template = {
|
||||
ui: {
|
||||
previewSrc: "/blocks/content.png",
|
||||
defaultItem: {
|
||||
body: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio. Quisque volutpat mattis eros. Nullam malesuada erat ut turpis. Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede.",
|
||||
},
|
||||
body: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec odio. Quisque volutpat mattis eros. Nullam malesuada erat ut turpis. Suspendisse urna nibh, viverra non, semper suscipit, posuere a, pede."
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "rich-text",
|
||||
label: "Body",
|
||||
name: "body",
|
||||
name: "body"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
@@ -45,8 +45,8 @@ export const contentBlockSchema: Template = {
|
||||
options: [
|
||||
{ label: "Default", value: "default" },
|
||||
{ label: "Tint", value: "tint" },
|
||||
{ label: "Primary", value: "primary" },
|
||||
],
|
||||
},
|
||||
],
|
||||
{ label: "Primary", value: "primary" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -4,62 +4,62 @@ import { Icon } from "../util/icon";
|
||||
import { iconSchema } from "../util/icon";
|
||||
import {
|
||||
PageBlocksFeatures,
|
||||
PageBlocksFeaturesItems,
|
||||
PageBlocksFeaturesItems
|
||||
} from "../../tina/__generated__/types";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
import {Template} from "tinacms";
|
||||
import { Template } from "tinacms";
|
||||
|
||||
export const Feature = ({
|
||||
featuresColor,
|
||||
data,
|
||||
data
|
||||
}: {
|
||||
featuresColor: string;
|
||||
data: PageBlocksFeaturesItems;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
data-tina-field={tinaField(data)}
|
||||
data-tina-field={ tinaField(data) }
|
||||
className="flex-1 flex flex-col gap-6 text-center items-center lg:items-start lg:text-left max-w-xl mx-auto"
|
||||
style={{ flexBasis: "16rem" }}
|
||||
style={ { flexBasis: "16rem" } }
|
||||
>
|
||||
{data.icon && (
|
||||
{ data.icon && (
|
||||
<Icon
|
||||
tinaField={tinaField(data, "icon")}
|
||||
parentColor={featuresColor}
|
||||
data={{ size: "large", ...data.icon }}
|
||||
tinaField={ tinaField(data, "icon") }
|
||||
parentColor={ featuresColor }
|
||||
data={ { size: "large", ...data.icon } }
|
||||
/>
|
||||
)}
|
||||
{data.title && (
|
||||
) }
|
||||
{ data.title && (
|
||||
<h3
|
||||
data-tina-field={tinaField(data, "title")}
|
||||
data-tina-field={ tinaField(data, "title") }
|
||||
className="text-2xl font-semibold title-font"
|
||||
>
|
||||
{data.title}
|
||||
{ data.title }
|
||||
</h3>
|
||||
)}
|
||||
{data.text && (
|
||||
) }
|
||||
{ data.text && (
|
||||
<p
|
||||
data-tina-field={tinaField(data, "text")}
|
||||
data-tina-field={ tinaField(data, "text") }
|
||||
className="text-base opacity-80 leading-relaxed"
|
||||
>
|
||||
{data.text}
|
||||
{ data.text }
|
||||
</p>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Features = ({ data }: { data: PageBlocksFeatures }) => {
|
||||
return (
|
||||
<Section color={data.color}>
|
||||
<Section color={ data.color }>
|
||||
<Container
|
||||
className={`flex flex-wrap gap-x-10 gap-y-8 text-left`}
|
||||
className={ "flex flex-wrap gap-x-10 gap-y-8 text-left" }
|
||||
size="large"
|
||||
>
|
||||
{data.items &&
|
||||
data.items.map(function (block, i) {
|
||||
return <Feature featuresColor={data.color} key={i} data={block} />;
|
||||
})}
|
||||
{ data.items &&
|
||||
data.items.map((block, i) => {
|
||||
return <Feature featuresColor={ data.color } key={ i } data={ block } />;
|
||||
}) }
|
||||
</Container>
|
||||
</Section>
|
||||
);
|
||||
@@ -71,8 +71,8 @@ const defaultFeature = {
|
||||
icon: {
|
||||
color: "",
|
||||
style: "float",
|
||||
name: "",
|
||||
},
|
||||
name: ""
|
||||
}
|
||||
};
|
||||
|
||||
export const featureBlockSchema: Template = {
|
||||
@@ -81,8 +81,8 @@ export const featureBlockSchema: Template = {
|
||||
ui: {
|
||||
previewSrc: "/blocks/features.png",
|
||||
defaultItem: {
|
||||
items: [defaultFeature, defaultFeature, defaultFeature],
|
||||
},
|
||||
items: [defaultFeature, defaultFeature, defaultFeature]
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -93,29 +93,29 @@ export const featureBlockSchema: Template = {
|
||||
ui: {
|
||||
itemProps: (item) => {
|
||||
return {
|
||||
label: item?.title,
|
||||
label: item?.title
|
||||
};
|
||||
},
|
||||
defaultItem: {
|
||||
...defaultFeature,
|
||||
},
|
||||
...defaultFeature
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
iconSchema,
|
||||
{
|
||||
type: "string",
|
||||
label: "Title",
|
||||
name: "title",
|
||||
name: "title"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Text",
|
||||
name: "text",
|
||||
ui: {
|
||||
component: "textarea",
|
||||
},
|
||||
},
|
||||
],
|
||||
component: "textarea"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
@@ -124,8 +124,8 @@ export const featureBlockSchema: Template = {
|
||||
options: [
|
||||
{ label: "Default", value: "default" },
|
||||
{ label: "Tint", value: "tint" },
|
||||
{ label: "Primary", value: "primary" },
|
||||
],
|
||||
},
|
||||
],
|
||||
{ label: "Primary", value: "primary" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Actions } from "../util/actions";
|
||||
import { Container } from "../util/container";
|
||||
import { Section } from "../util/section";
|
||||
import { TinaMarkdown } from "tinacms/dist/rich-text";
|
||||
import type {Template, TinaTemplate} from "tinacms";
|
||||
import type { Template, TinaTemplate } from "tinacms";
|
||||
import { PageBlocksHero } from "../../tina/__generated__/types";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
|
||||
@@ -16,70 +16,70 @@ export const Hero = ({ data }: { data: PageBlocksHero }) => {
|
||||
pink: "from-pink-400 to-pink-600",
|
||||
purple: "from-purple-400 to-purple-600",
|
||||
orange: "from-orange-300 to-orange-600",
|
||||
yellow: "from-yellow-400 to-yellow-600",
|
||||
yellow: "from-yellow-400 to-yellow-600"
|
||||
};
|
||||
|
||||
return (
|
||||
<Section color={data.color}>
|
||||
<Section color={ data.color }>
|
||||
<Container
|
||||
size="large"
|
||||
className="grid grid-cols-1 md:grid-cols-5 gap-14 items-center justify-center"
|
||||
>
|
||||
<div className="row-start-2 md:row-start-1 md:col-span-3 text-center md:text-left">
|
||||
{data.tagline && (
|
||||
{ data.tagline && (
|
||||
<h2
|
||||
data-tina-field={tinaField(data, "tagline")}
|
||||
data-tina-field={ tinaField(data, "tagline") }
|
||||
className="relative inline-block px-3 py-1 mb-8 text-md font-bold tracking-wide title-font z-20"
|
||||
>
|
||||
{data.tagline}
|
||||
<span className="absolute w-full h-full left-0 top-0 rounded-full -z-1 bg-current opacity-7"></span>
|
||||
{ data.tagline }
|
||||
<span className="absolute w-full h-full left-0 top-0 rounded-full -z-1 bg-current opacity-7" />
|
||||
</h2>
|
||||
)}
|
||||
{data.headline && (
|
||||
) }
|
||||
{ data.headline && (
|
||||
<h3
|
||||
data-tina-field={tinaField(data, "headline")}
|
||||
className={`w-full relative mb-10 text-5xl font-extrabold tracking-normal leading-tight title-font`}
|
||||
data-tina-field={ tinaField(data, "headline") }
|
||||
className={ "w-full relative mb-10 text-5xl font-extrabold tracking-normal leading-tight title-font" }
|
||||
>
|
||||
<span
|
||||
className={"bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-100"}
|
||||
className={ "bg-clip-text text-transparent bg-gradient-to-r from-white to-gray-100" }
|
||||
>
|
||||
{data.headline}
|
||||
{ data.headline }
|
||||
</span>
|
||||
</h3>
|
||||
)}
|
||||
{data.text && (
|
||||
) }
|
||||
{ data.text && (
|
||||
<div
|
||||
data-tina-field={tinaField(data, "text")}
|
||||
className={"prose prose-lg mx-auto md:mx-0 mb-10 prose-primary" }
|
||||
data-tina-field={ tinaField(data, "text") }
|
||||
className={ "prose prose-lg mx-auto md:mx-0 mb-10 prose-primary" }
|
||||
>
|
||||
<TinaMarkdown content={data.text} />
|
||||
<TinaMarkdown content={ data.text } />
|
||||
</div>
|
||||
)}
|
||||
{data.actions && (
|
||||
) }
|
||||
{ data.actions && (
|
||||
<Actions
|
||||
className="justify-center md:justify-start py-2"
|
||||
parentColor={data.color}
|
||||
actions={data.actions}
|
||||
parentColor={ data.color }
|
||||
actions={ data.actions }
|
||||
/>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
{data.image && (
|
||||
{ data.image && (
|
||||
<div
|
||||
data-tina-field={tinaField(data.image, "src")}
|
||||
data-tina-field={ tinaField(data.image, "src") }
|
||||
className="relative row-start-1 md:col-span-2 flex justify-center"
|
||||
>
|
||||
<img
|
||||
className="absolute w-full rounded-lg max-w-xs md:max-w-none h-auto blur-2xl brightness-150 contrast-[0.9] dark:brightness-150 saturate-200 opacity-50 dark:opacity-30 mix-blend-multiply dark:mix-blend-hard-light"
|
||||
src={data.image.src}
|
||||
src={ data.image.src }
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<img
|
||||
className="relative z-10 w-full max-w-xs rounded-lg md:max-w-none h-auto"
|
||||
alt={data.image.alt}
|
||||
src={data.image.src}
|
||||
alt={ data.image.alt }
|
||||
src={ data.image.src }
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
) }
|
||||
</Container>
|
||||
</Section>
|
||||
);
|
||||
@@ -93,24 +93,24 @@ export const heroBlockSchema: Template = {
|
||||
defaultItem: {
|
||||
tagline: "Here's some text above the other text",
|
||||
headline: "This Big Text is Totally Awesome",
|
||||
text: "Phasellus scelerisque, libero eu finibus rutrum, risus risus accumsan libero, nec molestie urna dui a leo.",
|
||||
},
|
||||
text: "Phasellus scelerisque, libero eu finibus rutrum, risus risus accumsan libero, nec molestie urna dui a leo."
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Tagline",
|
||||
name: "tagline",
|
||||
name: "tagline"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Headline",
|
||||
name: "headline",
|
||||
name: "headline"
|
||||
},
|
||||
{
|
||||
label: "Text",
|
||||
name: "text",
|
||||
type: "rich-text",
|
||||
type: "rich-text"
|
||||
},
|
||||
{
|
||||
label: "Actions",
|
||||
@@ -122,15 +122,15 @@ export const heroBlockSchema: Template = {
|
||||
label: "Action Label",
|
||||
type: "button",
|
||||
icon: true,
|
||||
link: "/",
|
||||
link: "/"
|
||||
},
|
||||
itemProps: (item) => ({ label: item.label }),
|
||||
itemProps: (item) => ({ label: item.label })
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
label: "Label",
|
||||
name: "label",
|
||||
type: "string",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
label: "Type",
|
||||
@@ -138,20 +138,20 @@ export const heroBlockSchema: Template = {
|
||||
type: "string",
|
||||
options: [
|
||||
{ label: "Button", value: "button" },
|
||||
{ label: "Link", value: "link" },
|
||||
],
|
||||
{ label: "Link", value: "link" }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Icon",
|
||||
name: "icon",
|
||||
type: "boolean",
|
||||
type: "boolean"
|
||||
},
|
||||
{
|
||||
label: "Link",
|
||||
name: "link",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
@@ -161,14 +161,14 @@ export const heroBlockSchema: Template = {
|
||||
{
|
||||
name: "src",
|
||||
label: "Image Source",
|
||||
type: "image",
|
||||
type: "image"
|
||||
},
|
||||
{
|
||||
name: "alt",
|
||||
label: "Alt Text",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
@@ -177,8 +177,8 @@ export const heroBlockSchema: Template = {
|
||||
options: [
|
||||
{ label: "Default", value: "default" },
|
||||
{ label: "Tint", value: "tint" },
|
||||
{ label: "Primary", value: "primary" },
|
||||
],
|
||||
},
|
||||
],
|
||||
{ label: "Primary", value: "primary" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { Container } from "../util/container";
|
||||
import { Section } from "../util/section";
|
||||
import type {Template, TinaTemplate} from "tinacms";
|
||||
import type { Template } from "tinacms";
|
||||
import { PageBlocksTestimonial } from "../../tina/__generated__/types";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { FaFacebookF, FaGithub, FaTwitter } from "react-icons/fa";
|
||||
import { AiFillInstagram } from "react-icons/ai";
|
||||
import { Container } from "../../util/container";
|
||||
import { RawRenderer } from "./rawRenderer";
|
||||
import { ObjectField } from "@tinacms/schema-tools/dist/types";
|
||||
|
||||
export const Footer = ({ data, rawData }) => {
|
||||
const socialIconClasses = "h-7 w-auto";
|
||||
@@ -16,7 +17,7 @@ export const Footer = ({ data, rawData }) => {
|
||||
purple: "text-purple-500 dark:text-purple-400 hover:text-purple-300",
|
||||
orange: "text-orange-500 dark:text-orange-400 hover:text-orange-300",
|
||||
yellow: "text-yellow-500 dark:text-yellow-400 hover:text-yellow-300",
|
||||
primary: "text-white opacity-80 hover:opacity-100",
|
||||
primary: "text-white opacity-80 hover:opacity-100"
|
||||
};
|
||||
|
||||
const footerColor = {
|
||||
@@ -30,83 +31,116 @@ export const Footer = ({ data, rawData }) => {
|
||||
pink: "text-white from-pink-500 to-pink-600",
|
||||
purple: "text-white from-purple-500 to-purple-600",
|
||||
orange: "text-white from-orange-500 to-orange-600",
|
||||
yellow: "text-white from-yellow-500 to-yellow-600",
|
||||
},
|
||||
yellow: "text-white from-yellow-500 to-yellow-600"
|
||||
}
|
||||
};
|
||||
|
||||
const footerColorCss = footerColor.default;
|
||||
|
||||
return (
|
||||
<footer className={`bg-gradient-to-br ${footerColorCss}`}>
|
||||
<footer className={ `bg-gradient-to-br ${ footerColorCss }` }>
|
||||
<Container className="relative" size="small">
|
||||
<div className="flex justify-between items-center gap-6 flex-wrap">
|
||||
<Link
|
||||
href="/"
|
||||
className="group mx-2 flex items-center font-bold tracking-tight text-gray-400 dark:text-gray-300 opacity-50 hover:opacity-100 transition duration-150 ease-out whitespace-nowrap"
|
||||
>
|
||||
</Link>
|
||||
/>
|
||||
<div className="flex gap-4">
|
||||
{data.social && data.social.facebook && (
|
||||
{ data.social && data.social.facebook && (
|
||||
<a
|
||||
className="inline-block opacity-80 hover:opacity-100 transition ease-out duration-150"
|
||||
href={data.social.facebook}
|
||||
href={ data.social.facebook }
|
||||
target="_blank"
|
||||
>
|
||||
<FaFacebookF
|
||||
className={`${socialIconClasses} ${
|
||||
socialIconColorClasses["primary"]
|
||||
}`}
|
||||
className={ `${ socialIconClasses } ${
|
||||
socialIconColorClasses.primary
|
||||
}` }
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
{data.social && data.social.twitter && (
|
||||
) }
|
||||
{ data.social && data.social.twitter && (
|
||||
<a
|
||||
className="inline-block opacity-80 hover:opacity-100 transition ease-out duration-150"
|
||||
href={data.social.twitter}
|
||||
href={ data.social.twitter }
|
||||
target="_blank"
|
||||
>
|
||||
<FaTwitter
|
||||
className={`${socialIconClasses} ${
|
||||
socialIconColorClasses["primary"]
|
||||
}`}
|
||||
className={ `${ socialIconClasses } ${
|
||||
socialIconColorClasses.primary
|
||||
}` }
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
{data.social && data.social.instagram && (
|
||||
) }
|
||||
{ data.social && data.social.instagram && (
|
||||
<a
|
||||
className="inline-block opacity-80 hover:opacity-100 transition ease-out duration-150"
|
||||
href={data.social.instagram}
|
||||
href={ data.social.instagram }
|
||||
target="_blank"
|
||||
>
|
||||
<AiFillInstagram
|
||||
className={`${socialIconClasses} ${
|
||||
socialIconColorClasses["primary"]
|
||||
}`}
|
||||
className={ `${ socialIconClasses } ${
|
||||
socialIconColorClasses.primary
|
||||
}` }
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
{data.social && data.social.github && (
|
||||
) }
|
||||
{ data.social && data.social.github && (
|
||||
<a
|
||||
className="inline-block opacity-80 hover:opacity-100 transition ease-out duration-150"
|
||||
href={data.social.github}
|
||||
href={ data.social.github }
|
||||
target="_blank"
|
||||
>
|
||||
<FaGithub
|
||||
className={`${socialIconClasses} ${
|
||||
socialIconColorClasses["primary"]
|
||||
}`}
|
||||
className={ `${ socialIconClasses } ${
|
||||
socialIconColorClasses.primary
|
||||
}` }
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
<RawRenderer parentColor={data.color} rawData={rawData} />
|
||||
<RawRenderer parentColor={ data.color } rawData={ rawData } />
|
||||
</div>
|
||||
<div
|
||||
className={`absolute h-1 bg-gradient-to-r from-transparent ${
|
||||
data.color === "primary" ? `via-white` : `via-black dark:via-white`
|
||||
} to-transparent top-0 left-4 right-4 opacity-5`}
|
||||
></div>
|
||||
className={ `absolute h-1 bg-gradient-to-r from-transparent ${
|
||||
data.color === "primary" ? "via-white" : "via-black dark:via-white"
|
||||
} to-transparent top-0 left-4 right-4 opacity-5` }
|
||||
/>
|
||||
</Container>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export const footerSchema: ObjectField = {
|
||||
type: "object",
|
||||
label: "Footer",
|
||||
name: "footer",
|
||||
fields: [
|
||||
{
|
||||
type: "object",
|
||||
label: "Social Links",
|
||||
name: "social",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Facebook",
|
||||
name: "facebook"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Twitter",
|
||||
name: "twitter"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Instagram",
|
||||
name: "instagram"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Github",
|
||||
name: "github"
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
@@ -11,7 +11,7 @@ export const RawRenderer = ({ rawData, parentColor }) => {
|
||||
pink: "text-pink-500",
|
||||
purple: "text-purple-500",
|
||||
orange: "text-orange-500",
|
||||
yellow: "text-yellow-600",
|
||||
yellow: "text-yellow-600"
|
||||
};
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -27,29 +27,29 @@ export const RawRenderer = ({ rawData, parentColor }) => {
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={openModal}
|
||||
className={`z-10 relative flex items-center px-5 py-2 mx-3 my-2 font-semibold shadow-sm text-sm transition duration-150 ease-out rounded transform focus:shadow-outline focus:outline-none whitespace-nowrap opacity-80 hover:opacity-100 shadow-md ${
|
||||
onClick={ openModal }
|
||||
className={ `z-10 relative flex items-center px-5 py-2 mx-3 my-2 font-semibold shadow-sm text-sm transition duration-150 ease-out rounded transform focus:shadow-outline focus:outline-none whitespace-nowrap opacity-80 hover:opacity-100 shadow-md ${
|
||||
buttonColorClasses[buttonColorClasses.blue]
|
||||
}`}
|
||||
}` }
|
||||
>
|
||||
View Raw Data
|
||||
<span
|
||||
className={`absolute w-full h-full left-0 top-0 rounded -z-1 ${
|
||||
className={ `absolute w-full h-full left-0 top-0 rounded -z-1 ${
|
||||
parentColor === "primary"
|
||||
? `bg-white opacity-80`
|
||||
: `bg-current opacity-15`
|
||||
}`}
|
||||
></span>
|
||||
? "bg-white opacity-80"
|
||||
: "bg-current opacity-15"
|
||||
}` }
|
||||
/>
|
||||
</button>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Transition appear show={ isOpen } as={ Fragment }>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="fixed inset-0 z-10 overflow-y-auto"
|
||||
onClose={closeModal}
|
||||
onClose={ closeModal }
|
||||
>
|
||||
<div className="min-h-screen max-h-screen px-4 py-12 text-center flex flex-col items-center justify-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
as={ Fragment }
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
@@ -63,7 +63,7 @@ export const RawRenderer = ({ rawData, parentColor }) => {
|
||||
</Transition.Child>
|
||||
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
as={ Fragment }
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
@@ -73,12 +73,12 @@ export const RawRenderer = ({ rawData, parentColor }) => {
|
||||
>
|
||||
<div className="flex-1 w-full prose dark:prose-dark max-w-3xl p-6 overflow-hidden text-left align-middle transition-all transform bg-white dark:bg-gray-1000 shadow-xl rounded-xl inline-flex flex-col max-h-full">
|
||||
<pre className="flex-1 overflow-y-auto">
|
||||
<code>{JSON.stringify(rawData, null, 2)}</code>
|
||||
<code>{ JSON.stringify(rawData, null, 2) }</code>
|
||||
</pre>
|
||||
<button
|
||||
type="button"
|
||||
className="flex-0 font-semibold text-lg transition duration-150 ease-out opacity-80 hover:opacity-100"
|
||||
onClick={closeModal}
|
||||
onClick={ closeModal }
|
||||
>
|
||||
Great, thanks!
|
||||
</button>
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from "react";
|
||||
import Link from "next/link";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
import defaultLogo from "../../public/logo.png";
|
||||
import {GlobalHeader} from "../../tina/__generated__/types";
|
||||
import { GlobalHeader } from "../../tina/__generated__/types";
|
||||
import { ObjectField } from "@tinacms/schema-tools/dist/types";
|
||||
|
||||
export const Header = ({ data }: { data: GlobalHeader }) => {
|
||||
return (
|
||||
@@ -15,42 +16,102 @@ export const Header = ({ data }: { data: GlobalHeader }) => {
|
||||
>
|
||||
<img src={ data.logoSrc || defaultLogo.src }/>
|
||||
<div className="flex flex-col px-6">
|
||||
<h3 className="title" data-tina-field={tinaField(data, "title")}>{data.title}</h3>
|
||||
<div className="subtitle" data-tina-field={tinaField(data, "subtitle")}>{data.subtitle}</div>
|
||||
<h3 className="title" data-tina-field={ tinaField(data, "title") }>{ data.title }</h3>
|
||||
<div className="subtitle" data-tina-field={ tinaField(data, "subtitle") }>{ data.subtitle }</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<button className="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span className="navbar-toggler-icon"></span>
|
||||
<span className="navbar-toggler-icon" />
|
||||
</button>
|
||||
<div className={`flex items-center` } id="navbarSupportedContent">
|
||||
<div className={ "flex items-center" } id="navbarSupportedContent">
|
||||
<ul className="flex md:flex-row">
|
||||
{data.nav &&
|
||||
{ data.nav &&
|
||||
data.nav.map((item, i) => {
|
||||
/*const activeItem =
|
||||
/* const activeItem =
|
||||
(item.href === ""
|
||||
? router.asPath === "/"
|
||||
: router.asPath.includes(item.href)) && isClient;*/
|
||||
return (
|
||||
<li
|
||||
key={`${item.label}-${i}`}
|
||||
key={ `${ item.label }-${ i }` }
|
||||
className="nav-item"
|
||||
>
|
||||
<Link
|
||||
data-tina-field={tinaField(item, "label")}
|
||||
href={`${ item.external? "" : "/" }${ item.href }`}
|
||||
className={`nav-link p-2 ${ item.external ? "external-link-icon" : ""}`}
|
||||
data-tina-field={ tinaField(item, "label") }
|
||||
href={ `${ item.external? "" : "/" }${ item.href }` }
|
||||
className={ `nav-link p-2 ${ item.external ? "external-link-icon" : "" }` }
|
||||
>
|
||||
{item.label}
|
||||
{ item.label }
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export const headerSchema: ObjectField = {
|
||||
type: "object",
|
||||
label: "Header",
|
||||
name: "header",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Page Title",
|
||||
name: "pageTitle"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Title",
|
||||
name: "title"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Subtitle",
|
||||
name: "subtitle"
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
label: "Logo",
|
||||
name: "logoSrc"
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
label: "Nav Links",
|
||||
name: "nav",
|
||||
list: true,
|
||||
ui: {
|
||||
itemProps: (item) => {
|
||||
return { label: item?.label };
|
||||
},
|
||||
defaultItem: {
|
||||
href: "home",
|
||||
label: "Home",
|
||||
external: false
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Link",
|
||||
name: "href"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Label",
|
||||
name: "label"
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
label: "External",
|
||||
name: "external"
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import { Global } from "../../tina/__generated__/types";
|
||||
export const Layout = ({
|
||||
rawData = {},
|
||||
data = layoutData,
|
||||
children,
|
||||
children
|
||||
}: {
|
||||
rawData?: object;
|
||||
data?: Omit<Global, "id" | "_sys" | "_values">;
|
||||
@@ -17,7 +17,7 @@ export const Layout = ({
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{data?.header?.pageTitle}</title>
|
||||
<title>{ data?.header?.pageTitle }</title>
|
||||
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
@@ -28,15 +28,15 @@ export const Layout = ({
|
||||
/>
|
||||
</Head>
|
||||
<div
|
||||
className={"min-h-screen flex flex-col font-nunito" }
|
||||
className={ "min-h-screen flex flex-col font-nunito" }
|
||||
>
|
||||
<Header data={data?.header} />
|
||||
<Header data={ data?.header } />
|
||||
<div className="flex-1 text-gray-800 bg-gradient-to-br from-white to-gray-50 dark:from-gray-900 dark:to-gray-1000 flex flex-col">
|
||||
{children}
|
||||
{ children }
|
||||
</div>
|
||||
<Footer
|
||||
rawData={rawData}
|
||||
data={data?.footer}
|
||||
rawData={ rawData }
|
||||
data={ data?.footer }
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -36,7 +36,7 @@ const components: Components<{
|
||||
disclaimer?: TinaMarkdownContent;
|
||||
};
|
||||
}> = {
|
||||
code_block: (props) => <Prism {...props} />,
|
||||
code_block: (props) => <Prism { ...props } />,
|
||||
BlockQuote: (props: {
|
||||
children: TinaMarkdownContent;
|
||||
authorName: string;
|
||||
@@ -44,8 +44,8 @@ const components: Components<{
|
||||
return (
|
||||
<div>
|
||||
<blockquote>
|
||||
<TinaMarkdown content={props.children} />
|
||||
{props.authorName}
|
||||
<TinaMarkdown content={ props.children } />
|
||||
{ props.authorName }
|
||||
</blockquote>
|
||||
</div>
|
||||
);
|
||||
@@ -57,13 +57,13 @@ const components: Components<{
|
||||
|
||||
switch (props.format) {
|
||||
case "iso":
|
||||
return <span>{format(dt, "yyyy-MM-dd")}</span>;
|
||||
return <span>{ format(dt, "yyyy-MM-dd") }</span>;
|
||||
case "utc":
|
||||
return <span>{format(dt, "eee, dd MMM yyyy HH:mm:ss OOOO")}</span>;
|
||||
return <span>{ format(dt, "eee, dd MMM yyyy HH:mm:ss OOOO") }</span>;
|
||||
case "local":
|
||||
return <span>{format(dt, "P")}</span>;
|
||||
return <span>{ format(dt, "P") }</span>;
|
||||
default:
|
||||
return <span>{format(dt, "P")}</span>;
|
||||
return <span>{ format(dt, "P") }</span>;
|
||||
}
|
||||
},
|
||||
NewsletterSignup: (props) => {
|
||||
@@ -71,7 +71,7 @@ const components: Components<{
|
||||
<div className="bg-white">
|
||||
<div className="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<TinaMarkdown content={props.children} />
|
||||
<TinaMarkdown content={ props.children } />
|
||||
</div>
|
||||
<div className="mt-8 ">
|
||||
<form className="sm:flex">
|
||||
@@ -85,19 +85,19 @@ const components: Components<{
|
||||
autoComplete="email"
|
||||
required
|
||||
className="w-full px-5 py-3 border border-gray-300 shadow-sm placeholder-gray-400 focus:ring-1 focus:ring-teal-500 focus:border-teal-500 sm:max-w-xs rounded-md"
|
||||
placeholder={props.placeholder}
|
||||
placeholder={ props.placeholder }
|
||||
/>
|
||||
<div className="mt-3 rounded-md shadow sm:mt-0 sm:ml-3 sm:flex-shrink-0">
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full flex items-center justify-center py-3 px-5 border border-transparent text-base font-medium rounded-md text-white bg-teal-600 hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500"
|
||||
>
|
||||
{props.buttonText}
|
||||
{ props.buttonText }
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="mt-3 text-sm text-gray-500">
|
||||
{props.disclaimer && <TinaMarkdown content={props.disclaimer} />}
|
||||
{ props.disclaimer && <TinaMarkdown content={ props.disclaimer } /> }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -106,9 +106,9 @@ const components: Components<{
|
||||
},
|
||||
img: (props) => (
|
||||
<span className="flex items-center justify-center">
|
||||
<img src={props.url} alt={props.alt} />
|
||||
<img src={ props.url } alt={ props.alt } />
|
||||
</span>
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
export const Post = (props: PostType) => {
|
||||
@@ -123,7 +123,7 @@ export const Post = (props: PostType) => {
|
||||
orange:
|
||||
"from-orange-300 to-orange-600 dark:from-orange-200 dark:to-orange-500",
|
||||
yellow:
|
||||
"from-yellow-400 to-yellow-500 dark:from-yellow-300 dark:to-yellow-500",
|
||||
"from-yellow-400 to-yellow-500 dark:from-yellow-300 dark:to-yellow-500"
|
||||
};
|
||||
|
||||
const date = new Date(props.date);
|
||||
@@ -134,75 +134,75 @@ export const Post = (props: PostType) => {
|
||||
|
||||
return (
|
||||
<Section className="flex-1">
|
||||
<Container width="small" className={`flex-1 pb-2`} size="large">
|
||||
<Container width="small" className={ "flex-1 pb-2" } size="large">
|
||||
<h2
|
||||
data-tina-field={tinaField(props, "title")}
|
||||
className={`w-full relative mb-8 text-6xl font-extrabold tracking-normal text-center title-font`}
|
||||
data-tina-field={ tinaField(props, "title") }
|
||||
className={ "w-full relative mb-8 text-6xl font-extrabold tracking-normal text-center title-font" }
|
||||
>
|
||||
<span
|
||||
className={`bg-clip-text text-transparent bg-gradient-to-r`}
|
||||
className={ "bg-clip-text text-transparent bg-gradient-to-r" }
|
||||
>
|
||||
{props.title}
|
||||
{ props.title }
|
||||
</span>
|
||||
</h2>
|
||||
<div
|
||||
data-tina-field={tinaField(props, "author")}
|
||||
data-tina-field={ tinaField(props, "author") }
|
||||
className="flex items-center justify-center mb-16"
|
||||
>
|
||||
{props.author && (
|
||||
{ props.author && (
|
||||
<>
|
||||
<div className="flex-shrink-0 mr-4">
|
||||
<img
|
||||
data-tina-field={tinaField(props.author, "avatar")}
|
||||
data-tina-field={ tinaField(props.author, "avatar") }
|
||||
className="h-14 w-14 object-cover rounded-full shadow-sm"
|
||||
src={props.author.avatar}
|
||||
alt={props.author.name}
|
||||
src={ props.author.avatar }
|
||||
alt={ props.author.name }
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
data-tina-field={tinaField(props.author, "name")}
|
||||
data-tina-field={ tinaField(props.author, "name") }
|
||||
className="text-base font-medium text-gray-600 group-hover:text-gray-800 dark:text-gray-200 dark:group-hover:text-white"
|
||||
>
|
||||
{props.author.name}
|
||||
{ props.author.name }
|
||||
</p>
|
||||
<span className="font-bold text-gray-200 dark:text-gray-500 mx-2">
|
||||
—
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
) }
|
||||
<p
|
||||
data-tina-field={tinaField(props, "date")}
|
||||
data-tina-field={ tinaField(props, "date") }
|
||||
className="text-base text-gray-400 group-hover:text-gray-500 dark:text-gray-300 dark:group-hover:text-gray-150"
|
||||
>
|
||||
{formattedDate}
|
||||
{ formattedDate }
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
{props.heroImg && (
|
||||
{ props.heroImg && (
|
||||
<div className="px-4 w-full">
|
||||
<div
|
||||
data-tina-field={tinaField(props, "heroImg")}
|
||||
data-tina-field={ tinaField(props, "heroImg") }
|
||||
className="relative max-w-4xl lg:max-w-5xl mx-auto"
|
||||
>
|
||||
<img
|
||||
src={props.heroImg}
|
||||
src={ props.heroImg }
|
||||
className="absolute block rounded-lg w-full h-auto blur-2xl brightness-150 contrast-[0.9] dark:brightness-150 saturate-200 opacity-50 dark:opacity-30 mix-blend-multiply dark:mix-blend-hard-light"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<img
|
||||
src={props.heroImg}
|
||||
alt={props.title}
|
||||
src={ props.heroImg }
|
||||
alt={ props.title }
|
||||
className="relative z-10 mb-14 block rounded-lg w-full h-auto opacity-100"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Container className={`flex-1 pt-4`} width="small" size="large">
|
||||
) }
|
||||
<Container className={ "flex-1 pt-4" } width="small" size="large">
|
||||
<div
|
||||
data-tina-field={tinaField(props, "_body")}
|
||||
data-tina-field={ tinaField(props, "_body") }
|
||||
className="prose dark:prose-dark w-full max-w-none"
|
||||
>
|
||||
<TinaMarkdown components={components} content={props._body} />
|
||||
<TinaMarkdown components={ components } content={ props._body } />
|
||||
</div>
|
||||
</Container>
|
||||
</Section>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PostsType } from "../../pages/posts";
|
||||
export const Posts = ({ data }: { data: PostsType[] }) => {
|
||||
return (
|
||||
<>
|
||||
{data.map((postData) => {
|
||||
{ data.map((postData) => {
|
||||
const post = postData.node;
|
||||
const date = new Date(post.date);
|
||||
let formattedDate = "";
|
||||
@@ -17,46 +17,46 @@ export const Posts = ({ data }: { data: PostsType[] }) => {
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
key={post._sys.filename}
|
||||
href={`/posts/` + post._sys.filename}
|
||||
key={ post._sys.filename }
|
||||
href={ "/posts/" + post._sys.filename }
|
||||
className="group block px-6 sm:px-8 md:px-10 py-10 mb-8 last:mb-0 bg-gray-50 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-1000 rounded-md shadow-sm transition-all duration-150 ease-out hover:shadow-md hover:to-gray-50 dark:hover:to-gray-800"
|
||||
>
|
||||
<h3
|
||||
className={`text-gray-700 dark:text-white text-3xl lg:text-4xl font-semibold title-font mb-5 transition-all duration-150 ease-out`}
|
||||
className={ "text-gray-700 dark:text-white text-3xl lg:text-4xl font-semibold title-font mb-5 transition-all duration-150 ease-out" }
|
||||
>
|
||||
{post.title}{" "}
|
||||
{ post.title }{ " " }
|
||||
<span className="inline-block opacity-0 group-hover:opacity-100 transition-all duration-300 ease-out">
|
||||
<BsArrowRight className="inline-block h-8 -mt-1 ml-1 w-auto opacity-70" />
|
||||
</span>
|
||||
</h3>
|
||||
<div className="prose dark:prose-dark w-full max-w-none mb-5 opacity-70">
|
||||
<TinaMarkdown content={post.excerpt} />
|
||||
<TinaMarkdown content={ post.excerpt } />
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0 mr-2">
|
||||
<img
|
||||
className="h-10 w-10 object-cover rounded-full shadow-sm"
|
||||
src={post?.author?.avatar}
|
||||
alt={post?.author?.name}
|
||||
src={ post?.author?.avatar }
|
||||
alt={ post?.author?.name }
|
||||
/>
|
||||
</div>
|
||||
<p className="text-base font-medium text-gray-600 group-hover:text-gray-800 dark:text-gray-200 dark:group-hover:text-white">
|
||||
{post?.author?.name}
|
||||
{ post?.author?.name }
|
||||
</p>
|
||||
{formattedDate !== "" && (
|
||||
{ formattedDate !== "" && (
|
||||
<>
|
||||
<span className="font-bold text-gray-200 dark:text-gray-500 mx-2">
|
||||
—
|
||||
</span>
|
||||
<p className="text-base text-gray-400 group-hover:text-gray-500 dark:text-gray-300 dark:group-hover:text-gray-150">
|
||||
{formattedDate}
|
||||
{ formattedDate }
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { tinaField } from "tinacms/dist/react";
|
||||
export const Actions = ({
|
||||
parentColor = "default",
|
||||
className = "",
|
||||
actions,
|
||||
actions
|
||||
}: {
|
||||
parentColor: string;
|
||||
className: string;
|
||||
@@ -25,7 +25,7 @@ export const Actions = ({
|
||||
orange:
|
||||
"text-white bg-orange-500 hover:bg-orange-600 bg-gradient-to-r from-orange-400 to-orange-600 hover:from-orange-400 hover:to-orange-500",
|
||||
yellow:
|
||||
"text-gray-800 bg-yellow-500 hover:bg-yellow-600 bg-gradient-to-r from-yellow-400 to-yellow-600 hover:from-yellow-400 hover:to-yellow-500",
|
||||
"text-gray-800 bg-yellow-500 hover:bg-yellow-600 bg-gradient-to-r from-yellow-400 to-yellow-600 hover:from-yellow-400 hover:to-yellow-500"
|
||||
};
|
||||
|
||||
const invertedButtonColorClasses = {
|
||||
@@ -40,7 +40,7 @@ export const Actions = ({
|
||||
orange:
|
||||
"text-orange-500 bg-white hover:bg-gray-50 bg-gradient-to-r from-gray-50 to-white hover:to-gray-100",
|
||||
yellow:
|
||||
"text-yellow-500 bg-white hover:bg-gray-50 bg-gradient-to-r from-gray-50 to-white hover:to-gray-100",
|
||||
"text-yellow-500 bg-white hover:bg-gray-50 bg-gradient-to-r from-gray-50 to-white hover:to-gray-100"
|
||||
};
|
||||
|
||||
const linkButtonColorClasses = {
|
||||
@@ -55,28 +55,28 @@ export const Actions = ({
|
||||
orange:
|
||||
"text-orange-600 dark:text-orange-400 hover:text-orange-400 dark:hover:text-orange-200",
|
||||
yellow:
|
||||
"text-yellow-600 dark:text-yellow-400 hover:text-yellow-400 dark:hover:text-yellow-200",
|
||||
"text-yellow-600 dark:text-yellow-400 hover:text-yellow-400 dark:hover:text-yellow-200"
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex flex-wrap items-center gap-y-4 gap-x-6 ${className}`}>
|
||||
{actions &&
|
||||
actions.map(function (action, index) {
|
||||
<div className={ `flex flex-wrap items-center gap-y-4 gap-x-6 ${ className }` }>
|
||||
{ actions &&
|
||||
actions.map((action, index) => {
|
||||
let element = null;
|
||||
if (action.type === "button") {
|
||||
element = (
|
||||
<Link key={index} href={action.link ? action.link : "/"}>
|
||||
<Link key={ index } href={ action.link ? action.link : "/" }>
|
||||
<button
|
||||
data-tina-field={tinaField(action)}
|
||||
className={`z-10 relative flex items-center px-7 py-3 font-semibold text-lg transition duration-150 ease-out rounded-lg transform focus:shadow-outline focus:outline-none focus:ring-2 ring-offset-current ring-offset-2 whitespace-nowrap ${
|
||||
invertedButtonColorClasses["blue"] }`}
|
||||
data-tina-field={ tinaField(action) }
|
||||
className={ `z-10 relative flex items-center px-7 py-3 font-semibold text-lg transition duration-150 ease-out rounded-lg transform focus:shadow-outline focus:outline-none focus:ring-2 ring-offset-current ring-offset-2 whitespace-nowrap ${
|
||||
invertedButtonColorClasses.blue }` }
|
||||
>
|
||||
{action.label}
|
||||
{action.icon && (
|
||||
{ action.label }
|
||||
{ action.icon && (
|
||||
<BiRightArrowAlt
|
||||
className={`ml-1 -mr-1 w-6 h-6 opacity-80`}
|
||||
className={ "ml-1 -mr-1 w-6 h-6 opacity-80" }
|
||||
/>
|
||||
)}
|
||||
) }
|
||||
</button>
|
||||
</Link>
|
||||
);
|
||||
@@ -84,23 +84,23 @@ export const Actions = ({
|
||||
if (action.type === "link" || action.type === "linkExternal") {
|
||||
element = (
|
||||
<Link
|
||||
key={index}
|
||||
href={action.link ? action.link : "/"}
|
||||
data-tina-field={tinaField(action)}
|
||||
className={"group inline-flex items-center font-semibold text-lg transition duration-150 ease-out text-white hover:text-gray-50" }
|
||||
style={{
|
||||
key={ index }
|
||||
href={ action.link ? action.link : "/" }
|
||||
data-tina-field={ tinaField(action) }
|
||||
className={ "group inline-flex items-center font-semibold text-lg transition duration-150 ease-out text-white hover:text-gray-50" }
|
||||
style={ {
|
||||
textShadow: "0 3px 7px rgba(var(--color-rgb-blue-400),0.2)"
|
||||
}}
|
||||
} }
|
||||
>
|
||||
{action.label}
|
||||
{action.icon && (
|
||||
<BiRightArrowAlt className={`ml-0 mr-0 w-6 h-6 opacity-80`} />
|
||||
)}
|
||||
{ action.label }
|
||||
{ action.icon && (
|
||||
<BiRightArrowAlt className={ "ml-0 mr-0 w-6 h-6 opacity-80" } />
|
||||
) }
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return element;
|
||||
})}
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as React from "react";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import {tinaField} from "tinacms/dist/react";
|
||||
import {ObjectField} from "@tinacms/schema-tools/dist/types";
|
||||
import {SectionElement} from "@react-types/shared";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { tinaField } from "tinacms/dist/react";
|
||||
import { ObjectField } from "@tinacms/schema-tools/dist/types";
|
||||
|
||||
interface AnchoringProps {
|
||||
text?: string, // Default: Découvrez-en plus !
|
||||
@@ -26,26 +25,26 @@ export const Anchoring = (props: AnchoringProps) => {
|
||||
behavior: "smooth",
|
||||
block: "start"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleScrollOpacity(); // Initialize opacity on mount
|
||||
|
||||
window.addEventListener('scroll', handleScrollOpacity);
|
||||
window.addEventListener("scroll", handleScrollOpacity);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScrollOpacity);
|
||||
window.removeEventListener("scroll", handleScrollOpacity);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="anchoring"
|
||||
ref={anchoringRef}
|
||||
ref={ anchoringRef }
|
||||
style={ { opacity: opacity } }
|
||||
>
|
||||
<a onClick={ handleScrollClick }>
|
||||
<h1 data-tina-field={tinaField(props, "text")}>{ props.text }</h1>
|
||||
<h1 data-tina-field={ tinaField(props, "text") }>{ props.text }</h1>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTExLjI1OCAxNi4yNDItNi02YTEuMDUgMS4wNSAwIDEgMSAxLjQ4NC0xLjQ4NEwxMiAxNC4wMTVsNS4yNTgtNS4yNTdhMS4wNSAxLjA1IDAgMSAxIDEuNDg0IDEuNDg0bC02IDZhMS4wNSAxLjA1IDAgMCAxLTEuNDg0IDBaIiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGNsYXNzPSJmaWxsLTAwMDAwMCI+PC9wYXRoPjwvc3ZnPg=="/>
|
||||
</a>
|
||||
@@ -62,7 +61,7 @@ export const anchoringSchema: ObjectField = {
|
||||
text: "Découvrez-en plus !",
|
||||
linkTo: "#main-page",
|
||||
enabled: true
|
||||
},
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
|
||||
@@ -12,21 +12,21 @@ export const Container = ({
|
||||
small: "py-8",
|
||||
medium: "py-12",
|
||||
large: "py-24",
|
||||
default: "py-12",
|
||||
default: "py-12"
|
||||
};
|
||||
const widthClass = {
|
||||
small: "max-w-4xl",
|
||||
medium: "max-w-5xl",
|
||||
large: "max-w-7xl",
|
||||
custom: "",
|
||||
custom: ""
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${widthClass[width]} mx-auto px-6 sm:px-8 ${verticalPadding[size]} ${className}`}
|
||||
{...props}
|
||||
className={ `${ widthClass[width] } mx-auto px-6 sm:px-8 ${ verticalPadding[size] } ${ className }` }
|
||||
{ ...props }
|
||||
>
|
||||
{children}
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,13 +2,13 @@ import * as React from "react";
|
||||
import { ColorPickerInput } from "../../tina/fields/color";
|
||||
import { IconPickerInput } from "../../tina/fields/icon";
|
||||
import * as BoxIcons from "react-icons/bi";
|
||||
import {Template} from "tinacms";
|
||||
import {ObjectField} from "@tinacms/schema-tools/dist/types";
|
||||
import { Template } from "tinacms";
|
||||
import { ObjectField } from "@tinacms/schema-tools/dist/types";
|
||||
|
||||
export const IconOptions = {
|
||||
Tina: (props) => (
|
||||
<svg
|
||||
{...props}
|
||||
{ ...props }
|
||||
viewBox="0 0 66 80"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -24,7 +24,7 @@ export const IconOptions = {
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
...BoxIcons,
|
||||
...BoxIcons
|
||||
};
|
||||
|
||||
const iconColorClass: {
|
||||
@@ -32,40 +32,40 @@ const iconColorClass: {
|
||||
} = {
|
||||
blue: {
|
||||
regular: "text-blue-400",
|
||||
circle: "bg-blue-400 dark:bg-blue-500 text-blue-50",
|
||||
circle: "bg-blue-400 dark:bg-blue-500 text-blue-50"
|
||||
},
|
||||
teal: {
|
||||
regular: "text-teal-400",
|
||||
circle: "bg-teal-400 dark:bg-teal-500 text-teal-50",
|
||||
circle: "bg-teal-400 dark:bg-teal-500 text-teal-50"
|
||||
},
|
||||
green: {
|
||||
regular: "text-green-400",
|
||||
circle: "bg-green-400 dark:bg-green-500 text-green-50",
|
||||
circle: "bg-green-400 dark:bg-green-500 text-green-50"
|
||||
},
|
||||
red: {
|
||||
regular: "text-red-400",
|
||||
circle: "bg-red-400 dark:bg-red-500 text-red-50",
|
||||
circle: "bg-red-400 dark:bg-red-500 text-red-50"
|
||||
},
|
||||
pink: {
|
||||
regular: "text-pink-400",
|
||||
circle: "bg-pink-400 dark:bg-pink-500 text-pink-50",
|
||||
circle: "bg-pink-400 dark:bg-pink-500 text-pink-50"
|
||||
},
|
||||
purple: {
|
||||
regular: "text-purple-400",
|
||||
circle: "bg-purple-400 dark:bg-purple-500 text-purple-50",
|
||||
circle: "bg-purple-400 dark:bg-purple-500 text-purple-50"
|
||||
},
|
||||
orange: {
|
||||
regular: "text-orange-400",
|
||||
circle: "bg-orange-400 dark:bg-orange-500 text-orange-50",
|
||||
circle: "bg-orange-400 dark:bg-orange-500 text-orange-50"
|
||||
},
|
||||
yellow: {
|
||||
regular: "text-yellow-400",
|
||||
circle: "bg-yellow-400 dark:bg-yellow-500 text-yellow-50",
|
||||
circle: "bg-yellow-400 dark:bg-yellow-500 text-yellow-50"
|
||||
},
|
||||
white: {
|
||||
regular: "text-white opacity-80",
|
||||
circle: "bg-white-400 dark:bg-white-500 text-white-50",
|
||||
},
|
||||
circle: "bg-white-400 dark:bg-white-500 text-white-50"
|
||||
}
|
||||
};
|
||||
|
||||
const iconSizeClass = {
|
||||
@@ -74,14 +74,14 @@ const iconSizeClass = {
|
||||
medium: "w-12 h-12 flex-shrink-0",
|
||||
large: "w-14 h-14 flex-shrink-0",
|
||||
xl: "w-16 h-16 flex-shrink-0",
|
||||
custom: "",
|
||||
custom: ""
|
||||
};
|
||||
|
||||
export const Icon = ({
|
||||
data,
|
||||
parentColor = "",
|
||||
className = "",
|
||||
tinaField = "",
|
||||
tinaField = ""
|
||||
}) => {
|
||||
if (IconOptions[data.name] === null || IconOptions[data.name] === undefined) {
|
||||
return null;
|
||||
@@ -101,22 +101,21 @@ export const Icon = ({
|
||||
if (style == "circle") {
|
||||
return (
|
||||
<div
|
||||
data-tina-field={tinaField}
|
||||
className={`relative z-10 inline-flex items-center justify-center flex-shrink-0 ${iconSizeClasses} rounded-full ${iconColorClass[iconColor].circle} ${className}`}
|
||||
data-tina-field={ tinaField }
|
||||
className={ `relative z-10 inline-flex items-center justify-center flex-shrink-0 ${ iconSizeClasses } rounded-full ${ iconColorClass[iconColor].circle } ${ className }` }
|
||||
>
|
||||
<IconSVG className="w-2/3 h-2/3" />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
const iconColorClasses =
|
||||
iconColorClass[parentColor === "primary" && (iconColor === "primary") ? "white" : iconColor]?.regular || "white";
|
||||
return (
|
||||
<IconSVG
|
||||
data-tina-field={tinaField}
|
||||
className={`${iconSizeClasses} ${iconColorClasses} ${className}`}
|
||||
data-tina-field={ tinaField }
|
||||
className={ `${ iconSizeClasses } ${ iconColorClasses } ${ className }` }
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const iconSchema: ObjectField = {
|
||||
@@ -129,16 +128,16 @@ export const iconSchema: ObjectField = {
|
||||
label: "Icon",
|
||||
name: "name",
|
||||
ui: {
|
||||
component: (props) => IconPickerInput(props),
|
||||
},
|
||||
component: (props) => IconPickerInput(props)
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Color",
|
||||
name: "color",
|
||||
ui: {
|
||||
component: () => ColorPickerInput,
|
||||
},
|
||||
component: () => ColorPickerInput
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "style",
|
||||
@@ -147,13 +146,13 @@ export const iconSchema: ObjectField = {
|
||||
options: [
|
||||
{
|
||||
label: "Circle",
|
||||
value: "circle",
|
||||
value: "circle"
|
||||
},
|
||||
{
|
||||
label: "Float",
|
||||
value: "float",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
value: "float"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
@@ -17,8 +17,8 @@ export const Section = ({ children, color = "", className = "" }) => {
|
||||
orange:
|
||||
"text-white bg-orange-500 bg-gradient-to-br from-orange-500 to-orange-600",
|
||||
yellow:
|
||||
"text-white bg-yellow-500 bg-gradient-to-br from-yellow-500 to-yellow-600",
|
||||
},
|
||||
"text-white bg-yellow-500 bg-gradient-to-br from-yellow-500 to-yellow-600"
|
||||
}
|
||||
};
|
||||
const sectionColorCss = sectionColor[color]
|
||||
? sectionColor[color]
|
||||
@@ -26,9 +26,9 @@ export const Section = ({ children, color = "", className = "" }) => {
|
||||
|
||||
return (
|
||||
<section
|
||||
className={`flex-1 relative transition duration-150 ease-out body-font overflow-hidden ${sectionColorCss} ${className}`}
|
||||
className={ `flex-1 relative transition duration-150 ease-out body-font overflow-hidden ${ sectionColorCss } ${ className }` }
|
||||
>
|
||||
{children}
|
||||
{ children }
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,3 +76,6 @@ blocks:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
"@types/react": "^17.0.35",
|
||||
"@types/styled-components": "^5.1.15",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^6.5.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"postcss": "^8.3.11",
|
||||
"postcss-import": "^14.0.2",
|
||||
"postcss-nesting": "^10.1.0"
|
||||
|
||||
10
pages/404.js
10
pages/404.js
@@ -5,7 +5,7 @@ export default function FourOhFour() {
|
||||
return (
|
||||
<Layout>
|
||||
<Hero
|
||||
data={{
|
||||
data={ {
|
||||
color: "default",
|
||||
headline: "404 – Page Not Found",
|
||||
text: "Oops! It seems there's nothing here, how embarrassing.",
|
||||
@@ -14,10 +14,10 @@ export default function FourOhFour() {
|
||||
label: "Return Home",
|
||||
type: "button",
|
||||
icon: true,
|
||||
link: "/",
|
||||
},
|
||||
],
|
||||
}}
|
||||
link: "/"
|
||||
}
|
||||
]
|
||||
} }
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -11,22 +11,22 @@ export default function HomePage(
|
||||
const { data } = useTina(props);
|
||||
|
||||
return (
|
||||
<Layout rawData={data} data={data.global as any}>
|
||||
<Blocks {...data.page} />
|
||||
<Layout rawData={ data } data={ data.global as any }>
|
||||
<Blocks { ...data.page } />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export const getStaticProps = async ({ params }) => {
|
||||
const tinaProps = await client.queries.contentQuery({
|
||||
relativePath: `${params.filename}.md`,
|
||||
relativePath: `${ params.filename }.md`
|
||||
});
|
||||
const props = {
|
||||
...tinaProps,
|
||||
enableVisualEditing: process.env.VERCEL_ENV === "preview",
|
||||
enableVisualEditing: process.env.VERCEL_ENV === "preview"
|
||||
};
|
||||
return {
|
||||
props: JSON.parse(JSON.stringify(props)) as typeof props,
|
||||
props: JSON.parse(JSON.stringify(props)) as typeof props
|
||||
};
|
||||
};
|
||||
|
||||
@@ -34,8 +34,8 @@ export const getStaticPaths = async () => {
|
||||
const pagesListData = await client.queries.pageConnection();
|
||||
return {
|
||||
paths: pagesListData.data.pageConnection?.edges?.map((page) => ({
|
||||
params: { filename: page?.node?._sys.filename },
|
||||
params: { filename: page?.node?._sys.filename }
|
||||
})),
|
||||
fallback: false,
|
||||
fallback: false
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "../styles/_styles.scss";
|
||||
|
||||
const App = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
return <Component { ...pageProps } />;
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function HomePage(
|
||||
<Layout>
|
||||
<Section className="flex-1">
|
||||
<Container size="large" width="small">
|
||||
<Posts data={posts} />
|
||||
<Posts data={ posts } />
|
||||
</Container>
|
||||
</Section>
|
||||
</Layout>
|
||||
@@ -25,8 +25,8 @@ export const getStaticProps = async () => {
|
||||
const tinaProps = await client.queries.pageQuery();
|
||||
return {
|
||||
props: {
|
||||
...tinaProps,
|
||||
},
|
||||
...tinaProps
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ export default function BlogPostPage(
|
||||
const { data } = useTina({
|
||||
query: props.query,
|
||||
variables: props.variables,
|
||||
data: props.data,
|
||||
data: props.data
|
||||
});
|
||||
if (data && data.post) {
|
||||
return (
|
||||
<Layout rawData={data} data={data.global}>
|
||||
<Post {...data.post} />
|
||||
<Layout rawData={ data } data={ data.global }>
|
||||
<Post { ...data.post } />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -29,12 +29,12 @@ export default function BlogPostPage(
|
||||
|
||||
export const getStaticProps = async ({ params }) => {
|
||||
const tinaProps = await client.queries.blogPostQuery({
|
||||
relativePath: `${params.filename}.mdx`,
|
||||
relativePath: `${ params.filename }.mdx`
|
||||
});
|
||||
return {
|
||||
props: {
|
||||
...tinaProps,
|
||||
},
|
||||
...tinaProps
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -49,9 +49,9 @@ export const getStaticPaths = async () => {
|
||||
const postsListData = await client.queries.postConnection();
|
||||
return {
|
||||
paths: postsListData.data.postConnection.edges.map((post) => ({
|
||||
params: { filename: post.node._sys.filename },
|
||||
params: { filename: post.node._sys.filename }
|
||||
})),
|
||||
fallback: "blocking",
|
||||
fallback: "blocking"
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
195
tina/config.tsx
195
tina/config.tsx
@@ -3,8 +3,9 @@ import { contentBlockSchema } from "../components/blocks/content";
|
||||
import { featureBlockSchema } from "../components/blocks/features";
|
||||
import { heroBlockSchema } from "../components/blocks/hero";
|
||||
import { testimonialBlockSchema } from "../components/blocks/testimonial";
|
||||
import { ColorPickerInput } from "./fields/color";
|
||||
import {carouselBlockSchema} from "../components/blocks/carousel";
|
||||
import { carouselBlockSchema } from "../components/blocks/carousel";
|
||||
import { headerSchema } from "../components/layout/header";
|
||||
import { footerSchema } from "../components/layout/footer/footer";
|
||||
|
||||
const config = defineConfig({
|
||||
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID!,
|
||||
@@ -22,12 +23,12 @@ const config = defineConfig({
|
||||
// this is the config for the tina cloud media store
|
||||
tina: {
|
||||
publicFolder: "public",
|
||||
mediaRoot: "uploads",
|
||||
},
|
||||
mediaRoot: "uploads"
|
||||
}
|
||||
},
|
||||
build: {
|
||||
publicFolder: "public", // The public asset folder for your framework
|
||||
outputFolder: "admin", // within the public folder
|
||||
outputFolder: "admin" // within the public folder
|
||||
},
|
||||
schema: {
|
||||
collections: [
|
||||
@@ -38,8 +39,8 @@ const config = defineConfig({
|
||||
format: "mdx",
|
||||
ui: {
|
||||
router: ({ document }) => {
|
||||
return `/posts/${document._sys.filename}`;
|
||||
},
|
||||
return `/posts/${ document._sys.filename }`;
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -47,23 +48,23 @@ const config = defineConfig({
|
||||
label: "Title",
|
||||
name: "title",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "heroImg",
|
||||
label: "Hero Image",
|
||||
label: "Hero Image"
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
label: "Excerpt",
|
||||
name: "excerpt",
|
||||
name: "excerpt"
|
||||
},
|
||||
{
|
||||
type: "reference",
|
||||
label: "Author",
|
||||
name: "author",
|
||||
collections: ["author"],
|
||||
collections: ["author"]
|
||||
},
|
||||
{
|
||||
type: "datetime",
|
||||
@@ -71,8 +72,8 @@ const config = defineConfig({
|
||||
name: "date",
|
||||
ui: {
|
||||
dateFormat: "MMMM DD YYYY",
|
||||
timeFormat: "hh:mm A",
|
||||
},
|
||||
timeFormat: "hh:mm A"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
@@ -88,9 +89,9 @@ const config = defineConfig({
|
||||
name: "format",
|
||||
label: "Format",
|
||||
type: "string",
|
||||
options: ["utc", "iso", "local"],
|
||||
},
|
||||
],
|
||||
options: ["utc", "iso", "local"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "BlockQuote",
|
||||
@@ -99,14 +100,14 @@ const config = defineConfig({
|
||||
{
|
||||
name: "children",
|
||||
label: "Quote",
|
||||
type: "rich-text",
|
||||
type: "rich-text"
|
||||
},
|
||||
{
|
||||
name: "authorName",
|
||||
label: "Author",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "NewsletterSignup",
|
||||
@@ -115,35 +116,35 @@ const config = defineConfig({
|
||||
{
|
||||
name: "children",
|
||||
label: "CTA",
|
||||
type: "rich-text",
|
||||
type: "rich-text"
|
||||
},
|
||||
{
|
||||
name: "placeholder",
|
||||
label: "Placeholder",
|
||||
type: "string",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "buttonText",
|
||||
label: "Button Text",
|
||||
type: "string",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "disclaimer",
|
||||
label: "Disclaimer",
|
||||
type: "rich-text",
|
||||
},
|
||||
type: "rich-text"
|
||||
}
|
||||
],
|
||||
ui: {
|
||||
defaultItem: {
|
||||
placeholder: "Enter your email",
|
||||
buttonText: "Notify Me",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
isBody: true,
|
||||
},
|
||||
buttonText: "Notify Me"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
isBody: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Global",
|
||||
@@ -151,104 +152,12 @@ const config = defineConfig({
|
||||
path: "content/global",
|
||||
format: "json",
|
||||
ui: {
|
||||
global: true,
|
||||
global: true
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "object",
|
||||
label: "Header",
|
||||
name: "header",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Page Title",
|
||||
name: "pageTitle",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Title",
|
||||
name: "title",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Subtitle",
|
||||
name: "subtitle",
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
label: "Logo",
|
||||
name: "logoSrc"
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
label: "Nav Links",
|
||||
name: "nav",
|
||||
list: true,
|
||||
ui: {
|
||||
itemProps: (item) => {
|
||||
return { label: item?.label };
|
||||
},
|
||||
defaultItem: {
|
||||
href: "home",
|
||||
label: "Home",
|
||||
external: false
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Link",
|
||||
name: "href",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Label",
|
||||
name: "label",
|
||||
},
|
||||
{
|
||||
type: "boolean",
|
||||
label: "External",
|
||||
name: "external",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
label: "Footer",
|
||||
name: "footer",
|
||||
fields: [
|
||||
{
|
||||
type: "object",
|
||||
label: "Social Links",
|
||||
name: "social",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
label: "Facebook",
|
||||
name: "facebook",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Twitter",
|
||||
name: "twitter",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Instagram",
|
||||
name: "instagram",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
label: "Github",
|
||||
name: "github",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
headerSchema,
|
||||
footerSchema
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Authors",
|
||||
@@ -261,14 +170,14 @@ const config = defineConfig({
|
||||
label: "Name",
|
||||
name: "name",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
label: "Avatar",
|
||||
name: "avatar",
|
||||
},
|
||||
],
|
||||
name: "avatar"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Pages",
|
||||
@@ -277,13 +186,13 @@ const config = defineConfig({
|
||||
ui: {
|
||||
router: ({ document }) => {
|
||||
if (document._sys.filename === "home") {
|
||||
return `/`;
|
||||
return "/";
|
||||
}
|
||||
if (document._sys.filename === "about") {
|
||||
return `/about`;
|
||||
return "/about";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -293,7 +202,7 @@ const config = defineConfig({
|
||||
description:
|
||||
"The title of the page. This is used to display the title in the CMS",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
@@ -301,7 +210,7 @@ const config = defineConfig({
|
||||
name: "blocks",
|
||||
label: "Sections",
|
||||
ui: {
|
||||
visualSelector: true,
|
||||
visualSelector: true
|
||||
},
|
||||
templates: [
|
||||
heroBlockSchema,
|
||||
@@ -311,12 +220,12 @@ const config = defineConfig({
|
||||
testimonialBlockSchema,
|
||||
// @ts-ignore
|
||||
carouselBlockSchema
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -10,7 +10,7 @@ export const colorOptions = [
|
||||
"red",
|
||||
"pink",
|
||||
"purple",
|
||||
"white",
|
||||
"white"
|
||||
];
|
||||
|
||||
export const ColorPickerInput = wrapFieldsWithMeta(({ input }) => {
|
||||
@@ -23,29 +23,29 @@ export const ColorPickerInput = wrapFieldsWithMeta(({ input }) => {
|
||||
red: "bg-red-500 border-red-600",
|
||||
pink: "bg-pink-500 border-pink-600",
|
||||
purple: "bg-purple-500 border-purple-600",
|
||||
white: "bg-white border-gray-150",
|
||||
white: "bg-white border-gray-150"
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<input type="text" id={input.name} className="hidden" {...input} />
|
||||
<input type="text" id={ input.name } className="hidden" { ...input } />
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{colorOptions.map((color) => {
|
||||
{ colorOptions.map((color) => {
|
||||
return (
|
||||
<button
|
||||
className={`w-9 h-9 rounded-full shadow border ${
|
||||
className={ `w-9 h-9 rounded-full shadow border ${
|
||||
inputClasses[color]
|
||||
} ${
|
||||
input.value === color
|
||||
? "ring-[3px] ring-offset-2 ring-blue-400"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
}` }
|
||||
onClick={ () => {
|
||||
input.onChange(color);
|
||||
}}
|
||||
></button>
|
||||
} }
|
||||
/>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -9,9 +9,8 @@ const parseIconName = (name: string) => {
|
||||
const splitName = name.split(/(?=[A-Z])/);
|
||||
if (splitName.length > 1) {
|
||||
return splitName.slice(1).join(" ");
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
export const IconPickerInput = wrapFieldsWithMeta(({ input }) => {
|
||||
@@ -29,29 +28,29 @@ export const IconPickerInput = wrapFieldsWithMeta(({ input }) => {
|
||||
|
||||
return (
|
||||
<div className="relative z-[1000]">
|
||||
<input type="text" id={input.name} className="hidden" {...input} />
|
||||
<input type="text" id={ input.name } className="hidden" { ...input } />
|
||||
<Popover>
|
||||
{({ open }) => (
|
||||
{ ({ open }) => (
|
||||
<>
|
||||
<Popover.Button as={"span"}>
|
||||
<Popover.Button as={ "span" }>
|
||||
<Button
|
||||
className={`text-sm h-11 px-4 ${InputIcon ? "h-11" : "h-10"}`}
|
||||
className={ `text-sm h-11 px-4 ${ InputIcon ? "h-11" : "h-10" }` }
|
||||
size="custom"
|
||||
rounded="full"
|
||||
variant={open ? "secondary" : "white"}
|
||||
variant={ open ? "secondary" : "white" }
|
||||
>
|
||||
{InputIcon && (
|
||||
{ InputIcon && (
|
||||
<InputIcon className="w-7 mr-1 h-auto fill-current text-blue-500" />
|
||||
)}
|
||||
{inputLabel}
|
||||
{!InputIcon && (
|
||||
) }
|
||||
{ inputLabel }
|
||||
{ !InputIcon && (
|
||||
<BiChevronRight className="w-5 h-auto fill-current opacity-70 ml-1" />
|
||||
)}
|
||||
) }
|
||||
</Button>
|
||||
</Popover.Button>
|
||||
<div
|
||||
className="absolute w-full min-w-[192px] max-w-2xl -bottom-2 left-0 translate-y-full"
|
||||
style={{ zIndex: 1000 }}
|
||||
style={ { zIndex: 1000 } }
|
||||
>
|
||||
<Transition
|
||||
enter="transition duration-150 ease-out"
|
||||
@@ -62,72 +61,72 @@ export const IconPickerInput = wrapFieldsWithMeta(({ input }) => {
|
||||
leaveTo="transform opacity-0 -translate-y-2"
|
||||
>
|
||||
<Popover.Panel className="relative overflow-hidden rounded-lg shadow-lg bg-white border border-gray-150 z-50">
|
||||
{({ close }) => (
|
||||
{ ({ close }) => (
|
||||
<div className="max-h-[24rem] flex flex-col w-full h-full">
|
||||
<div className="bg-gray-50 p-2 border-b border-gray-100 z-10 shadow-sm">
|
||||
<input
|
||||
type="text"
|
||||
className="bg-white text-sm rounded-sm border border-gray-100 shadow-inner py-1.5 px-2.5 w-full block placeholder-gray-200"
|
||||
onClick={(event: any) => {
|
||||
onClick={ (event: any) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}}
|
||||
value={filter}
|
||||
onChange={(event: any) => {
|
||||
} }
|
||||
value={ filter }
|
||||
onChange={ (event: any) => {
|
||||
setFilter(event.target.value);
|
||||
}}
|
||||
} }
|
||||
placeholder="Filter..."
|
||||
/>
|
||||
</div>
|
||||
{filteredBlocks.length === 0 && (
|
||||
{ filteredBlocks.length === 0 && (
|
||||
<span className="relative text-center text-xs px-2 py-3 text-gray-300 bg-gray-50 italic">
|
||||
No matches found
|
||||
</span>
|
||||
)}
|
||||
{filteredBlocks.length > 0 && (
|
||||
) }
|
||||
{ filteredBlocks.length > 0 && (
|
||||
<div className="w-full grid grid-cols-6 auto-rows-auto p-2 overflow-y-auto">
|
||||
<button
|
||||
className="relative rounded-lg text-center text-xs py-2 px-3 flex-1 outline-none transition-all ease-out duration-150 hover:text-blue-500 focus:text-blue-500 focus:bg-gray-50 hover:bg-gray-50"
|
||||
key={"clear-input"}
|
||||
onClick={() => {
|
||||
key={ "clear-input" }
|
||||
onClick={ () => {
|
||||
input.onChange("");
|
||||
setFilter("");
|
||||
close();
|
||||
}}
|
||||
} }
|
||||
>
|
||||
<GoCircleSlash className="w-6 h-auto text-gray-200" />
|
||||
</button>
|
||||
{filteredBlocks.map((name) => {
|
||||
{ filteredBlocks.map((name) => {
|
||||
return (
|
||||
<button
|
||||
className="relative flex items-center justify-center rounded-lg text-center text-xs py-2 px-3 flex-1 outline-none transition-all ease-out duration-150 hover:text-blue-500 focus:text-blue-500 focus:bg-gray-50 hover:bg-gray-50"
|
||||
key={name}
|
||||
onClick={() => {
|
||||
key={ name }
|
||||
onClick={ () => {
|
||||
input.onChange(name);
|
||||
setFilter("");
|
||||
close();
|
||||
}}
|
||||
} }
|
||||
>
|
||||
<Icon
|
||||
data={{
|
||||
data={ {
|
||||
name: name,
|
||||
size: "custom",
|
||||
color: "blue",
|
||||
}}
|
||||
color: "blue"
|
||||
} }
|
||||
className="w-7 h-auto"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
}) }
|
||||
</div>
|
||||
)}
|
||||
) }
|
||||
</div>
|
||||
)}
|
||||
) }
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
) }
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user