diff --git a/README.md b/README.md index b4bc546..47fa956 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is based on Jeff Thompson's [recipes PHP project](https://github.com/jeffTh [Live Recipe Book Demo](https://buzcarter.github.io/recipes-nodejs/) -This project generates static HTML. You may view locally just by dragging one of the output files into your browser, loading onto your webserver, or by using a utility such as [http-server (node)](https://github.com/http-party/http-server). +This project generates static HTML. You may view locally just by dragging one of the output files into your browser, or load them onto your website, or zip up the output and share with a friend. #### The "Recipe Book" Index @@ -45,7 +45,10 @@ Create your own... ummm, I owe y'all some documentation here, don't I? Soon... A ### Features: * Recipes are written in plain text using the intuitive-ish [Markdown format](https://daringfireball.net/projects/markdown). -* Publishes a recipe index, including on-page links to more easily locate a recipe. +* Publishes an easy to read recipe index + * use alphabetical index at page top; + * filter (search) with partial matches (for example, filtering "ap pi" might filters out all but "Deep Dish Apple Pie") + * pick how you want to view your recipes: compact (names only), names with thumbnails, or if you're more visual, choose "recipe cards" featuring large images. * Template driven layout, customize or use the provided template * Customize with themes -- color and minor layout settings. Plus, each recipe may choose its own theme. * No more overlooked ingredients or skipped Steps as recipe pages allow you to flag your current step and mark-off those you've already done. In the Ingredients and Steps sections: @@ -72,7 +75,9 @@ EXAMPLE: below a recipe with a few ingredients "ticked" off (indicating they've ## Installation -After cloning this repo: +This requires Node16 or later. + +After cloning this repo or downloading the zip file from github simply install, add your recipes, and build: ```sh npm install @@ -202,7 +207,19 @@ The `recipe.html` template file also includes some more options you can customiz * `autoUrlSections`: list of sections in the recepe template where you want raw URLs (ex: www.instagram.com) to be turned into real links. Great for the `Based On` section but not so good if you want to include Markdown-formatted links in other sections * `shortenUrls`: turns a super-long url into just the main domain name (link will still work as normal, just less cluttered). Off by default but exists if you want it +## Advanced customizations + +### Command Line Arguments + + If you'd prefer _not_ editing `config.js` (perhaps you maintain a couple recipe collections, or, like me: just want to keep personal recipes separate from this repo) you may specify the image, recipe, and output directories via command line. + + (examples show paths for a Mac) + + ```sh + npm run build imageDir="~/documents/recipes/images" recipeDir="~/documents/recipes/recipes" outputDir="~/websites/recipes/html" + ``` +(I keep my personal recipes outside this which makes keeping this app's code up-to-date easy, just a `git pull` away) ## Colophon & Thank Yous diff --git a/build.js b/build.js index c7d4d94..477f74b 100644 --- a/build.js +++ b/build.js @@ -49,6 +49,31 @@ function makeThumbnails(outputPath, images) { }); } +/** + * Intended for, well, me: makes it easier to have my own private directory of images outside + * of this git repo. For example, on a Mac you might use: + * + * ```sh + * npm run build imageDir="~/documents/recipes/images" recipeDir="~/documents/recipes/recipes" outputDir="~/websites/recipes/html" + * ``` + */ +function getCommanLineOverrides(args) { + const cmdArgs = args + ?.filter((z, index) => index > 1) + .reduce((acc, arg) => { + const [key, value] = arg.split('='); + if (key && value) { + acc[key] = value; + } + return acc; + }, {}); + + if (cmdArgs && Object.keys(cmdArgs).length) { + console.log(`Using command line overrides: ${JSON.stringify(cmdArgs, null, 3)}`); + } + return cmdArgs; +} + function main(configs) { const startTime = new Date(); const imagesPath = resolve(__dirname, configs.imageDir); @@ -90,4 +115,7 @@ function main(configs) { }); } -main(configs); +main({ + ...configs, + ...getCommanLineOverrides(process.argv), +}); diff --git a/package-lock.json b/package-lock.json index 0bccaaa..8c89c6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "recipes-nodejs", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "recipes-nodejs", - "version": "1.0.2", + "version": "1.0.3", "license": "MIT", "dependencies": { "pretty": "^2.0.0", diff --git a/package.json b/package.json index 9781614..6b3b81d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "recipes-nodejs", - "version": "1.0.2", + "version": "1.0.3", "description": "NodeJS recipe publisher, converts markdown/text recipe files into well formatted HTML.", "main": "build.js", "scripts": { diff --git a/src/static/scripts/index.js b/src/static/scripts/index.js index 426b6bc..199373a 100644 --- a/src/static/scripts/index.js +++ b/src/static/scripts/index.js @@ -29,3 +29,43 @@ init, }); })(); + +(() => { + const Styles = { + HIDDEN: 'recipe-list__item--hidden', + }; + + const Selectors = { + RECIPE_ITEMS: '.recipe-list li', + SEARCH: '#filter' + }; + + const scrub = value => value + .trim() + .toLowerCase() + .replace(/\W/g, '') + .replace(/\s+/g, ' '); + + function filter(filterText) { + const words = filterText.split(/\s+/).map(w => scrub(w)).filter(Boolean); + + document.querySelectorAll(Selectors.RECIPE_ITEMS) + .forEach((item) => { + if (!filterText) { + item.classList.remove(Styles.HIDDEN); + } + const { searchText } = item.dataset || ''; + const isMatch = words.reduce((isMatch, word) => isMatch && searchText.includes(word), true); + item.classList.toggle(Styles.HIDDEN, !isMatch); + }); + } + + function init() { + document.querySelectorAll(Selectors.RECIPE_ITEMS).forEach(item => item.dataset.searchText = scrub(item.innerText)); + document.querySelector(Selectors.SEARCH).addEventListener('keyup', function () { + filter(this.value); + }); + } + + init(); +})(); diff --git a/src/static/styles/recipesIndex.css b/src/static/styles/recipesIndex.css index e589c92..4583212 100644 --- a/src/static/styles/recipesIndex.css +++ b/src/static/styles/recipesIndex.css @@ -30,7 +30,9 @@ /* View radio buttons */ .view--content {} + .view--compact-list {} + .view--grid {} .nav-view {} @@ -76,6 +78,26 @@ html body .nav-view__item { fill: var(--color-view-svg-selected); } +/* Filter/Search */ +.filter__wrap { + float: right; +} + +.filter__terms { + border-radius: 4px; + padding: .4em; + border: solid gray 1px; + font-size: inherit; +} + +.recipe-list__photo { + display: none; +} + +body .recipe-list .recipe-list__item--hidden { + display: none; +} + /* THE Recipe List */ .recipe-list { margin-top: 1em; @@ -132,21 +154,21 @@ html body .nav-view__item { } /* view: Grid */ -.view--grid .recipe-list__item { +.view--grid .recipe-list__item { display: inline-block; width: 260px; margin: 0; margin: 0 15px 15px 0; } -.view--grid .recipe-list__item a { +.view--grid .recipe-list__item a { height: 260px; border: solid 1px transparent; border-radius: 6px; overflow: hidden; } -.view--grid .recipe-list__item a:hover { +.view--grid .recipe-list__item a:hover { border-color: black; } @@ -192,6 +214,7 @@ html body .nav-view__item { /* larger screen: ingredients and steps as two columns */ @media screen and (min-width: 820px) { + .view--content .recipe-list, .view--compact-list .recipe-list { column-count: 2; diff --git a/src/templates/index.html b/src/templates/index.html index 1230a4b..da295f5 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -25,6 +25,12 @@

Recipe Book

+
+ +