diff --git a/assets/v3.0.0/welcome-light.png b/assets/v3.0.0/welcome-light.png index 8384ee13..9f8d79a4 100644 Binary files a/assets/v3.0.0/welcome-light.png and b/assets/v3.0.0/welcome-light.png differ diff --git a/assets/v3.0.0/welcome-progress.png b/assets/v3.0.0/welcome-progress.png index db21867d..537855ae 100644 Binary files a/assets/v3.0.0/welcome-progress.png and b/assets/v3.0.0/welcome-progress.png differ diff --git a/assets/v3.1.0/media.png b/assets/v3.1.0/media.png new file mode 100644 index 00000000..ddc1d39b Binary files /dev/null and b/assets/v3.1.0/media.png differ diff --git a/docs/components/Page/Hero.tsx b/docs/components/Page/Hero.tsx index e6338934..c1e73239 100644 --- a/docs/components/Page/Hero.tsx +++ b/docs/components/Page/Hero.tsx @@ -2,37 +2,54 @@ import Link from 'next/link'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -export interface IHeroProps {} +export interface IHeroProps { + view: "left" | "right"; + title: string; + description: string | JSX.Element; + imgSrc: string; + imgAlt: string; + link?: string; + linkText?: string; + className?: string; +} -export const Hero: React.FunctionComponent = (props: React.PropsWithChildren) => { - const { t: strings } = useTranslation(); +export const Hero: React.FunctionComponent = ({view, title, description, imgSrc, imgAlt, link, linkText, className}: React.PropsWithChildren) => { return ( -
-
-
-

- {strings(`hero_title`)} -

-

- {strings(`hero_description`)} -

-

- {strings(`hero_description_second`)} -

- - - {strings(`hero_button_primary`)} - - +
+
+
+
+

+ {title} +

+ { + typeof description === 'string' ? ( +

+ {description} +

+ ) : ( + {...description} + ) + } + { + link && linkText && ( + + + {linkText} + + + ) + } +
-
-
-
- Front Matter CMS editor dashboard of your static site content +
+
+ {imgAlt} +
diff --git a/docs/components/Page/Layout.tsx b/docs/components/Page/Layout.tsx index 75049e11..0426686d 100644 --- a/docs/components/Page/Layout.tsx +++ b/docs/components/Page/Layout.tsx @@ -8,7 +8,7 @@ export interface ILayoutProps {} export const Layout: React.FunctionComponent = (props: React.PropsWithChildren) => { return (
-
+
diff --git a/docs/content/changelog/CHANGELOG.md b/docs/content/changelog/CHANGELOG.md index 890dde42..9ae2741b 100644 --- a/docs/content/changelog/CHANGELOG.md +++ b/docs/content/changelog/CHANGELOG.md @@ -2,6 +2,8 @@ ## [3.1.0] - (Upcoming release) +- BETA version available at: [beta.frontmatter.codes](https://beta.frontmatter.codes) +- [#72](https://github.com/estruyf/vscode-front-matter/issues/72): Media view on the dashboard - [#73](https://github.com/estruyf/vscode-front-matter/issues/73): List view option for the dashboard - [#77](https://github.com/estruyf/vscode-front-matter/issues/77): Dashboard grouping pages functionality integrated - [#81](https://github.com/estruyf/vscode-front-matter/issues/81): Optimizing the content folders to use a new setting to simplify configuration @@ -9,6 +11,7 @@ - [#88](https://github.com/estruyf/vscode-front-matter/issues/88): Fix issue with search sorting - [#89](https://github.com/estruyf/vscode-front-matter/issues/89): Clear filter, sorting, and grouping button added - [#90](https://github.com/estruyf/vscode-front-matter/issues/90): Refactoring to use Recoil state management +- [#91](https://github.com/estruyf/vscode-front-matter/issues/91): Support image previews from content folders ## [3.0.2] - 2021-08-31 diff --git a/docs/content/docs/dashboard.md b/docs/content/docs/dashboard.md index fa615414..188c0c29 100644 --- a/docs/content/docs/dashboard.md +++ b/docs/content/docs/dashboard.md @@ -9,25 +9,41 @@ weight: 3 # Dashboard -Managing your Markdown pages has never been easier in VS Code. With the Front Matter dashboard, you will be able to view all your pages and **search** through them, **filter**, **sort**, and much more. +Managing your Markdown pages/media has never been easier in VS Code. With the Front Matter dashboard, you will be able to view all your pages and media. -![Dashboard](/assets/dashboard.png) +On the contents view, you can **search**, **filter**, **sort** your pages and much more. + +![Dashboard - Contents view](/assets/dashboard.png) + +On the media view, you can quickly glance all the available media files in your project and perform quick actions like copying the relative path. + +![Dashboard - Media view](/assets/media.png) In order to start using the dashboard, you will have to let the extension know in which folder(s) it can find your pages. Be sure to follow our [getting started](/docs/getting-started) guide. > **Important**: If your preview images are not loading, it might be that you need to configure the `publicFolder` where the extension can find them. For instance, in Hugo, this is the static folder. You can configure this by updating the `frontMatter.content.publicFolder` setting. -## Supported filters +## Contents view + +### Supported filters - Tag filter - Category filter - Content folder (when you have multiple registered) -## Supported sorting +### Supported sorting - Last modified - Filename (asc/desc) +## Media view + +The media view has been created to make it easier to look at all media files available for your articles. When you click on an image, it will show a lightbox, so that it is easier to glance at small images. + +![Dashboard - Media view - Lightbox](/assets/lightbox.png) + +On the image card, there are actions like copying the relative path which you can then use in your article. + ## Show on startup If you want, you can check on the `Open on startup?` checkbox. This setting will allow the dashboard to automatically open when you launch the project in VS Code. It will only apply to the current project, not for all of them. \ No newline at end of file diff --git a/docs/locale/en.ts b/docs/locale/en.ts index 069aae94..5c608580 100644 --- a/docs/locale/en.ts +++ b/docs/locale/en.ts @@ -20,6 +20,11 @@ export const strings = { hero_description_second: "We at Front Matter believe that you should keep using what you like. For us, this is Visual Studio Code. Use the same editor you use to code, but with unique features to make it suitable for writing and managing your Markdown articles.", hero_button_primary: "Get started", + // Hero media + hero_media_title: "Checking your media was never easier", + hero_media_description: "Quickly glance all your media files straight from within VS Code. You will be able to filter by your content folders, and perform quick media actions.", + hero_media_button_primary: "See what it can do", + // Testimonials testimonials_title: "What others are saying", testimonials_description: "We love Front Matter and we're excited to share it with the world.", diff --git a/docs/pages/index.tsx b/docs/pages/index.tsx index 87ad3003..f85b04cf 100644 --- a/docs/pages/index.tsx +++ b/docs/pages/index.tsx @@ -1,10 +1,13 @@ import type { NextPage } from 'next'; import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Description, OtherMeta, Title } from '../components/Meta'; import { CTA, Features, Generators, Hero, Layout } from '../components/Page'; import { Extension } from '../constants/extension'; const Home: NextPage = () => { + const { t: strings } = useTranslation(); + return ( <> @@ -16,7 +19,34 @@ const Home: NextPage = () => { <Generators /> - <Hero /> + <Hero + view={"left"} + title={strings(`hero_title`)} + description={( + <> + <p className="my-6 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl"> + {strings(`hero_description`)} + </p> + <p className="my-6 text-base text-whisper-700 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl"> + {strings(`hero_description_second`)} + </p> + </> + )} + imgSrc={"/assets/dashboard.png"} + imgAlt={"Front Matter CMS editor dashboard of your static site content"} + link={`/docs/getting-started`} + linkText={strings(`hero_button_primary`)} /> + + + <Hero + view={"right"} + title={strings(`hero_media_title`)} + description={strings(`hero_media_description`)} + imgSrc={"/assets/media.png"} + imgAlt={"Front Matter CMS - media management was never easier in VS Code"} + link={`/docs/dashboard`} + linkText={strings(`hero_media_button_primary`)} + className={`-mt-12 sm:-mt-16`} /> <Features /> </Layout> diff --git a/docs/public/assets/lightbox.png b/docs/public/assets/lightbox.png new file mode 100644 index 00000000..f0ee583f Binary files /dev/null and b/docs/public/assets/lightbox.png differ diff --git a/docs/public/assets/media.png b/docs/public/assets/media.png new file mode 100644 index 00000000..cd77d13e Binary files /dev/null and b/docs/public/assets/media.png differ diff --git a/docs/public/assets/welcome-light.png b/docs/public/assets/welcome-light.png index 79217951..9f8d79a4 100644 Binary files a/docs/public/assets/welcome-light.png and b/docs/public/assets/welcome-light.png differ diff --git a/docs/public/assets/welcome-progress.png b/docs/public/assets/welcome-progress.png index 5ab6d5ec..537855ae 100644 Binary files a/docs/public/assets/welcome-progress.png and b/docs/public/assets/welcome-progress.png differ diff --git a/package-lock.json b/package-lock.json index 633b7595..8dffae71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -262,6 +262,15 @@ "fastq": "^1.6.0" } }, + "@tailwindcss/forms": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.3.3.tgz", + "integrity": "sha512-U8Fi/gq4mSuaLyLtFISwuDYzPB73YzgozjxOIHsK6NXgg/IWD1FLaHbFlWmurAMyy98O+ao74ksdQefsquBV1Q==", + "dev": true, + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -3818,6 +3827,12 @@ } } }, + "mini-svg-data-uri": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz", + "integrity": "sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", diff --git a/package.json b/package.json index 6534e2ae..ca25a2ec 100644 --- a/package.json +++ b/package.json @@ -400,6 +400,7 @@ "@headlessui/react": "1.4.0", "@heroicons/react": "1.0.4", "@iarna/toml": "2.2.3", + "@tailwindcss/forms": "^0.3.3", "@types/glob": "7.1.3", "@types/js-yaml": "3.12.1", "@types/lodash.uniqby": "4.7.6", diff --git a/src/pagesView/components/Media/Item.tsx b/src/pagesView/components/Media/Item.tsx index e4e2caf9..4d9b6cf3 100644 --- a/src/pagesView/components/Media/Item.tsx +++ b/src/pagesView/components/Media/Item.tsx @@ -2,10 +2,10 @@ import { Messenger } from '@estruyf/vscode/dist/client'; import { ClipboardCopyIcon } from '@heroicons/react/outline'; import { basename, dirname } from 'path'; import * as React from 'react'; -import { useRecoilValue } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { MediaInfo } from '../../../models/MediaPaths'; import { DashboardMessage } from '../../DashboardMessage'; -import { SettingsSelector } from '../../state'; +import { LightboxAtom, SettingsSelector } from '../../state'; export interface IItemProps { media: MediaInfo; @@ -13,6 +13,7 @@ export interface IItemProps { export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWithChildren<IItemProps>) => { const settings = useRecoilValue(SettingsSelector); + const [ , setLightbox ] = useRecoilState(LightboxAtom); const getFolder = () => { if (settings?.wsFolder && media.fsPath) { @@ -51,11 +52,15 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi } }; + const openLightbox = () => { + setLightbox(media.vsPath || ""); + }; + return ( - <li className="relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100"> - <div className="bg-white group block w-full aspect-w-10 aspect-h-7 overflow-hidden "> - <img src={media.vsPath} alt={basename(media.fsPath)} className="mx-auto object-cover pointer-events-none" /> - </div> + <li className="group relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100"> + <button className="bg-white block w-full aspect-w-10 aspect-h-7 overflow-hidden cursor-pointer" onClick={openLightbox}> + <img src={media.vsPath} alt={basename(media.fsPath)} className="mx-auto object-cover" /> + </button> <div className={`relative py-4 pl-4 pr-10`}> <div className={`absolute top-4 right-4`}> <button title={`Copy media path`} diff --git a/src/pagesView/components/Media/Lightbox.tsx b/src/pagesView/components/Media/Lightbox.tsx new file mode 100644 index 00000000..2444e096 --- /dev/null +++ b/src/pagesView/components/Media/Lightbox.tsx @@ -0,0 +1,26 @@ +import { basename } from 'path'; +import * as React from 'react'; +import { useRecoilState } from 'recoil'; +import { LightboxAtom } from '../../state'; + +export interface ILightboxProps {} + +export const Lightbox: React.FunctionComponent<ILightboxProps> = (props: React.PropsWithChildren<ILightboxProps>) => { + const [ lightbox, setLightbox ] = useRecoilState(LightboxAtom); + + if (!lightbox) { + return null; + } + + const hideLightbox = () => { + setLightbox(null); + }; + + return ( + <div onClick={hideLightbox} className={`fixed top-0 left-0 right-0 bottom-0 w-full h-full flex flex-wrap items-center justify-center bg-white bg-opacity-50 z-50`}> + <div className={`w-full h-full flex flex-wrap items-center justify-center`}> + <img src={lightbox} alt={basename(lightbox)} className={`w-1/2 h-auto rounded-lg border border-vulcan-500 shadow-2xl`} /> + </div> + </div> + ); +}; \ No newline at end of file diff --git a/src/pagesView/components/Media/Media.tsx b/src/pagesView/components/Media/Media.tsx index 91527e15..a0d68e0d 100644 --- a/src/pagesView/components/Media/Media.tsx +++ b/src/pagesView/components/Media/Media.tsx @@ -9,6 +9,7 @@ import { Header } from '../Header'; import { Spinner } from '../Spinner'; import { SponsorMsg } from '../SponsorMsg'; import { Item } from './Item'; +import { Lightbox } from './Lightbox'; import { List } from './List'; export interface IMediaProps {} @@ -58,6 +59,8 @@ export const Media: React.FunctionComponent<IMediaProps> = (props: React.PropsWi loading && ( <Spinner /> ) } + <Lightbox /> + <SponsorMsg beta={settings?.beta} version={settings?.versionInfo} /> </div> </main> diff --git a/src/pagesView/state/atom/LightboxAtom.ts b/src/pagesView/state/atom/LightboxAtom.ts new file mode 100644 index 00000000..629eb6d4 --- /dev/null +++ b/src/pagesView/state/atom/LightboxAtom.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const LightboxAtom = atom<string | null>({ + key: 'LightboxAtom', + default: "" +}); \ No newline at end of file diff --git a/src/pagesView/state/atom/index.ts b/src/pagesView/state/atom/index.ts index 5ba0e876..6d399e55 100644 --- a/src/pagesView/state/atom/index.ts +++ b/src/pagesView/state/atom/index.ts @@ -2,6 +2,7 @@ export * from './CategoryAtom'; export * from './DashboardViewAtom'; export * from './FolderAtom'; export * from './GroupingAtom'; +export * from './LightboxAtom'; export * from './LoadingAtom'; export * from './MediaFoldersAtom'; export * from './MediaTotalAtom'; diff --git a/src/pagesView/styles.css b/src/pagesView/styles.css index d88b334c..fe174556 100644 --- a/src/pagesView/styles.css +++ b/src/pagesView/styles.css @@ -16,4 +16,17 @@ @keyframes spinner { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } +} + +[type=checkbox]:checked { + background-image: none; +} + +[type=checkbox]:checked:after { + content: '\2713'; + font-size: 14px; + position: absolute; + top: -1px; + left: 2px; + color: white; } \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index a408540b..89e27fdd 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -111,5 +111,7 @@ module.exports = { variants: { extend: {}, }, - plugins: [], + plugins: [ + require("@tailwindcss/forms") + ], }