something done

This commit is contained in:
Vishnu Raghav B 2020-10-15 01:02:32 +05:30
parent fe6d6d1d60
commit eb9e2da756
15 changed files with 2891 additions and 790 deletions

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ns_accent">#3d5afe</color>
<color name="ns_accent">#ff7043</color>
</resources>

View file

@ -1,7 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ns_primary">#F5F5F5</color>
<color name="ns_primaryDark">#757575</color>
<color name="ns_accent">#33B5E5</color>
<color name="ns_blue">#272734</color>
<color name="ns_primary">
#FFFFFF
</color>
<color name="ns_primaryDark">
#ff7043
</color>
<color name="ns_accent">
#ff7043
</color>
<color name="ns_blue">
#272734
</color>
</resources>

View file

@ -25,6 +25,7 @@
<item name="colorPrimary">@color/ns_primary</item>
<item name="colorPrimaryDark">@color/ns_primaryDark</item>
<item name="colorAccent">@color/ns_accent</item>
<item name="android:forceDarkAllowed">true</item>
</style>
<style name="AppTheme" parent="AppThemeBase">

View file

@ -1,22 +1,370 @@
// NativeScript core theme
// @see https://docs.nativescript.org/ui/theme
@import '~@nativescript/theme/core';
@import "~@nativescript/theme/core";
// Override variables here
$grayD4: #212121;
$grayD3: #424242;
$grayD2: #616161;
$grayD1: #757575;
$gray: #9e9e9e;
$grayL1: #e0e0e0;
$grayL2: #eeeeee;
$grayL3: #f5f5f5;
$grayL4: #fafafa;
$orange: #ff7043;
$paleOrange: #fbe9e7;
// Global SCSS styling
// @see https://docs.nativescript.org/ui/styling
Page {
font-family: 'Orkney-Regular';
font-family: "Orkney-Regular";
}
.orkm {
font-family: 'Orkney-Medium';
font-family: "Orkney-Medium";
}
.orkb {
font-family: 'Orkney-Bold';
font-family: "Orkney-Bold";
}
.bx {
font-family: 'boxicons';
font-size: 24;
font-family: "boxicons";
font-size: 24;
}
.ns-light {
Page,
ActionBar,
SearchBar {
background: $grayL4;
color: $grayD4;
}
TabView {
tab-background-color: $grayL4;
tab-text-color: $gray;
selected-tab-text-color: $grayD4;
}
.hr {
border-color: $grayL2;
}
TextField,
TextView,
TimePickerField {
border-color: $gray;
}
.sd,
.fieldLabel {
background: $grayL4;
}
.recipe-li,
.option-highlight {
background: white;
}
.sd-item,
.sd-group-header,
.recipe-time {
color: $grayD2;
}
.selected-sd-item {
background: $paleOrange;
color: $orange;
}
.option,
.icon-option {
.bx,
.option-info {
color: $grayD2;
}
.option-title {
color: $grayD4;
}
}
.view-imageHolder {
background: $grayL2;
color: $grayL1;
}
.view-other {
color: $grayD2;
}
.view-count {
color: $grayL4;
background: $grayD4;
}
.view-instruction {
border-color: $grayD4;
}
.instructionWOBorder {
border-color: transparent;
}
}
.ns-dark {
Page,
ActionBar,
SearchBar {
background: $grayD4;
color: $grayL4;
}
TabView {
tab-background-color: $grayD4;
tab-text-color: $gray;
selected-tab-text-color: $grayL4;
}
.hr {
border-color: #111;
}
TextField,
TextView,
TimePickerField {
border-color: $gray;
}
.sd,
.fieldLabel {
background: $grayD4;
}
.recipe-li,
.option-highlight {
background: $grayD3;
}
.sd-item,
.sd-group-header,
.recipe-time {
color: $grayL2;
}
.selected-sd-item {
background: $paleOrange;
color: $orange;
}
.option,
.icon-option {
.bx,
.option-info {
color: $grayL2;
}
.option-title {
color: $grayL4;
}
}
.view-imageHolder {
background: black;
color: $grayD4;
}
.view-other {
color: $grayL2;
}
.view-count {
color: $grayD4;
background: $grayL4;
}
.view-instruction {
border-color: $grayL4;
}
.instructionWOBorder {
border-color: transparent;
}
}
// Elements
TextField,
TextView,
TimePickerField {
border-width: 1;
font-size: 15;
padding: 16;
margin: 8 0 0 0;
border-radius: 4;
placeholder-color: $gray;
}
TextView {
line-height: 12;
}
.inputField {
margin-top: 16;
}
.fieldLabel {
font-size: 13;
margin-left: 8;
padding: 0 8;
}
// ActionBar
ActionBar {
width: 100%;
margin: 0;
padding: 0;
height: 64;
.bx {
padding: 16;
}
.leftAction {
padding: 16 16 16 4;
margin: 0;
}
}
.actionBarContainer {
margin: 0;
padding: 0;
}
SearchBar {
font-size: 16;
margin-top: 4;
}
.title {
padding-left: 8;
text-align: left;
font-size: 18;
}
// Side Drawer
.sd-item {
border-radius: 4;
padding: 12 16;
font-size: 14;
}
.sd-group-header {
padding: 12;
font-size: 12;
}
// Home
.recipe-li {
margin: 8 16;
border-radius: 6;
.recipe-info {
margin: 4 0;
}
.recipe-cat {
padding: 0;
margin: 0;
}
.recipe-title {
font-size: 16;
line-height: 4;
margin: 0;
padding: 4 0;
}
.recipe-time {
padding: 0;
}
.recipe-favorite {
font-size: 14;
padding: 14 12 0 0;
}
.recipe-cat,
.recipe-favorite {
color: $orange;
}
.recipe-favorite.hide {
opacity: 0;
}
}
// Settings
.group-header {
padding: 12;
color: #ff7043;
}
.main-container {
padding: 16 8 128;
.option {
padding: 16;
border-radius: 4;
font-size: 16;
.bx {
margin: 0 24 0 0;
}
.option-info {
font-size: 12;
}
}
}
// About
.app-icon-container {
margin: 32 0;
width: 100%;
.app-icon {
width: 56;
height: 56;
margin: 0 6 0 0;
padding: 0;
}
.app-name {
font-size: 24;
}
}
.icon-option {
padding: 16;
border-radius: 4;
.bx {
margin: 0 24 0 0;
}
.option-title {
font-size: 16;
}
}
// View Recipe
.view-cat {
font-size: 14;
color: #ff7043;
}
.view-other {
font-size: 14;
}
.view-title {
font-size: 22;
}
.view-ingredient {
font-size: 14;
line-height: 6;
margin-top: 12;
}
.view-favorited {
color: #ff7043;
}
.view-count {
font-size: 10;
width: 20;
height: 20;
padding: 3 0 0;
margin-left: 6;
text-align: center;
border-radius: 100;
}
.view-instruction,
.view-note,
.view-reference {
font-size: 14;
line-height: 6;
padding: 0 0 12 24;
margin: 0 0 0 15;
border-width: 0 0 0 2;
}
.view-note {
border-width: 0;
}
.view-reference {
border-width: 0;
}
// Edit Recipe
.btnFab {
margin: 16;
}
.sectionTitle {
font-size: 20;
}
.sec-btn {
font-size: 14;
color: #ff7043;
text-align: left;
padding: 16;
margin: 8 0 0 0;
}

BIN
app/assets/images/food.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -1,76 +1,91 @@
<template>
<StackLayout class="main-container">
<StackLayout orientation="horizontal" class="app-icon-container">
<Image src="res://icon" class="app-icon" stretch="fill" />
<Label
text="EnRecipes"
verticalAlignment="center"
class="app-name orkb"
/>
</StackLayout>
<StackLayout orientation="horizontal" class="icon-option">
<Label verticalAlignment="center" class="bx" :text="icon.info" />
<StackLayout>
<Label text="Version" class="option-title" />
<Label text="1.0.0" class="option-info" textWrap="true" />
<Page>
<ActionBar :flat="viewIsScrolled ? false : true">
<!-- Settings Actionbar -->
<GridLayout rows="*" columns="auto, *" class="actionBarContainer">
<Label
class="bx leftAction"
:text="icon.menu"
automationText="Menu"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" :text="title" col="1" />
</GridLayout>
</ActionBar>
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout class="main-container">
<StackLayout
horizontalAlignment="center"
orientation="horizontal"
class="app-icon-container"
>
<Image src="res://icon" class="app-icon" stretch="fill" />
<Label
text="EnRecipes"
verticalAlignment="center"
class="app-name orkb"
/>
</StackLayout>
<StackLayout orientation="horizontal" class="icon-option">
<Label verticalAlignment="center" class="bx" :text="icon.info" />
<StackLayout>
<Label text="Version" class="option-title" />
<Label text="1.0.0" class="option-info" textWrap="true" />
</StackLayout>
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://github.com/vishnuraghavb/enrecipes')"
>
<Label class="bx" :text="icon.link" />
<Label text="View project on GitHub" class="option-title" />
</StackLayout>
<StackLayout orientation="horizontal" class="icon-option">
<Label class="bx" :text="icon.file" />
<Label text="Licenses" class="option-title" />
</StackLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Author" class="group-header" />
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://www.vishnuraghav.com')"
>
<Label class="bx" :text="icon.user" />
<Label text="Vishnu Raghav" class="option-title" />
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://github.com/vishnuraghavb')"
>
<Label class="bx" :text="icon.link" />
<Label text="Follow on GitHub" class="option-title" />
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://mastodon.social/@vishnuraghavb')"
>
<Label class="bx" :text="icon.link" />
<Label text="Follow on Mastodon" class="option-title" />
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://github.com/vishnuraghavb/enrecipes')"
>
<Label class="bx" :text="icon.link" />
<Label text="View project on GitHub" class="option-title" />
</StackLayout>
<StackLayout orientation="horizontal" class="icon-option">
<Label class="bx" :text="icon.file" />
<Label text="Licenses" class="option-title" />
</StackLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Author" class="group-header" />
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://www.vishnuraghav.com')"
>
<Label class="bx" :text="icon.user" />
<Label text="Vishnu Raghav" class="option-title" />
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://github.com/vishnuraghavb')"
>
<Label class="bx" :text="icon.link" />
<Label text="Follow on GitHub" class="option-title" />
</StackLayout>
<StackLayout
orientation="horizontal"
class="icon-option"
@tap="openURL($event, 'https://mastodon.social/@vishnuraghavb')"
>
<Label class="bx" :text="icon.link" />
<Label text="Follow on Mastodon" class="option-title" />
</StackLayout>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
import * as utils from "tns-core-modules/utils/utils"
import { mapState, mapActions } from "vuex"
export default {
props: ["highlight"],
data() {
return {
icon: {
info: "\ueda7",
link: "\ueb09",
file: "\ued60",
user: "\uee8e",
},
}
props: ["highlight", "viewIsScrolled", "showDrawer", "title"],
computed: {
...mapState(["icon"]),
},
methods: {
openURL(args, url) {
@ -80,32 +95,3 @@ export default {
},
}
</script>
<style lang="scss">
.app-icon-container {
.app-icon {
width: 56;
height: 56;
margin: 0 6 0 0;
padding: 0;
}
.app-name {
font-size: 24;
}
}
.icon-option {
padding: 16;
border-radius: 4;
background: white;
.bx {
color: #546e7a;
margin: 0 24 0 0;
}
.option-title {
color: #333333;
font-size: 16;
}
.option-info {
color: #546e7a;
}
}
</style>

View file

@ -3,7 +3,7 @@
<RadSideDrawer
ref="drawer"
allowEdgeSwipe="true"
drawerContentSize="300"
drawerContentSize="270"
showOverNavigation="true"
gesturesEnabled="true"
drawerTransition="RevealTransition"
@ -12,55 +12,59 @@
rows="auto, auto, *, auto, auto"
columns="*"
~drawerContent
backgroundColor="#ffffff"
padding="8"
class="sd"
>
<StackLayout row="0">
<StackLayout
@tap="navigateTo('EnRecipes', true, false)"
@tap="localNavigation('EnRecipes', 'EnRecipes', true, false)"
orientation="horizontal"
class="drawer-item orkm"
class="sd-item orkm"
:class="{
'selected-drawer-item':
currentComponent == 'EnRecipes' &&
'selected-sd-item':
currentComponent === 'EnRecipes' &&
!filterFavorites &&
!selectedCategory,
}"
>
<Label class="bx" :text="icon.home" margin="0 24 0 0" />
<Label text="Home" />
<Label verticalAlignment="center" text="Home" />
</StackLayout>
<StackLayout
@tap="navigateTo('Favorites', true, false)"
@tap="localNavigation('Favorites', 'Favorites', true, false)"
orientation="horizontal"
class="drawer-item orkm"
:class="{ 'selected-drawer-item': filterFavorites }"
class="sd-item orkm"
:class="{
'selected-sd-item':
currentComponent === 'EnRecipes' && filterFavorites,
}"
>
<Label class="bx" :text="icon.heart" margin="0 24 0 0" />
<Label text="Favorites" />
<Label verticalAlignment="center" text="Favorites" />
</StackLayout>
</StackLayout>
<StackLayout
orientation="horizontal"
row="1"
class="drawer-group-header orkr"
class="sd-group-header orkr"
>
<Label text="Categories" />
</StackLayout>
<ScrollView row="2">
<ScrollView row="2" scrollBarIndicatorVisible="false">
<StackLayout>
<StackLayout
@tap="navigateTo(item, false, true)"
@tap="localNavigation(item, item, false, true)"
v-for="(item, index) in categories"
:key="index"
orientation="horizontal"
class="drawer-item orkm"
class="sd-item orkm"
:class="{
'selected-drawer-item': selectedCategory == item,
'selected-sd-item':
currentComponent === 'EnRecipes' && selectedCategory == item,
}"
>
<Label class="bx" :text="icon.label" margin="0 24 0 0" />
<Label :text="item" />
<Label verticalAlignment="center" :text="item" />
</StackLayout>
</StackLayout>
</ScrollView>
@ -68,102 +72,30 @@
<StackLayout row="3" class="hr m-10"></StackLayout>
<StackLayout row="4">
<StackLayout
@tap="navigateTo(item.title, false, false)"
@tap="navigateTo(item.component, item.title)"
v-for="(item, index) in bottommenu"
:key="index"
orientation="horizontal"
class="drawer-item orkm"
class="sd-item orkm"
:class="{
'selected-drawer-item': currentComponent == item.title,
'selected-sd-item': currentComponent == item.title,
}"
>
<Label class="bx" :text="item.icon" margin="0 24 0 0" />
<Label :text="item.title" />
<Label class="bx" :text="icon[item.icon]" margin="0 24 0 0" />
<Label verticalAlignment="center" :text="item.title" />
</StackLayout>
</StackLayout>
</GridLayout>
<GridLayout ~mainContent rows="*" columns="*">
<Frame ref="page">
<Page>
<ActionBar margin="0" flat="true" row="0" col="0">
<GridLayout v-if="showSearch" rows="auto" columns="auto, *">
<Label
color="#000000"
class="bx"
padding="16 16 16 8"
margin="0"
:text="icon.back"
automationText="Back"
col="0"
@tap="closeSearch"
/>
<SearchBar
@loaded="searchBarLoaded()"
id="searchField"
col="1"
hint="Search"
textFieldHintColor="#555555"
v-model="searchQuery"
/>
</GridLayout>
<GridLayout
v-else
rows="auto"
columns="auto, *, auto, auto"
margin="0"
padding="0"
>
<Label
color="#000000"
class="bx"
padding="16 16 16 8"
margin="0"
:text="icon.menu"
automationText="Menu"
@tap="$refs.drawer.nativeView.showDrawer()"
col="0"
/>
<Label
color="#000000"
class="title orkm"
:text="title"
col="1"
/>
<Label
v-if="currentComponent == 'EnRecipes'"
color="#000000"
class="bx"
:text="icon.search"
col="2"
@tap="showSearch = true"
/>
<Label
v-if="currentComponent == 'EnRecipes'"
color="#000000"
class="bx"
:text="icon.sort"
@tap="sortTapped"
col="3"
/>
</GridLayout>
</ActionBar>
<GridLayout rows="*" columns="*">
<component
row="0"
col="0"
v-for="(component, index) in componentsArray"
v-show="component === currentComponent"
:key="index"
:is="component"
:recipes="recipes"
:selectedCategory="selectedCategory"
:searchQuery="searchQuery"
:filterFavorites="filterFavorites"
:highlight="highlight"
/>
</GridLayout>
</Page>
<Frame id="main-frame">
<!-- Home -->
<EnRecipes
:selectedCategory="selectedCategory"
:filterFavorites="filterFavorites"
:title="title"
:showDrawer="showDrawer"
/>
</Frame>
</GridLayout>
</RadSideDrawer>
@ -174,14 +106,12 @@
import * as utils from "tns-core-modules/utils/utils"
import { isAndroid } from "tns-core-modules/platform"
import * as application from "tns-core-modules/application"
import { Label } from "tns-core-modules/ui/label"
import { Menu } from "nativescript-menu"
import { Popup } from "nativescript-popup"
import EnRecipes from "./EnRecipes.vue"
import Settings from "./Settings.vue"
import About from "./About.vue"
import EnRecipes from "./EnRecipes"
import Settings from "./Settings"
import About from "./About"
import { mapState } from "vuex"
let page
export default {
@ -193,98 +123,27 @@ export default {
data() {
return {
title: "EnRecipes",
currentComponent: "EnRecipes",
componentsArray: ["EnRecipes", "Settings", "About"],
selectedCategory: null,
filterFavorites: false,
searchQuery: "",
showSearch: false,
icon: {
menu: "\ueb2a",
home: "\ued99",
heart: "\ued94",
search: "\uebbc",
sort: "\ueb2b",
cog: "\ued05",
info: "\ueda7",
label: "\uedaf",
plus: "\ueb89",
close: "\uec4e",
back: "\ue988",
},
topmenu: [
{
title: "EnRecipes",
icon: "\ued99",
},
{
title: "Favorites",
icon: "\ued94",
},
],
currentComponent: "EnRecipes",
bottommenu: [
{
title: "Settings",
icon: "\ued05",
component: Settings,
icon: "cog",
},
{
title: "About",
icon: "\ueda7",
},
],
recipes: [
{
id: 1,
img: "",
category: "Salads",
title: "Mediterranean Salad",
time: "30m",
isFavorite: true,
},
{
id: 2,
img: "",
category: "Sauces",
title: "Fresh Tomato Sauce",
time: "45m",
isFavorite: false,
},
{
id: 3,
img: "",
category: "Lunch",
title: "Creamy Mushroom Herb Pasta Creamy Mushroom Herb Pasta",
time: "30m",
isFavorite: false,
},
{
id: 4,
img: "",
category: "Lunch",
title: "Grilled Cheese Sandwich",
time: "20m",
isFavorite: false,
},
{
id: 5,
img: "",
category: "Lunch",
title: "Creamy Mushroom Herb Pasta Creamy Mushroom Herb Pasta",
time: "30m",
isFavorite: false,
},
{
id: 6,
img: "",
category: "Lunch",
title: "Grilled Cheese Sandwich",
time: "20m",
isFavorite: false,
component: About,
icon: "info",
},
],
}
},
computed: {
...mapState(["recipes", "icon"]),
categories() {
let arr = this.recipes.map((e) => {
return e.category
@ -294,188 +153,117 @@ export default {
},
methods: {
highlight(args) {
console.log(args.object.className)
let temp = args.object.className
args.object.className = `${temp} option-highlight`
setTimeout(() => {
args.object.className = temp
}, 100)
},
// Navigation
setSelectedCategory(e) {
this.selectedCategory = e.item
console.log(e)
this.closeDrawer()
},
// SearchBar
closeSearch() {
this.searchQuery = ""
this.showSearch = false
utils.ad.dismissSoftInput()
},
searchBarLoaded() {
application.android.on(
application.AndroidApplication.activityBackPressedEvent,
this.backEvent
)
},
removeBackEvent() {
application.android.off(
application.AndroidApplication.activityBackPressedEvent,
this.backEvent
)
},
// Sort
sortTapped(args) {
let anchor = args.object
const popup = new Popup({
backgroundColor: "white" | "#fff",
height: 100,
width: 100,
unit: "dp" | "px" | "%",
elevation: 10, // android only
borderRadius: 25, // android only
})
const View = new Label()
View.text = "Test"
popup.showPopup(anchor, View)
},
// Navigation
backEvent(args) {
if (this.showSearch) {
args.cancel = true
this.closeSearch()
this.removeBackEvent()
} else if (
this.currentComponent !== "EnRecipes" ||
this.filterFavorites
) {
args.cancel = true
console.log("backEvent")
args.cancel = true
if (this.$refs.drawer.nativeView.getIsOpen()) this.closeDrawer()
else if (this.currentComponent !== "EnRecipes") {
this.$navigateBack({ frame: "main-frame" })
this.currentComponent = "EnRecipes"
this.filterFavorites = false
} else if (this.filterFavorites || this.selectedCategory) {
this.title = "EnRecipes"
this.filterFavorites = false
this.selectedCategory = null
this.removeBackEvent()
}
},
navigateBackToHome() {
localNavigation(to, title, filter, filterCategory) {
// navigateBackToHome
application.android.on(
application.AndroidApplication.activityBackPressedEvent,
this.backEvent
)
console.log("navigateBackToHome")
},
navigateTo(to, filter, filterCategory) {
this.navigateBackToHome()
if (this.currentComponent !== "EnRecipes") {
this.currentComponent = "EnRecipes"
this.$navigateBack({ frame: "main-frame" })
}
this.filterFavorites = false
this.selectedCategory = null
this.title = to
this.title = title
console.log(title)
if (filter) {
this.currentComponent = "EnRecipes"
if (to === "Favorites") {
this.filterFavorites = true
}
} else if (filterCategory) {
this.currentComponent = "EnRecipes"
this.selectedCategory = to
} else {
this.currentComponent = to
}
if (to === "Favorites") this.filterFavorites = true
} else if (filterCategory) this.selectedCategory = to
if (!this.filterFavorites && !this.selectedCategory)
this.removeBackEvent()
this.closeDrawer()
},
navigateTo(to, title) {
this.currentComponent = title
this.$navigateTo(to, {
frame: "main-frame",
// transition: {
// name: "slide",
// duration: 250,
// curve: "easeIn",
// },
props: {
highlight: this.highlight,
viewIsScrolled: this.viewIsScrolled,
showDrawer: this.showDrawer,
title,
},
backstackVisible: false,
})
this.closeDrawer()
},
showDrawer() {
this.$refs.drawer.nativeView.showDrawer()
},
closeDrawer() {
this.$refs.drawer.nativeView.toggleDrawerState()
this.$refs.drawer.nativeView.closeDrawer()
},
},
}
</script>
<style lang="scss">
ActionBar {
background: white;
color: #000000;
height: 64;
.bx {
background: white;
padding: 16;
}
}
SearchBar {
background: #fff;
color: #333333;
.noResults {
width: 100%;
padding: 16;
font-size: 16;
margin-top: 4;
line-height: 8;
}
.hr {
border-color: #eeeeee;
}
.title {
text-align: left;
padding-left: 8;
font-size: 20;
.swipe-item {
margin: 0 8;
background: #ff7043;
color: #fff;
height: 128;
border-radius: 6;
}
.message {
vertical-align: center;
text-align: center;
font-size: 20;
color: #333333;
}
.drawer-item {
border-radius: 4;
padding: 12 16;
color: #546e7a;
font-size: 16;
}
.drawer-group-header {
padding: 12;
color: #546e7a;
font-size: 12;
}
.selected-drawer-item {
background: #fbe9e7;
color: #ff7043;
}
.recipe-list-item {
background: white;
margin: 16 16 0;
border-radius: 4;
&:last-of-type {
margin-bottom: 16;
}
.recipeCategory {
font-size: 14;
padding: 0;
color: #ff7043;
}
.recipeTitle {
font-size: 16;
line-height: 4;
// height: 48;
// width: 128;
}
.recipeTime {
font-size: 12;
color: #546e7a;
}
}
#categoriesMenu {
max-height: 336;
}
#btnFabContainer {
width: 100%;
height: 100%;
}
#btnFab {
.btnFab {
width: 56;
height: 56;
padding: 16;
background-color: #ff7043;
color: #000;
color: #fff;
border-radius: 28;
text-align: center;
}
// prettier-ignore
Button {
color: #ff7043;
}
</style>

View file

@ -0,0 +1,515 @@
<template>
<Page>
<ActionBar :flat="viewIsScrolled ? false : true">
<GridLayout rows="*" columns="auto, *, auto," class="actionBarContainer">
<Label
class="bx leftAction"
:text="icon.back"
automationText="Back"
col="0"
@tap="navigateBack"
/>
<Label class="title orkm" text="New recipe" col="1" />
<Label
v-if="hasEnoughDetails"
class="bx"
:text="icon.save"
col="2"
@tap="saveRecipe"
/>
</GridLayout>
</ActionBar>
<ScrollView
width="100%"
height="100%"
@scroll="onScroll($event)"
scrollBarIndicatorVisible="false"
>
<StackLayout width="100%">
<!-- Image and camera btn -->
<AbsoluteLayout>
<StackLayout
width="100%"
:height="screenWidth"
class="view-imageHolder"
verticalAlignment="center"
>
<Image
v-if="recipeContent.imageSrc"
:src="recipeContent.imageSrc"
stretch="aspectFill"
width="100%"
:height="screenWidth"
/>
<Label
v-else
horizontalAlignment="center"
class="bx"
fontSize="160"
:text="icon.dish"
/>
</StackLayout>
<StackLayout
width="100%"
horizontalAlignment="center"
:top="screenWidth - 42"
>
<Label
v-if="recipeContent.imageSrc"
class="bx btnFab"
:text="icon.close"
androidElevation="8"
@tap="removePicture()"
/>
<Label
v-else
class="bx btnFab"
:text="icon.camera"
androidElevation="8"
@tap="takePicture()"
/>
</StackLayout>
</AbsoluteLayout>
<!-- Primary information -->
<StackLayout margin="0 16">
<AbsoluteLayout class="inputField">
<TextField
width="100%"
hint="My Healthy Recipe"
v-model="recipeContent.title"
autocapitalizationType="words"
maxLength="32"
/>
<Label top="0" class="fieldLabel" text="Title" />
</AbsoluteLayout>
<AbsoluteLayout class="inputField">
<TextField
width="100%"
v-model="recipeContent.category"
editable="false"
@tap="showCategories()"
/>
<Label top="0" class="fieldLabel" text="Category" />
</AbsoluteLayout>
<GridLayout columns="*, 8, *">
<AbsoluteLayout class="inputField" col="0">
<TimePickerField
width="100%"
timeFormat="HH:mm"
pickerTitle="Approx. preparation time"
@timeChange="onPrepTimeChange"
:time="recipeContent.prepTime"
></TimePickerField>
<Label top="0" class="fieldLabel" text="Preparation time" />
</AbsoluteLayout>
<AbsoluteLayout class="inputField" col="2">
<TimePickerField
width="100%"
timeFormat="HH:mm"
pickerTitle="Approx. cooking time"
@timeChange="onCookTimeChange"
:time="recipeContent.cookTime"
></TimePickerField>
<Label top="0" class="fieldLabel" text="Cooking time" />
</AbsoluteLayout>
</GridLayout>
<GridLayout columns="*, 16, *">
<AbsoluteLayout class="inputField" col="0">
<TextField
width="100%"
keyboardType="number"
v-model="recipeContent.portionSize"
/>
<Label top="0" class="fieldLabel" text="Portion size" />
</AbsoluteLayout>
</GridLayout>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Ingredients section -->
<StackLayout margin="0 16">
<Label text="Ingredients" class="sectionTitle" />
<GridLayout
columns="*,8,auto,8,auto,8,auto"
v-for="(ingredient, index) in recipeContent.ingredients"
:key="index"
>
<TextField
col="0"
v-model="recipeContent.ingredients[index].item"
:hint="`Item ${index + 1}`"
autocapitalizationType="words"
/>
<TextField
width="72"
col="2"
v-model="recipeContent.ingredients[index].quantity"
hint="1.000"
keyboardType="number"
/>
<TextField
width="64"
col="4"
v-model="recipeContent.ingredients[index].unit"
hint="Unit"
editable="false"
@tap="showUnits($event)"
/>
<Label
verticalAlignment="center"
col="6"
padding="4"
margin="8 0 0 0"
class="bx"
:text="icon.close"
@tap="removeIngredient(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD INGREDIENT"
@tap="addIngredient()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Instructions section -->
<StackLayout margin="0 16">
<Label text="Instructions" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(instruction, index) in recipeContent.instructions"
:key="index"
>
<TextView
col="0"
:hint="`Step ${index + 1}`"
v-model="recipeContent.instructions[index]"
editable="true"
/>
<!-- <TextField
col="0"
v-model="recipeContent.instructions[index]"
:hint="`Step ${index + 1}`"
/> -->
<Label
verticalAlignment="center"
col="2"
padding="4"
margin="8 0 0 0"
class="bx"
:text="icon.close"
@tap="removeInstruction(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD STEP"
@tap="addInstruction()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- Notes section -->
<StackLayout margin="0 16">
<Label text="Notes" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(note, index) in recipeContent.notes"
:key="index"
>
<TextView
col="0"
v-model="recipeContent.notes[index]"
:hint="`Note ${index + 1}`"
editable="true"
/>
<Label
verticalAlignment="center"
col="2"
padding="4"
margin="8 0 0 0"
class="bx"
:text="icon.close"
@tap="removeNote(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD NOTE"
@tap="addNote()"
/>
<StackLayout class="hr" margin="24 16"></StackLayout>
</StackLayout>
<!-- References section -->
<StackLayout margin="0 16">
<Label text="References" class="sectionTitle" />
<GridLayout
columns="*,8,auto"
v-for="(reference, index) in recipeContent.references"
:key="index"
>
<TextField
col="0"
v-model="recipeContent.references[index]"
hint="Website or Video URL"
/>
<Label
verticalAlignment="center"
col="2"
padding="4"
margin="8 0 0 0"
class="bx"
:text="icon.close"
@tap="removeReference(index)"
/>
</GridLayout>
<Label
class="sec-btn pull-left orkm"
text="+ ADD REFERENCE"
@tap="addReference()"
/>
<StackLayout margin="32"></StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
import { screen } from "tns-core-modules/platform"
import { Mediafilepicker } from "nativescript-mediafilepicker"
import { mapState, mapActions } from "vuex"
export default {
data() {
return {
viewIsScrolled: false,
units: [
"unit",
"tsp",
"Tbsp",
"oz",
"cup",
"pt",
"qt",
"lb",
"gal",
"ml",
"L",
"mg",
"g",
"kg",
"mm",
"cm",
"m",
"in",
"°C",
"°F",
],
recipeContent: {
imageSrc: null,
title: null,
category: null,
prepTime: "00:00",
cookTime: "00:00",
portionSize: 1,
ingredients: [
{
item: null,
quantity: null,
unit: "unit",
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: false,
},
categories: [
"Appetizers",
"BBQ",
"Beverages",
"Breads",
"Breakfast",
"Desserts",
"Dinner",
"Drinks",
"Healthy",
"Lunch",
"Main dishes",
"Meat",
"Noodles",
"Pasta",
"Poultry",
"Rice",
"Salads",
"Sauces",
"Seafood",
"Side dishes",
"Snacks",
"Soups",
"Vegan",
"Vegetarian",
"ADD NEW CATEGORY",
],
}
},
computed: {
...mapState(["icon"]),
screenWidth() {
return screen.mainScreen.widthDIPs
},
hasEnoughDetails() {
let recipe = this.recipeContent
return recipe.title && recipe.category
},
},
methods: {
setTime(time) {
if (Date.parse(this.recipeContent[time])) {
let date = new Date(this.recipeContent[time])
let h = date.getHours()
let m = date.getMinutes()
this.recipeContent[time] =
(h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m)
}
// console.log(this.recipeContent[time])
},
saveRecipe() {
this.setTime("prepTime")
this.setTime("cookTime")
// console.log(this.recipeContent)
this.$store.dispatch("addRecipe", this.recipeContent)
this.$navigateBack()
},
onPrepTimeChange(args) {
this.recipeContent.prepTime = args.value
},
onCookTimeChange(args) {
this.recipeContent.cookTime = args.value
},
onScroll(args) {
args.scrollY
? (this.viewIsScrolled = true)
: (this.viewIsScrolled = false)
},
showCategories() {
action("Select a category", "Cancel", [...this.categories]).then(
(result) => {
if (result != "Cancel") this.recipeContent.category = result
}
)
},
navigateBack() {
confirm({
message:
"Are you sure you want discard unsaved changes to this recipe?",
cancelButtonText: "Keep Editing",
okButtonText: "Discard",
}).then((res) => {
if (res) {
this.$navigateBack()
}
})
},
takePicture() {
let mediafilepicker = new Mediafilepicker()
let vm = this
const options = { width: 800, height: 800, lockSquare: true }
const androidOptions = {
isFreeStyleCropEnabled: true,
statusBarColor: "black",
setAspectRatioOptions: {
defaultIndex: 0,
aspectRatios: [
{
aspectRatioTitle: "1:1",
aspectRatioX: 1,
aspectRatioY: 1,
},
{
aspectRatioTitle: "16:9",
aspectRatioX: 16,
aspectRatioY: 9,
},
{
aspectRatioTitle: "18:9",
aspectRatioX: 18,
aspectRatioY: 9,
},
],
},
}
mediafilepicker.openImagePicker({
android: {
isCaptureMood: false, // if true then camera will open directly.
isNeedCamera: true,
maxNumberFiles: 1,
isNeedFolderList: false,
},
})
mediafilepicker.on("getFiles", function(res) {
let result = res.object.get("results")
vm.recipeContent.imageSrc = result[0].file
})
},
removePicture() {
confirm({
title: "Delete Recipe Photo",
message: "Are you sure you want to delete the recipe photo?",
okButtonText: "Delete",
cancelButtonText: "Cancel",
}).then((e) => {
if (e) this.recipeContent.imageSrc = null
})
},
addIngredient() {
this.recipeContent.ingredients.push({
item: null,
quantity: null,
unit: "unit",
})
},
removeIngredient(index) {
this.recipeContent.ingredients.splice(index, 1)
},
addInstruction() {
this.recipeContent.instructions.push("")
},
removeInstruction(index) {
this.recipeContent.instructions.splice(index, 1)
},
addNote() {
this.recipeContent.notes.push("")
},
removeNote(index) {
this.recipeContent.notes.splice(index, 1)
},
addReference() {
this.recipeContent.references.push("")
},
removeReference(index) {
this.recipeContent.references.splice(index, 1)
},
showUnits(e) {
action("Select measuring unit", "Cancel", [...this.units]).then(
(result) => {
if (result != "Cancel") e.object.text = result
}
)
},
},
}
</script>

View file

@ -1,109 +1,186 @@
<template>
<AbsoluteLayout>
<ScrollView scrollBarIndicatorVisible="false" width="100%" height="100%">
<StackLayout v-if="filteredRecipes.length">
<!-- <RadListView
ref="listView"
for="item in filteredRecipes"
swipeActions="true"
@itemSwipeProgressStarted="onSwipeStarted"
>
<v-template>
<StackLayout class="item p-t-10" orientation="vertical">
<Label :text="item.name" class="nameLabel"></Label>
<Label :text="item.description" class="descriptionLabel"></Label>
</StackLayout>
</v-template>
<v-template name="itemswipe">
<GridLayout columns="auto, *, auto" backgroundColor="White">
<StackLayout
id="mark-view"
col="0"
class="swipe-item left"
orientation="horizontal"
@tap="onLeftSwipeClick"
>
<Label
text="mark"
verticalAlignment="center"
horizontalAlignment="center"
/>
</StackLayout>
<StackLayout
id="delete-view"
col="2"
class="swipe-item right"
orientation="horizontal"
@tap="onRightSwipeClick"
>
<Label
text="delete"
verticalAlignment="center"
horizontalAlignment="center"
/>
</StackLayout>
</GridLayout>
</v-template>
</RadListView>-->
<GridLayout
v-for="recipe in filteredRecipes"
:key="recipe.id"
rows="128"
columns="128, *"
class="recipe-list-item"
androidElevation="2"
>
<Image src="res://icon" stretch="fill" col="0" />
<StackLayout
col="1"
horizontalAlignment="left"
verticalAlignment="top"
margin="16"
>
<Label :text="recipe.category" class="orkm recipeCategory" />
<Label :text="recipe.title" class="orkb recipeTitle" />
<Label :text="recipe.time" class="orkr recipeTime" />
</StackLayout>
</GridLayout>
<StackLayout height="128"></StackLayout>
</StackLayout>
<Label
<Page>
<ActionBar :flat="viewIsScrolled ? false : true">
<!-- Search Actionbar -->
<GridLayout
v-if="showSearch"
rows="*"
columns="auto, *"
class="actionBarContainer"
>
<Label
class="bx leftAction"
:text="icon.back"
automationText="Back"
col="0"
@tap="closeSearch"
/>
<SearchBar
@loaded="searchBarLoaded"
id="searchField"
col="1"
hint="Search"
textFieldHintColor="#bdbdbd"
v-model="searchQuery"
/>
</GridLayout>
<!-- Home Actionbar -->
<GridLayout
v-else
class="recipe-list-item"
rows="*"
columns="auto, *, auto,"
class="actionBarContainer"
>
<Label
class="bx leftAction"
:text="icon.menu"
automationText="Menu"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" :text="title" col="1" />
<Label
class="bx"
:text="icon.search"
col="2"
@tap="showSearch = true"
/>
</GridLayout>
</ActionBar>
<AbsoluteLayout>
<RadListView
v-if="filteredRecipes.length"
ref="listView"
for="recipe in filteredRecipes"
swipeActions="true"
@itemSwipeProgressChanged="onSwiping"
@itemSwipeProgressEnded="onSwipeEnded"
@scrolled="onScroll($event)"
@itemTap="viewRecipe"
>
<v-template>
<GridLayout
class="recipe-li"
rows="128"
columns="auto, *, auto"
androidElevation="2"
>
<Image
src="res://icon"
stretch="fill"
col="0"
width="128"
height="128"
/>
<StackLayout
class="recipe-info"
col="1"
horizontalAlignment="left"
verticalAlignment="top"
>
<Label :text="recipe.category" class="orkm h4 recipe-cat" />
<Label :text="recipe.title" class="orkm recipe-title" />
<Label
:text="recipeTotalTime(recipe.prepTime, recipe.cookTime)"
class="h4 recipe-time"
/>
</StackLayout>
<Label
verticalAlignment="top"
col="2"
class="bx recipe-favorite"
:class="{ hide: !recipe.isFavorite }"
:text="icon.heart"
/>
</GridLayout>
</v-template>
<v-template name="itemswipe">
<GridLayout columns="auto, *, auto">
<StackLayout
id="favorite-action"
col="0"
class="swipe-item left"
verticalAlignment="top"
>
<Label class="bx" padding="8 6 0 0" :text="icon.heart" />
</StackLayout>
<StackLayout
id="delete-action"
col="2"
class="swipe-item right"
verticalAlignment="top"
>
<Label class="bx" padding="8 0 0 6" :text="icon.trash" />
</StackLayout>
</GridLayout>
</v-template>
</RadListView>
<Label
v-if="!filteredRecipes.length && searchQuery"
class="noResults"
horizontalAlignment="center"
:text="
`Your search '${searchQuery}' did not match any recipes in this category.`
`Your search &quot;${searchQuery}&quot; did not match any recipes in this category.`
"
textAlignment="center"
textWrap="true"
/>
</ScrollView>
<GridLayout id="btnFabContainer" rows="*,88" columns="*,88">
<Label
row="1"
col="1"
id="btnFab"
class="bx"
:text="icon.plus"
androidElevation="8"
@tap="FabTapped"
v-if="!filteredRecipes.length && filterFavorites && !searchQuery"
class="noResults"
horizontalAlignment="center"
text="Your favorite recipes will be listed here."
textAlignment="center"
textWrap="true"
/>
</GridLayout>
</AbsoluteLayout>
<GridLayout
id="btnFabContainer"
rows="*,88"
columns="*,88"
v-if="!showSearch"
>
<Label
row="1"
col="1"
class="bx btnFab"
:text="icon.plus"
androidElevation="8"
@tap="addRecipe"
/>
</GridLayout>
</AbsoluteLayout>
</Page>
</template>
<script>
export default {
props: ["searchQuery", "filterFavorites", "recipes", "selectedCategory"],
import * as utils from "tns-core-modules/utils/utils"
import * as application from "tns-core-modules/application"
import * as gestures from "tns-core-modules/ui/gestures"
import { ObservableArray } from "tns-core-modules/data/observable-array"
import EditRecipe from "./EditRecipe.vue"
import ViewRecipe from "./ViewRecipe.vue"
import { mapState, mapActions } from "vuex"
export default {
props: ["filterFavorites", "selectedCategory", "title", "showDrawer"],
components: {
EditRecipe,
ViewRecipe,
},
data() {
return {
icon: {
plus: "\ueb89",
},
searchQuery: "",
showSearch: false,
viewIsScrolled: false,
leftAction: false,
rightAction: false,
}
},
computed: {
...mapState(["recipes", "icon"]),
recipesByCategory() {
return this.recipes.reduce((acc, e) => {
acc[e.category] = [...(acc[e.category] || []), e]
@ -130,12 +207,114 @@ export default {
},
},
methods: {
onSwiping({ data, object }) {
const swipeLimits = data.swipeLimits
const swipeView = object
const leftItem = swipeView.getViewById("favorite-action")
const rightItem = swipeView.getViewById("delete-action")
swipeLimits.left = leftItem.getMeasuredWidth() - 12
swipeLimits.right = rightItem.getMeasuredWidth() - 12
swipeLimits.threshold = swipeLimits.left - 4
if (data.x > swipeLimits.threshold) {
this.leftAction = true
this.$refs.listView.notifySwipeToExecuteFinished()
} else if (data.x < -swipeLimits.threshold) {
this.rightAction = true
this.$refs.listView.notifySwipeToExecuteFinished()
}
},
onSwipeEnded({ index }) {
let context = this.recipes.indexOf(this.filteredRecipes[index])
if (this.leftAction) this.toggleFavorite(context)
else if (this.rightAction) this.deleteRecipe()
this.leftAction = this.rightAction = false
},
toggleFavorite(index) {
this.$store.dispatch("toggleFavorite", index)
},
deleteRecipe() {
alert("Are you sure you want to delete?")
},
// swipeAction(args, index) {
// let vm = this
// args.object.on(gestures.GestureTypes.swipe, function(args) {
// console.log("Swipe Direction: " + args.direction)
// if (args.direction === 1) {
// vm.filteredRecipes[index].isFavorite = !vm.filteredRecipes[index]
// .isFavorite
// console.log(vm.filteredRecipes[index].isFavorite)
// }
// })
// },
recipeTotalTime(prepTime, cookTime) {
let pT = prepTime.split(":")
let cT = cookTime.split(":")
let hrs = parseInt(pT[0]) + parseInt(cT[0])
let mins = parseInt(pT[1]) + parseInt(cT[1])
if (mins > 60) {
hrs += Math.floor(mins / 60)
mins -= 60
}
return hrs ? `${hrs}h ${mins}m` : `${mins}m`
},
onScroll(args) {
args.scrollOffset
? (this.viewIsScrolled = true)
: (this.viewIsScrolled = false)
},
// SearchBar
searchBarLoaded() {
application.android.on(
application.AndroidApplication.activityBackPressedEvent,
this.backEvent
)
},
backEvent(args) {
if (this.showSearch) {
args.cancel = true
this.closeSearch()
}
this.removeBackEvent()
},
removeBackEvent() {
application.android.off(
application.AndroidApplication.activityBackPressedEvent,
this.backEvent
)
},
closeSearch() {
this.searchQuery = ""
this.showSearch = false
utils.ad.dismissSoftInput()
},
FabTapped() {
alert("fab tapped")
},
},
mounted() {
// console.log(this.recipesByCategory)
addRecipe() {
this.$navigateTo(EditRecipe, {
transition: {
name: "slide",
duration: 250,
curve: "easeIn",
},
props: {
viewIsScrolled: this.viewIsScrolled,
},
})
},
viewRecipe({ item }) {
// console.log(item)
this.$navigateTo(ViewRecipe, {
transition: {
name: "fade",
duration: 250,
curve: "easeIn",
},
props: {
recipe: item,
},
})
},
},
}
</script>

View file

@ -1,79 +1,105 @@
<template>
<StackLayout class="main-container">
<Label text="Interface" class="group-header" />
<Page>
<ActionBar :flat="viewIsScrolled ? false : true">
<!-- Settings Actionbar -->
<GridLayout rows="*" columns="auto, *" class="actionBarContainer">
<Label
class="bx leftAction"
:text="icon.menu"
automationText="Menu"
@tap="showDrawer"
col="0"
/>
<Label class="title orkm" :text="title" col="1" />
</GridLayout>
</ActionBar>
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout class="main-container">
<Label text="Interface" class="group-header" />
<StackLayout orientation="horizontal" class="option" @tap="selectThemes">
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
<StackLayout>
<Label text="Theme" class="option-title" />
<Label :text="themeName" class="option-info" textWrap="true" />
<StackLayout
orientation="horizontal"
class="option"
@tap="selectThemes"
>
<Label verticalAlignment="center" class="bx" :text="icon.theme" />
<StackLayout>
<Label text="Theme" class="option-title" />
<Label :text="themeName" class="option-info" textWrap="true" />
</StackLayout>
</StackLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Backup/Restore" class="group-header" />
<StackLayout
orientation="horizontal"
class="option"
@tap="selectBackupDir"
>
<Label verticalAlignment="center" class="bx" :text="icon.folder" />
<StackLayout>
<Label text="EnRecipes Backup Directory" class="option-title" />
<Label text="/storage/emulated/0/EnRecipes" class="option-info" />
</StackLayout>
</StackLayout>
<StackLayout orientation="horizontal" class="option" @tap="backupData">
<Label class="bx" :text="icon.backup" />
<Label text="Backup Data" class="option-title" />
</StackLayout>
<StackLayout orientation="horizontal" class="option" @tap="restoreData">
<Label class="bx" :text="icon.restore" />
<Label text="Restore Data" class="option-title" />
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout class="hr m-10"></StackLayout>
<Label text="Backup/Restore" class="group-header" />
<StackLayout orientation="horizontal" class="option" @tap="selectBackupDir">
<Label verticalAlignment="center" class="bx" :text="icon.folder" />
<StackLayout>
<Label text="EnRecipes Backup Directory" class="option-title" />
<Label text="/storage/emulated/0/EnRecipes" class="option-info" />
</StackLayout>
</StackLayout>
<StackLayout orientation="horizontal" class="option" @tap="backupData">
<Label class="bx" :text="icon.backup" />
<Label text="Backup Data" class="option-title" />
</StackLayout>
<StackLayout orientation="horizontal" class="option" @tap="restoreData">
<Label class="bx" :text="icon.restore" />
<Label text="Restore Data" class="option-title" />
</StackLayout>
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
import { Menu } from "nativescript-menu"
import * as permissions from "nativescript-permissions"
import * as application from "tns-core-modules/application"
import { getString, setString } from "application-settings"
import Theme from "@nativescript/theme"
import { mapState, mapActions } from "vuex"
export default {
props: ["highlight"],
props: ["highlight", "viewIsScrolled", "showDrawer", "title"],
data() {
return {
icon: {
theme: "\ued09",
folder: "\ued7c",
backup: "\uee48",
restore: "\ueadc",
},
options: {
interface: [
{
title: "Theme",
subTitle: "Light",
icon: "\ued09",
},
],
backupRestore: [
{
title: "EnRecipes Backup Directory",
subTitle: "/storage/emulated/0/EnRecipes",
icon: "\ued7c",
},
{
title: "Backup Data",
subTitle: null,
icon: "\uee48",
},
{
title: "Restore Data",
subTitle: null,
icon: "\ueadc",
},
],
interface: {
theme: {
title: "Theme",
subTitle: "Light",
icon: "\ued09",
},
},
backupRestore: [
{
title: "EnRecipes Backup Directory",
subTitle: "/storage/emulated/0/EnRecipes",
icon: "\ued7c",
},
{
title: "Backup Data",
subTitle: null,
icon: "\uee48",
},
{
title: "Restore Data",
subTitle: null,
icon: "\ueadc",
},
],
themeName: "Light",
themesArray: ["Light", "Dark", "Black"],
themesArray: ["Light", "Dark"],
}
},
computed: {
...mapState(["icon"]),
},
methods: {
selectThemes(args) {
this.highlight(args)
@ -83,10 +109,10 @@ export default {
actions: this.themesArray,
})
.then((action) => {
if (this.themesArray.includes(action.title)) {
this.options.interface[0].subTitle = this.themeName = action.title
}
// alert(action.id + " - " + action.title)
this.interface.theme.subTitle = this.themeName = action.title
console.log(this.themeName)
setString("application-theme", action.title)
Theme.toggleMode()
})
.catch(console.log)
},
@ -127,35 +153,11 @@ export default {
})
},
},
created() {
this.interface.theme.subTitle = this.themeName = getString(
"application-theme",
"Light"
)
},
}
</script>
<style lang="scss">
.option-highlight {
background-color: #eeeeee;
}
.group-header {
padding: 12;
color: #ff7043;
}
.main-container {
padding: 16 8 0;
.option {
padding: 16;
border-radius: 4;
background: transparent;
font-size: 16;
.bx {
margin: 0 24 0 0;
color: #546e7a;
}
.option-title {
color: #333333;
background: transparent;
}
.option-info {
font-size: 12;
color: #546e7a;
}
}
}
</style>

View file

@ -0,0 +1,289 @@
<template>
<Page>
<ActionBar margin="0" flat="true">
<GridLayout
rows="*"
columns="auto, *, auto,auto"
class="actionBarContainer"
>
<Label
col="0"
class="bx leftAction"
:text="icon.back"
automationText="Back"
@tap="navigateBack"
verticalAlignment="top"
/>
<Label
class="title orkm"
:text="recipe.title"
lineHeight="4"
col="1"
textWrap="true"
verticalAlignment="bottom"
/>
<Label
col="2"
class="bx"
:text="icon.share"
@tap=""
verticalAlignment="top"
/>
<Label
col="3"
class="bx"
:class="{ 'view-favorited': recipe.isFavorite }"
:text="recipe.isFavorite ? icon.heart : icon.heartOutline"
@tap="toggleFavorite"
verticalAlignment="top"
/>
</GridLayout>
</ActionBar>
<AbsoluteLayout>
<TabView androidElevation="0" width="100%" height="100%">
<TabViewItem title="Overview">
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout class="">
<StackLayout
width="100%"
:height="screenWidth"
verticalAlignment="center"
class="view-imageHolder"
>
<Image
v-if="recipe.imageSrc"
:src="recipe.imageSrc"
stretch="aspectFill"
width="100%"
:height="screenWidth"
/>
<Label
v-else
horizontalAlignment="center"
class="bx"
fontSize="160"
:text="icon.dish"
/>
</StackLayout>
<StackLayout margin="16 16 128">
<Label class="view-cat orkm" :text="recipe.category" />
<Label
class="view-title p-b-8 orkm"
:text="recipe.title"
textWrap="true"
/>
<Label
class="view-other"
:text="`Preparation time: ${getTime(recipe.prepTime)}`"
/>
<Label
class="view-other"
:text="`Cooking time: ${getTime(recipe.cookTime)}`"
/>
<Label
class="view-other"
:text="`Portion size: ${recipe.portionSize}`"
/>
</StackLayout>
</StackLayout>
</ScrollView>
</TabViewItem>
<TabViewItem title="Ingredients" v-if="recipe.ingredients[0].item">
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout padding="16 16 128">
<AbsoluteLayout class="inputField">
<TextField
width="50%"
v-model="portionScale"
keyboardType="number"
/>
<Label top="0" class="fieldLabel" text="Set portion size" />
</AbsoluteLayout>
<StackLayout margin="24 0 8 0">
<Label
class="view-title p-b-8 orkm"
:text="
`Ingredients for ${portionScale}${
portionScale > 1
? ' portions'
: portionScale == 0
? '1 portion'
: ' portion'
}`
"
textWrap="true"
/>
<Label
class="view-ingredient"
v-for="(item, index) in recipe.ingredients"
:key="index"
:text="
`${roundedQuantity(item.quantity)}${
item.unit ? ' ' + item.unit : ''
} ${item.item}`
"
textWrap="true"
/>
</StackLayout>
</StackLayout>
</ScrollView>
</TabViewItem>
<TabViewItem title="Instructions" v-if="recipe.instructions[0].length">
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout padding="16 16 128">
<GridLayout
columns="auto ,*"
v-for="(instruction, index) in recipe.instructions"
:key="index"
>
<Label
col="0"
colSpan="2"
class="view-instruction"
:class="{
instructionWOBorder:
index === recipe.instructions.length - 1,
}"
:text="instruction"
textWrap="true"
/>
<Label
verticalAlignment="top"
horizontalAlignment="center"
class="view-count orkb"
col="0"
:text="index + 1"
/>
</GridLayout>
</StackLayout>
</ScrollView>
</TabViewItem>
<TabViewItem title="Notes" v-if="recipe.notes[0].length">
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout padding="16 16 128">
<GridLayout
columns="auto ,*"
v-for="(note, index) in recipe.notes"
:key="index"
>
<Label
col="0"
colSpan="2"
class="view-note"
:text="note"
textWrap="true"
/>
<Label
verticalAlignment="top"
horizontalAlignment="center"
class="view-count orkb"
col="0"
:text="index + 1"
/>
</GridLayout>
</StackLayout>
</ScrollView>
</TabViewItem>
<TabViewItem title="References" v-if="recipe.references[0].length">
<ScrollView scrollBarIndicatorVisible="false">
<StackLayout padding="16 16 128">
<GridLayout
columns="auto ,*"
v-for="(reference, index) in recipe.references"
:key="index"
@tap="openURL($event, reference)"
>
<Label
col="0"
colSpan="2"
class="view-reference"
:text="reference"
textWrap="true"
/>
<Label
verticalAlignment="top"
horizontalAlignment="center"
class="orkb view-count"
col="0"
:text="index + 1"
/>
</GridLayout>
</StackLayout>
</ScrollView>
</TabViewItem>
</TabView>
<GridLayout id="btnFabContainer" rows="*,88" columns="*,88">
<Label
row="1"
col="1"
class="bx btnFab"
:text="icon.edit"
androidElevation="8"
@tap="editRecipe"
/>
</GridLayout>
</AbsoluteLayout>
</Page>
</template>
<script>
import { screen } from "tns-core-modules/platform"
import * as utils from "tns-core-modules/utils/utils"
import { mapState, mapActions } from "vuex"
export default {
props: ["recipe"],
data() {
return {
portionScale: 1,
}
},
computed: {
...mapState(["icon", "recipes"]),
screenWidth() {
return screen.mainScreen.widthDIPs
},
isPortionScalePositive() {
return this.portionScale && !isNaN(this.portionScale)
? parseFloat(this.portionScale)
: 1
},
},
methods: {
roundedQuantity(quantity, unit) {
return Math.round(quantity * this.isPortionScalePositive * 100) / 100
},
// indexChange(args) {
// let newIndex = args.value
// console.log("Current tab index: " + newIndex)
// },
navigateBack() {
this.$navigateBack()
},
editRecipe() {
alert("edit recipe")
},
toggleFavorite() {
this.$store.dispatch("toggleFavorite", this.recipes.indexOf(this.recipe))
},
getTime(time) {
let t = time.split(":")
let h = t[0]
let m = t[1]
return h !== "00" ? `${h}h ${m}m` : `${m}m`
},
openURL(args, url) {
utils.openUrl(url)
},
},
}
</script>
<style lang="scss" scoped>
ActionBar {
height: 128;
}
.actionBarContainer .bx {
margin-top: 4;
}
</style>

View file

@ -1,18 +1,29 @@
// import VueDevtools from "nativescript-vue-devtools"
import Vue from "nativescript-vue"
import App from "./components/App"
// import RadListView from "nativescript-ui-listview/vue"
import RadListView from "nativescript-ui-listview/vue"
Vue.registerElement(
"CheckBox",
() => require("@nstudio/nativescript-checkbox").CheckBox,
{
model: {
prop: "checked",
event: "checkedChange",
},
}
)
// Vue.use('RadListView')
// Vue.registerElement(
// "RadListView",
// () => require("nativescript-ui-listview/vue").RadListView
// )
Vue.use(RadListView)
// Vue.use(VueDevtools)
// Vue.registerElement(
// "CheckBox",
// () => require("@nstudio/nativescript-checkbox").CheckBox,
// {
// model: {
// prop: "checked",
// event: "checkedChange",
// },
// }
// )
import DateTimePicker from "nativescript-datetimepicker/vue"
Vue.use(DateTimePicker)
// import VueDevtools from 'nativescript-vue-devtools'
// import { TNSFontIcon, fonticon } from 'nativescript-fonticon'

View file

@ -1,16 +1,237 @@
import Vue from 'vue';
import Vuex from 'vuex';
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex);
Vue.use(Vuex)
export default new Vuex.Store({
state: {
recipes: [
{
imageSrc: null,
title: "Mediterranean Salad",
category: "Salads",
prepTime: "00:10",
cookTime: "00:20",
portionSize: 1,
ingredients: [
{
item: "Cucumbers, Seeded And Sliced",
quantity: 3,
unit: null,
},
{
item: "Crumbled Feta Cheese",
quantity: 1.5,
unit: "cup",
},
{
item: "Black Olives, Pitted And Sliced",
quantity: 1,
unit: "cup",
},
{
item: "Diced Roma Tomatoes",
quantity: 3,
unit: "cup",
},
{
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
quantity: 0.3,
unit: "cup",
},
{
item: "Onion, Sliced",
quantity: 1.5,
unit: null,
},
{
item: "Cucumbers, Seeded And Sliced",
quantity: 3,
unit: null,
},
{
item: "Crumbled Feta Cheese",
quantity: 1.5,
unit: "cup",
},
{
item: "Black Olives, Pitted And Sliced",
quantity: 1,
unit: "cup",
},
{
item: "Diced Roma Tomatoes",
quantity: 3,
unit: "cup",
},
{
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
quantity: 0.3,
unit: "cup",
},
{
item: "Onion, Sliced",
quantity: 1.5,
unit: null,
},
{
item: "Cucumbers, Seeded And Sliced",
quantity: 3,
unit: null,
},
{
item: "Crumbled Feta Cheese",
quantity: 1.5,
unit: "cup",
},
{
item: "Black Olives, Pitted And Sliced",
quantity: 1,
unit: "cup",
},
{
item: "Diced Roma Tomatoes",
quantity: 3,
unit: "cup",
},
{
item: "Diced Oil Packed Sun Dried Tomatoes, Drained, Oil Reserved",
quantity: 0.3,
unit: "cup",
},
{
item: "Onion, Sliced",
quantity: 1.5,
unit: null,
},
],
instructions: [
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
"Chill until serving.",
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion. In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
"Chill until serving.",
"Chill until serving.",
"In a large salad bowl, toss together the cucumbers, feta cheese, olives, roma tomatoes, sun-dried tomatoes, 2 tablespoons reserved sun-dried tomato oil, and red onion.",
"Chill until serving.",
"Chill until serving.",
"Chill until serving.",
"Chill until serving.",
"Chill until serving.",
],
notes: [
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
"Per Serving: 130.6 calories; protein 5.5g 11% DV; carbohydrates 9.3g 3% DV; fat 8.8g 14% DV; cholesterol 25mg 8% DV; sodium 486.4mg 20% DV.",
],
references: [
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
"https://www.allrecipes.com/recipe/14403/mediterranean-greek-salad/",
],
isFavorite: true,
},
{
imageSrc: null,
title: "Fresh Tomato Sauce",
category: "Sauces",
prepTime: "00:20",
cookTime: "00:25",
portionSize: 1,
ingredients: [
{
item: null,
quantity: null,
unit: null,
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: true,
},
{
imageSrc: null,
title: "Creamy Mushroom Herb Pasta",
category: "Lunch",
prepTime: "00:45",
cookTime: "00:25",
portionSize: 1,
ingredients: [
{
item: null,
quantity: null,
unit: null,
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: false,
},
{
imageSrc: null,
title: "Grilled Cheese Sandwich",
category: "Lunch",
prepTime: "00:20",
cookTime: "00:10",
portionSize: 1,
ingredients: [
{
item: null,
quantity: null,
unit: null,
},
],
instructions: [""],
notes: [""],
references: [""],
isFavorite: false,
},
],
viewIsScrolled: false,
icon: {
home: "\ued99",
heart: "\ued94",
heartOutline: "\uead6",
label: "\uedaf",
cog: "\ued05",
info: "\ueda7",
menu: "\ueb2a",
search: "\uebbc",
sort: "\ueb2b",
plus: "\ueb89",
close: "\uec4e",
dish: "\uea71",
back: "\ue988",
save: "\uee48",
camera: "\uecc2",
share: "\uee51",
edit: "\uee17",
theme: "\ued09",
folder: "\ued7c",
backup: "\uee48",
restore: "\ueadc",
link: "\ueb09",
file: "\ued60",
user: "\uee8e",
trash: "\uee83",
},
},
mutations: {
addRecipe(state, recipe) {
state.recipes.push(recipe)
},
toggleFavorite(state, index) {
state.recipes[index].isFavorite = !state.recipes[index].isFavorite
},
},
actions: {
}
});
addRecipe({ commit }, recipe) {
commit("addRecipe", recipe)
},
toggleFavorite({ commit }, index) {
commit("toggleFavorite", index)
},
},
})

1064
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -14,16 +14,25 @@
"version": "6.5.0"
}
},
"scripts": {
"run": "tns run"
},
"dependencies": {
"@nativescript/theme": "^2.2.1",
"@nstudio/nativescript-checkbox": "^1.0.0",
"@vue/devtools": "^5.3.3",
"nativescript-camera": "^4.5.0",
"nativescript-datetimepicker": "^1.2.3",
"nativescript-fonticon": "^2.0.2",
"nativescript-mediafilepicker": "^4.0.0",
"nativescript-menu": "^1.1.6",
"nativescript-permissions": "^1.3.9",
"nativescript-popup": "^1.5.0",
"nativescript-ui-listview": "^9.0.2",
"nativescript-socketio": "^3.3.1",
"nativescript-toasty": "^3.0.0-alpha.2",
"nativescript-ui-listview": "^8.2.0",
"nativescript-ui-sidedrawer": "^8.0.1",
"nativescript-vue": "^2.6.1",
"nativescript-vue-devtools": "^1.4.0",
"tns-core-modules": "^6.5.1",
"vuex": "^3.3.0"
},