Food..
This commit is contained in:
commit
bdc7036d8d
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
package-lock.json
|
||||
dist
|
||||
.eslintcache
|
||||
result
|
12
.gitlab-ci.yml
Normal file
12
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
pages:
|
||||
stage: deploy
|
||||
image: docker.io/library/alpine:edge
|
||||
script:
|
||||
- apk add nodejs npm git
|
||||
- npm install --save-dev
|
||||
- npx webpack
|
||||
- mv dist public
|
||||
- cp _redirects public
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
26
package.json
Normal file
26
package.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "food",
|
||||
"version": "latest",
|
||||
"devDependencies": {
|
||||
"css-loader": "^6.7.1",
|
||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"prettier": "^2.6.2",
|
||||
"sass-loader": "^13.0.0",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.7.2",
|
||||
"webpack": "^5.72.1",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/comic-neue": "^4.5.8",
|
||||
"@fontsource/opendyslexic": "^4.5.4",
|
||||
"bootstrap": "latest",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss-loader": "^7.0.0",
|
||||
"preact": "^10.7.2",
|
||||
"style-loader": "^3.3.1"
|
||||
}
|
||||
}
|
5
shell.nix
Normal file
5
shell.nix
Normal file
|
@ -0,0 +1,5 @@
|
|||
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
nodejs
|
||||
];
|
||||
}
|
0
src/defaults.tsx
Normal file
0
src/defaults.tsx
Normal file
69
src/foodItems.tsx
Normal file
69
src/foodItems.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
import { Component, JSX } from "preact"
|
||||
import { FoodExcludeTypes } from "./types";
|
||||
|
||||
export const FoodItemsContainer = (props: {
|
||||
children: JSX.Element | JSX.Element[] | null
|
||||
title: string,
|
||||
}): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<h3>{props.title}</h3>
|
||||
<div class="list-group">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function shouldShowItem(contents, exclude: FoodExcludeTypes): boolean {
|
||||
if (!exclude) return false;
|
||||
for (let key of Object.keys(exclude)) {
|
||||
if (Object.keys(contents).includes(key)) {
|
||||
if (!exclude[key]) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
type BaseFoodItemProps = {
|
||||
contents?: Partial<FoodExcludeTypes>,
|
||||
exclude?: FoodExcludeTypes,
|
||||
}
|
||||
|
||||
const HideIfFoodExcluded = (props: BaseFoodItemProps & { children: JSX.Element }) => {
|
||||
if (!props.contents) {
|
||||
return props.children;
|
||||
} else if (shouldShowItem(props.contents, props.exclude)) {
|
||||
return props.children;
|
||||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type FoodItemProps = BaseFoodItemProps & {
|
||||
title: string,
|
||||
};
|
||||
|
||||
export class FoodItem extends Component<FoodItemProps> {
|
||||
render() {
|
||||
return (
|
||||
<HideIfFoodExcluded {...this.props}>
|
||||
<li class="list-group-item">{this.props.title}</li>
|
||||
</HideIfFoodExcluded>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class AdvancedFoodItem extends Component<FoodItemProps & { extra: string }> {
|
||||
render() {
|
||||
return (
|
||||
<HideIfFoodExcluded {...this.props}>
|
||||
<div class="list-group-item">
|
||||
<h5 class="mb-1">{this.props.title}</h5>
|
||||
<p class="mb-1">{this.props.extra}</p>
|
||||
</div>
|
||||
</HideIfFoodExcluded>
|
||||
)
|
||||
}
|
||||
}
|
179
src/foodTypeToggles.tsx
Normal file
179
src/foodTypeToggles.tsx
Normal file
|
@ -0,0 +1,179 @@
|
|||
|
||||
import { Component, createRef, JSX, RefObject } from "preact";
|
||||
import { ToggleableBox } from "./toggleableBox";
|
||||
import { FoodExcludeTypes } from "./types";
|
||||
|
||||
let defaultBroadExclusions = {
|
||||
isVegan: false,
|
||||
isVegetarian: false,
|
||||
}
|
||||
|
||||
let defaultFoodToggles: FoodExcludeTypes = {
|
||||
containsMeat: true,
|
||||
containsFish: true,
|
||||
containsEggs: true,
|
||||
containsMilk: true,
|
||||
containsAlcohol: true,
|
||||
}
|
||||
|
||||
export class FoodTypeToggles extends Component<{
|
||||
onChange?: (toggles: FoodExcludeTypes) => void;
|
||||
}, {
|
||||
toggles: FoodExcludeTypes;
|
||||
broadExclusions: {
|
||||
isVegan: boolean;
|
||||
isVegetarian: boolean;
|
||||
}
|
||||
}> {
|
||||
refs = {
|
||||
"isVegan": createRef<ToggleableBox>(),
|
||||
"isVegetarian": createRef<ToggleableBox>(),
|
||||
"containsMeat": createRef<ToggleableBox>(),
|
||||
"containsFish": createRef<ToggleableBox>(),
|
||||
"containsEggs": createRef<ToggleableBox>(),
|
||||
"containsMilk": createRef<ToggleableBox>(),
|
||||
"containsAlcohol": createRef<ToggleableBox>(),
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.state = {
|
||||
toggles: { ...defaultFoodToggles },
|
||||
broadExclusions: defaultBroadExclusions,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.setToDefaults();
|
||||
this.sendChangedEvent();
|
||||
}
|
||||
|
||||
setCheckedStateOfInputBoxes(names: string[], value: boolean) {
|
||||
for (let name of names) {
|
||||
if (Object.keys(this.refs).includes(name)) {
|
||||
let ref: RefObject<ToggleableBox> = this.refs[name];
|
||||
if (ref.current) {
|
||||
if (Object.keys(this.state.toggles).includes(name)) {
|
||||
this.state.toggles[name] = value;
|
||||
}
|
||||
ref.current.forceChange(value)
|
||||
} else {
|
||||
console.debug(`Can't get current ref for: ${name}`)
|
||||
}
|
||||
} else {
|
||||
console.debug(`Can't find ref for ${name}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
veganModeEnable() {
|
||||
this.vegetarianModeEnable()
|
||||
this.setCheckedStateOfInputBoxes(["containsMilk", "containsEggs"], false);
|
||||
}
|
||||
|
||||
vegetarianModeEnable() {
|
||||
this.setCheckedStateOfInputBoxes(["containsMeat", "containsFish"], false);
|
||||
}
|
||||
|
||||
veganModeDisable() {
|
||||
this.setCheckedStateOfInputBoxes(["isVegan"], false);
|
||||
}
|
||||
|
||||
vegetarianModeDisable() {
|
||||
this.setCheckedStateOfInputBoxes(["isVegetarian"], false);
|
||||
}
|
||||
|
||||
setToDefaults() {
|
||||
for (let toggle of Object.keys(defaultBroadExclusions)) {
|
||||
console.debug(`Setting broad exclusion ${toggle} to ${defaultBroadExclusions[toggle]}`)
|
||||
this.setCheckedStateOfInputBoxes([toggle], defaultBroadExclusions[toggle])
|
||||
}
|
||||
for (let toggle of Object.keys(defaultFoodToggles)) {
|
||||
console.debug(`Setting food default of ${toggle} to ${defaultFoodToggles[toggle]}`)
|
||||
console.debug(defaultFoodToggles)
|
||||
this.setCheckedStateOfInputBoxes([toggle], defaultFoodToggles[toggle])
|
||||
}
|
||||
}
|
||||
|
||||
sendChangedEvent() {
|
||||
console.debug(`Changed state of food type toggles`, this.state.toggles)
|
||||
if(this.props.onChange) this.props.onChange(this.state.toggles);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h3 class="mt-5">Toggles</h3>
|
||||
<div>
|
||||
<p>Broad Toggles</p>
|
||||
<ToggleableBox
|
||||
title="Vegan" id="isVegan"
|
||||
checked={defaultBroadExclusions.isVegan}
|
||||
ref={this.refs.isVegan} onChange={(isVegan: boolean) => {
|
||||
if (isVegan) this.veganModeEnable();
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
<ToggleableBox
|
||||
title="Vegetarian (ovo-lacto vegetarian)" id="isVegetarian"
|
||||
checked={defaultBroadExclusions.isVegetarian}
|
||||
ref={this.refs.isVegetarian} onChange={(isVegetarian: boolean) => {
|
||||
if (isVegetarian) this.vegetarianModeEnable();
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
|
||||
<p>Specific Toggles</p>
|
||||
<ToggleableBox
|
||||
title="Meat" id="containsMeat"
|
||||
checked={defaultFoodToggles.containsMeat}
|
||||
ref={this.refs.containsMeat} onChange={(containsMeat: boolean) => {
|
||||
this.state.toggles.containsMeat = containsMeat;
|
||||
if (containsMeat) {
|
||||
this.veganModeDisable();
|
||||
this.vegetarianModeDisable();
|
||||
}
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
<ToggleableBox
|
||||
title="Fish" id="containsFish"
|
||||
checked={defaultFoodToggles.containsFish}
|
||||
ref={this.refs.containsFish} onChange={(containsFish: boolean) => {
|
||||
this.state.toggles.containsFish = containsFish;
|
||||
if (containsFish) {
|
||||
this.veganModeDisable();
|
||||
this.vegetarianModeDisable();
|
||||
}
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
<ToggleableBox
|
||||
title="Eggs" id="containsEggs"
|
||||
checked={defaultFoodToggles.containsEggs}
|
||||
ref={this.refs.containsEggs} onChange={(containsEggs: boolean) => {
|
||||
this.state.toggles.containsEggs = containsEggs;
|
||||
if (containsEggs) this.veganModeDisable();
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
<ToggleableBox
|
||||
title="Milk" id="containsMilk"
|
||||
checked={defaultFoodToggles.containsMilk}
|
||||
ref={this.refs.containsMilk} onChange={(containsMilk: boolean) => {
|
||||
this.state.toggles.containsMilk = containsMilk;
|
||||
if (containsMilk) this.veganModeDisable();
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
<ToggleableBox
|
||||
title="Alcohol" id="containsAlcohol"
|
||||
checked={defaultFoodToggles.containsAlcohol}
|
||||
ref={this.refs.containsAlcohol} onChange={(containsAlcohol: boolean) => {
|
||||
this.state.toggles.containsAlcohol = containsAlcohol;
|
||||
this.sendChangedEvent();
|
||||
}} />
|
||||
|
||||
<button type="button mt-5" class="btn btn-danger" onClick={() => {
|
||||
this.setToDefaults();
|
||||
this.sendChangedEvent();
|
||||
}}>Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
21
src/main.tsx
Normal file
21
src/main.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { render } from "preact";
|
||||
import { MainPage } from "./mainPage";
|
||||
import "./theme.scss";
|
||||
|
||||
document.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
async () => {
|
||||
render(
|
||||
<>
|
||||
<main role="main" class="container">
|
||||
<MainPage />
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<span class="text-muted">[Screaming]</span>
|
||||
</div>
|
||||
</footer>
|
||||
</>, document.body);
|
||||
}
|
||||
);
|
187
src/mainPage.tsx
Normal file
187
src/mainPage.tsx
Normal file
|
@ -0,0 +1,187 @@
|
|||
import { Component, createRef } from "preact";
|
||||
import { AdvancedFoodItem, FoodItem, FoodItemsContainer } from "./foodItems";
|
||||
import { FoodTypeToggles } from "./foodTypeToggles";
|
||||
import { FoodExcludeTypes } from "./types";
|
||||
|
||||
const FONT_TYPES = [
|
||||
["default", "Default (Comic Sans, Comic Neue or Default)"],
|
||||
["comicneue", "Comic Neue"],
|
||||
["opendyslexic", "Open Dyslexic"]
|
||||
];
|
||||
|
||||
export class SettingsBlock extends Component {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
fontSettingSelectRef = createRef<HTMLSelectElement>()
|
||||
|
||||
onFontChange() {
|
||||
if (!this.fontSettingSelectRef.current) return;
|
||||
let newFont = this.fontSettingSelectRef.current.value;
|
||||
console.log(newFont);
|
||||
|
||||
for (let font of FONT_TYPES) {
|
||||
document.body.removeAttribute(`data-font-${font[1]}`)
|
||||
}
|
||||
|
||||
document.body.setAttribute("data-font", newFont);
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<h3>Settings</h3>
|
||||
<div class="form-group">
|
||||
<label for="fontSettingSelect">Font</label>
|
||||
<select ref={this.fontSettingSelectRef} class="form-control" id="fontSettingSelect" onChange={() => this.onFontChange()}>
|
||||
{...FONT_TYPES.map((font: string[]) => {
|
||||
console.log(`FONT: ${font}`)
|
||||
return <option value={font[0]}>{font[1]}</option>;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export class MainPage extends Component<unknown, { toggles: FoodExcludeTypes }> {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<h1 class="mt-5">Chaos's Food Likes/Dislikes</h1>
|
||||
<p class="lead">a hopefully helpful guide on what food we like?</p>
|
||||
|
||||
<SettingsBlock />
|
||||
|
||||
<FoodTypeToggles onChange={(toggles) => {
|
||||
this.setState({ toggles });
|
||||
}} />
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Drinks">
|
||||
<FoodItem title="Chocolate flavor Soymilk" />
|
||||
<FoodItem title="Pineapple Juice" />
|
||||
<FoodItem title="Mango Juice" />
|
||||
<FoodItem title="Pineapple Juice" />
|
||||
<FoodItem title="Yogurt-like drinks" />
|
||||
<FoodItem title="Lactose Free Chocolate Milk" contents={{ containsMilk: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Caffeinated Energy Drinks" extra={"Brands such as Monster Energy & Red Bull"} />
|
||||
<AdvancedFoodItem
|
||||
title="Squash/Cordial/Fruit Juice Concentrate"
|
||||
extra={"Flavors such as \"Apple&Blackcurrent\", \"Blackcurrent\", \"Orange\" "}
|
||||
/>
|
||||
<AdvancedFoodItem
|
||||
title="Fruity Alcoholic Ciders"
|
||||
extra="Has to be quite sugary and not at all bitter"
|
||||
contents={{ containsAlcohol: true }} exclude={this.state.toggles}
|
||||
/>
|
||||
<AdvancedFoodItem
|
||||
title="Vodka-based Cocktails"
|
||||
extra="Good when sweet and fruity; do not like when too bitter"
|
||||
contents={{ containsAlcohol: true }} exclude={this.state.toggles}
|
||||
/>
|
||||
</FoodItemsContainer>
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Snacks">
|
||||
<AdvancedFoodItem title="Chips" extra={"Also known as crisps; Flavors such as salted & sweet chilli are recommended"} />
|
||||
<AdvancedFoodItem title="Pringles" extra={"Salted; or also any other brand of Hyperbolic Paraboloid shaped potato snacks"} />
|
||||
<FoodItem title="Beef/Pork Jerky" contents={{ containsMeat: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Hummus" extra="For dipping of carrots and chips" />
|
||||
<AdvancedFoodItem title="Wheat/Starch-Coated Chilli Peanuts" extra="Not a fan of peanuts on own; but ok when coated" />
|
||||
<FoodItem title="Whole or Sliced Firm Cucumber" />
|
||||
<FoodItem title="Sliced or Sticks of Carrots" />
|
||||
<FoodItem title="Sweetcorn" />
|
||||
<FoodItem title="Firm Pickles" />
|
||||
</FoodItemsContainer>
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Treats">
|
||||
<AdvancedFoodItem title="Sugary Sweets" extra={"Preferably ones not too acidic"} />
|
||||
<AdvancedFoodItem title="Doughnuts" extra={"Circle shape with hole in middle; glazed good but not too much of it; NOT jam filled ones"} />
|
||||
<AdvancedFoodItem title="Yum-Yums" extra={"A kind of twisted doughnut dough covered in sugary glaze"} />
|
||||
<AdvancedFoodItem title="Chocolate" extra={"Light chocolate only; Can't be too bitter"} />
|
||||
<FoodItem title="Skittles" />
|
||||
</FoodItemsContainer>
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Smaller Foods">
|
||||
<FoodItem title="Chicken Nuggets" contents={{ containsMeat: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="Turkey Dinos" contents={{ containsMeat: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="Smile or Waffle shaped formed potato" />
|
||||
<FoodItem title="Fish Fingers" contents={{ containsFish: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Veggie Dippers" extra="As long as don't contain sensory bads; McDonalds kind are good" />
|
||||
<AdvancedFoodItem title="Falafel Wraps" extra={"Great with hummus&cucumber"} />
|
||||
<FoodItem title="Scrambled Eggs" contents={{ containsEggs: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="French Fries" />
|
||||
</FoodItemsContainer>
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Larger Foods">
|
||||
<FoodItem title="Garlic Bread!!!!!!!!!!!! So Good!!!" />
|
||||
<FoodItem title="Pizza" contents={{ containsMilk: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="Pasta" />
|
||||
<FoodItem title="Spongy Fried Battered Tofu" />
|
||||
<FoodItem title="Rice or Cooked Grains" />
|
||||
<FoodItem title="Roast Potatos" />
|
||||
<FoodItem title="Baked Potatos" />
|
||||
<FoodItem title={"Fried Egg & French Fries"} contents={{ containsEggs: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="Omlette" contents={{ containsEggs: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title={"Cooked Potato with Spinach & Turmeric (Saag Aloo?)"} />
|
||||
<FoodItem title={"Pasta & Meatballs"} contents={{ containsMeat: true }} exclude={this.state.toggles} />
|
||||
<FoodItem title="Beef/Pork Chilli" contents={{ containsMeat: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Burgers" extra="Preferably vegan soy or pea protein based burger; ones with beetroot in are nice; not wheat protein based" />
|
||||
</FoodItemsContainer>
|
||||
|
||||
<FoodItemsContainer title="Ingredients / Condiments">
|
||||
<FoodItem title="Hendo's Relish" />
|
||||
<FoodItem title="Worcestershire Sauce" contents={{ containsFish: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Spicy Mayo for Fries" extra={"Good with fries"} contents={{ containsEggs: true }} exclude={this.state.toggles} />
|
||||
<AdvancedFoodItem title="Tomato Ketchup" extra={"Can't be too acidic"} exclude={this.state.toggles} />
|
||||
</FoodItemsContainer>
|
||||
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<FoodItemsContainer title="Sensory Bads (Do Not Like)">
|
||||
<AdvancedFoodItem title="Anything Bitter" extra="We are very sensitive to bitter things; way more than most ppl we know; even toast can sometimes be too bitter;" />
|
||||
<AdvancedFoodItem title="Lettuce" extra="Horrible crunch; only sometimes like the flat leafy kind but only nibbling on the lighter green parts" />
|
||||
<AdvancedFoodItem title="Spinach Stalks" extra={"Is fine if flattened & Stalks are removed; Is also fine pureed as can't texture then"} />
|
||||
<AdvancedFoodItem title="Onion" extra={"Too Crunchy; somewhat ok if cooked to remove all texture, fine as a powder or puree but not too sharp tasting"} />
|
||||
<AdvancedFoodItem title="Firm Mushrooms" extra={"Too Chewy; prefer mushrooms to be boiled or cooked in a way that makes them very soft and not tender"} />
|
||||
<AdvancedFoodItem title="Button Mushroom Stalks" extra={"Weird Texture"} />
|
||||
<AdvancedFoodItem title="Fried Shitake Mushroom" extra={"Again, also way too chewy and Bad"} />
|
||||
<AdvancedFoodItem title="Firm Cooked Carrots" extra={"is Okay but prefer carrots to be cooked until soft enough to be smushed when pressed on"} />
|
||||
<AdvancedFoodItem title="Firm Cooked Broccoli" extra={"same as carrots"} />
|
||||
<AdvancedFoodItem title="Firm Cooked Cauliflower" extra={"same as carrots"} />
|
||||
<AdvancedFoodItem title="Pickles that are too soft" extra={"is ok diced up as a ingredient; but do not like whole, closer to texture of firm cucumber is preferred"} />
|
||||
<AdvancedFoodItem title="Lettuce" extra="Horrible crunch; only sometimes like the flat leafy kind but only nibbling on the lighter green parts" />
|
||||
<AdvancedFoodItem title="Some Peppers" extra="Taste of bell peppers and jalapenos Very Bad And Repulsing Cannot Handle" />
|
||||
<AdvancedFoodItem title={"Peppers & Chillis"} extra="Bad when in large chunks, good when cut up to roughly size of cooked rice when surrounded by other foods of simular or larger size" />
|
||||
<AdvancedFoodItem title="Raw Garlic" extra={"Too sharp & digestive system isn't a fan; please lightly fry or soften; black garlic is yum"} />
|
||||
<AdvancedFoodItem title="Beansprouts" extra={"Too Crunch; VERY bad"} />
|
||||
<AdvancedFoodItem title="Baby Corn Cobs" extra={"Weird texture, never soft enough"} />
|
||||
<AdvancedFoodItem title="Vegan Cheese" extra={"Can't handle slimy/mucusy texture of most; the ones that puff up into a solid crunchy-ish bubble on pizza are Okay but not the ones that stay soft"} />
|
||||
<AdvancedFoodItem title="Seitan/Gluten-Based Protein" extra={"Can't handle texture of it, very weird"} />
|
||||
<AdvancedFoodItem title="Peanut Butter" extra={"Too creamy or crunchy, never a fan of taste"} />
|
||||
<FoodItem title="Anything else unexpectedly crunchy" />
|
||||
<FoodItem title="Sparkling Water" />
|
||||
<FoodItem title="Apple Cider Vinegar" />
|
||||
<FoodItem title="Anything over our limit of acidity for regular food/sauces" />
|
||||
<FoodItem title="Aniseed Flavor" />
|
||||
<AdvancedFoodItem title="Aniseed Liquorice Flavors" extra={"Absolutely love natural liquorice root flavor; hate anise"} />
|
||||
</FoodItemsContainer>
|
||||
</>;
|
||||
}
|
||||
}
|
71
src/theme.scss
Normal file
71
src/theme.scss
Normal file
|
@ -0,0 +1,71 @@
|
|||
@import "../node_modules/@fontsource/comic-neue/latin.css";
|
||||
@import "../node_modules/@fontsource/opendyslexic/latin.css";
|
||||
|
||||
|
||||
// Configuration
|
||||
@import "../node_modules/bootstrap/scss/functions";
|
||||
@import "../node_modules/bootstrap/scss/variables";
|
||||
@import "../node_modules/bootstrap/scss/mixins";
|
||||
@import "../node_modules/bootstrap/scss/utilities";
|
||||
|
||||
$default-fonts: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
$fonts-comic-sans-neue: "Comic Sans MS", "Comic Neue", $default-fonts;
|
||||
$fonts-comic-neue: "Comic Neue", $default-fonts;
|
||||
$fonts-opendyslexic: "OpenDyslexic", $default-fonts;
|
||||
$font-sans-serif: $fonts-comic-sans-neue;
|
||||
|
||||
|
||||
// Layout & components
|
||||
@import "../node_modules/bootstrap/scss/root";
|
||||
@import "../node_modules/bootstrap/scss/reboot";
|
||||
@import "../node_modules/bootstrap/scss/type";
|
||||
//@import "../node_modules/bootstrap/scss/images";
|
||||
@import "../node_modules/bootstrap/scss/containers";
|
||||
@import "../node_modules/bootstrap/scss/grid";
|
||||
@import "../node_modules/bootstrap/scss/list-group";
|
||||
@import "../node_modules/bootstrap/scss/tables";
|
||||
@import "../node_modules/bootstrap/scss/forms";
|
||||
@import "../node_modules/bootstrap/scss/buttons";
|
||||
@import "../node_modules/bootstrap/scss/button-group";
|
||||
@import "../node_modules/bootstrap/scss/card";
|
||||
|
||||
// Helpers
|
||||
@import "../node_modules/bootstrap/scss/helpers";
|
||||
|
||||
// Utilities
|
||||
@import "../node_modules/bootstrap/scss/utilities/api";
|
||||
// scss-docs-end import-stack
|
||||
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin-bottom: 60px;
|
||||
/* Margin bottom by footer height */
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
/* Set the fixed height of the footer here */
|
||||
line-height: 60px;
|
||||
/* Vertically center the text there */
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* for custom theme settings, options in mainPage.tsx as of writing */
|
||||
|
||||
html [data-font="default"] {
|
||||
font-family: $fonts-comic-sans-neue;
|
||||
}
|
||||
html [data-font="comicneue"] {
|
||||
font-family: $fonts-comic-neue;
|
||||
}
|
||||
html [data-font="opendyslexic"] {
|
||||
font-family: $fonts-opendyslexic;
|
||||
}
|
40
src/toggleableBox.tsx
Normal file
40
src/toggleableBox.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { Component, createRef } from "preact";
|
||||
|
||||
export class ToggleableBox extends Component<{
|
||||
title: string,
|
||||
id: string,
|
||||
checked?: boolean;
|
||||
onChange?: (checked: boolean) => void,
|
||||
}> {
|
||||
inputRef = createRef<HTMLInputElement>();
|
||||
|
||||
onChangeInternal(newState: boolean) {
|
||||
console.debug(`Changed state of ${this.props.id} to ${newState}`);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(newState);
|
||||
}
|
||||
}
|
||||
|
||||
public forceChange(status: boolean) {
|
||||
if (this.inputRef.current) {
|
||||
let inputElement = this.inputRef.current;
|
||||
inputElement.checked = status;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <>
|
||||
<div class="form-check form-switch">
|
||||
<input
|
||||
class="form-check-input" type="checkbox" role="switch"
|
||||
id={this.props.id}
|
||||
ref={this.inputRef}
|
||||
onChange={() => {
|
||||
this.onChangeInternal(this.inputRef.current.checked);
|
||||
}}
|
||||
/>
|
||||
<label class="form-check-label" for={this.props.id}>{this.props.title}</label>
|
||||
</div>
|
||||
</>;
|
||||
}
|
||||
}
|
7
src/types.tsx
Normal file
7
src/types.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
export type FoodExcludeTypes = {
|
||||
containsMeat: boolean;
|
||||
containsFish: boolean;
|
||||
containsEggs: boolean;
|
||||
containsMilk: boolean;
|
||||
containsAlcohol: boolean;
|
||||
}
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"noImplicitAny": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"module": "es6",
|
||||
"target": "es2019",
|
||||
"strictBindCallApply": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitThis": true,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
}
|
||||
}
|
77
webpack-dev.config.js
Normal file
77
webpack-dev.config.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
const path = require('path');
|
||||
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
cache: false,
|
||||
entry: './src/main.tsx',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/',
|
||||
},
|
||||
stats: {
|
||||
colors: true,
|
||||
timings: true,
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
devtool: "eval-source-map",
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({ title: "Chaos's Food Likes/Dislikes" })
|
||||
],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: ['.tsx', '.ts', '.js', ".mjs"],
|
||||
alias: { os: false }
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(scss)$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader',
|
||||
}, {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
options: function () {
|
||||
return [
|
||||
require('precss'),
|
||||
require('autoprefixer')
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
loader: 'sass-loader'
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.ts(x?)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'ts-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
optimization: {
|
||||
minimize: false,
|
||||
minimizer: [
|
||||
],
|
||||
},
|
||||
};
|
83
webpack.config.js
Normal file
83
webpack.config.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
const path = require('path');
|
||||
|
||||
const TerserPlugin = require("terser-webpack-plugin");
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
cache: false,
|
||||
entry: './src/main.tsx',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/',
|
||||
},
|
||||
stats: {
|
||||
colors: true,
|
||||
timings: true,
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({ title: "Chaos's Food Likes/Dislikes" })
|
||||
],
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: ['.tsx', '.ts', '.js', ".mjs"],
|
||||
alias: { os: false }
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(scss)$/,
|
||||
use: [{
|
||||
loader: 'style-loader',
|
||||
}, {
|
||||
loader: 'css-loader',
|
||||
}, {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
options: function () {
|
||||
return [
|
||||
require('precss'),
|
||||
require('autoprefixer')
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
loader: 'sass-loader'
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.ts(x?)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'ts-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
`...`,
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
ecma: "2015"
|
||||
}
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
],
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue