vue-toolkit-sample v0.1.0
Explorer Vue Toolkit
Outline
Project Setup
Vue CLI 3 Setup
Refer to the Vue CLI 3 Documentation if needed. Vue CLI requires Node.js version 8.9 or above. To install globally, run:
npm install -g @vue/cliThen run:
vue uiAfter the process completes, navigate to http://localhost:8000/project/select. From this area, you can manage all of your Vue projects.
- Click Create at the top center of the page. Choose where you would like the project folder to be created, then click Create a new project here at the bottom of the page.
- On the Details page, set the project folder's name and set Package Manger to npm. Likely, you'll want to uncheck the Initialize git repository option. After you have selected the options on this page, click Next.
- On the Presets page choose Manual and click Next.
- On the Features page make sure these are toggled on: Babel, TypeScript, Router, Vuex, Css Pre-processor, and Linter / Formatter, and Use config files. Click Next.
- On the Configuration page, set the CSS Pre-processor to SCSS/SASS and the linter / formatter config to TSLint. Click Create Project.
To save time on new future projects, save this configuration into a new preset. Then, instead of choosing Manual on the Presets page, you will have the ability to choose your saved preset.
To run the project, navigate to the project folder and run:
npm run serveThis will serve the project at: http://localhost:8080
Next, if you haven't previously logged into npm, you must do so in order to access Explorer private packages. To login, run:
npm loginFinally, install the Explorer Vue Toolkit by running:
npm install --save @explorerpipeline/vue-toolkitSetup TSLint
In the tsconfig.json file add allowJs and change the strict property.
{
"compilerOptions": {
"allowJs": true,
"strict": false
}
}In the tslint.json file, set the following rules for consistency across Explorer projects:
"quotemark": [true, "double"],
"semicolon": [true, "always"],
"trailing-comma": [true, {"multiline": "never", "singleline": "never"}]Import Javascript Files
In the main.ts file, import the toolkit's main.js file and import any other javascript files needed.
import "@explorerpipeline/vue-toolkit/main.js";
import "jquery-ui/ui/widgets/slider.js"; // Example (not required)Styles
File Structure
Within the src folder, create a folder called styles. Add the following files and folder for this file structure:
src
|-- styles
| |-- imports
| | |-- _768up.scss
| | |-- _1080up.scss
| | |-- _1200up.scss
| | |-- _base.scss
| | |-- _colors.scss
| | |-- _variables.scss
| |-- app.scssImports
Paste this code into app.scss to setup styling:
@import "../../node_modules/@explorerpipeline/vue-toolkit/styles/variables";
@import "./imports/_colors";
@import "../../node_modules/@explorerpipeline/vue-toolkit/styles/toolkit";
// Base
@import "./imports/_base";
// Medium devices (tablets, 768px and up)
@media (min-width: 768px) {
@import "./imports/_768up";
}
// Large devices (desktops, 1080px and up)
@media (min-width: 1080px) {
@import "./imports/_1080up";
}
// Extra large devices (large desktops, 1200px and up)
@media (min-width: 1200px) {
@import "./imports/_1200up";
}Adding Styles to the Project
At the bottom of App.vue, import the app.scss file within the style tag.
<style lang="scss">
@import "./styles/app";
</style>Background Image Urls
When adding background images within stylesheets, place the image in the src/assets folder. Then, reference the image like this:
background-image: url('../src/assets/image-name.svg');Router
Vue Router Documentation
If your project uses the Vue Router, you may view additional documentation here: https://router.vuejs.org
Initial Path
The initial path of "/" will need to redirect to a page for the Navigation Component's NavigationItems Component to highlight the page accordingly.
routes: [
{
path: "/",
redirect: "/dashboard"
}
]If the initial path needs to go to a page not linked in the Navigation Component's NavigationItems Component, such as Home, remove the redirect and add the component property.
routes: [
{
path: "/",
component: Home
}
]Page Path
Each page requires the following properties: path, component.
routes: [
{
path: "/dashboard",
component: Dashboard
}
]Child Path
When using the SubNavigation Component, the route requires child paths. There will always be an "index" path with a value of "". The toolkit's EmptyState Component can be designated as the component to use on this path (or any other custom component). Note: the EmptyState component will be hidden on mobile devices.
import Areas from "./Areas"
import Area from "./Area"
import { EmptyState } from "@explorerpipeline/vue-toolkit/Toolkit"
routes: [
{
path: "/areas",
component: Areas,
children: [
{
path: "",
name: "Areas",
component: EmptyState
},
{
path: "/areas/:id",
component: Area
}
]
}
]Error Path
This path catches any path that doesn't match a declared path. The toolkit's FourZeroFour Component (or any other custom component) can be used to handle requests to this route.
import { FourZeroFour } from "@explorerpipeline/vue-toolkit/Toolkit";
routes: [
{
path: "*",
component: FourZeroFour,
name: "404"
}
]Shortcuts
The file Toolkit.ts may be used as a shortcut to import the Components, Models, and Services within the toolkit.
Example:
import { Component, Vue } from "vue-property-decorator";
import { Page, KeyDisplayPair, ModalService } from "@explorerpipeline/vue-toolkit/Toolkit"
@Component({
components: {
Page
}
})
export default Example extends Vue {
// Data
private locationOptions = [
new KeyDisplayPair({
key: 10,
display: "Citywide"
})
];
// Methods
private close() {
ModalService.closeModal();
}
}Components
Application Component
This component requires the props:
This component has the optional props:
hide-user: Booleanhide-apps: Boolean
This component structures the navigation, page content, and modals. It is the only element needed in the App component.
<template>
<application :branding="branding" :nav-items="navItems" :hide-user="true" :hide-apps="true" />
</template>This component has two optional slots: left-nav and right-nav.
<application :branding="branding" :nav-items="navItems">
<h1 slot="left-nav">Left</h1>
<h1 slot="right-nav">Right</h1>
</application>Navigation Components
Navigation Component
This component is included in the Application Component and is responsible for the primary navigation of the app.
This component requires the props:
NavigationItems Component
This component is used to set links in the Navigation Component and the SubNavigation Component.
This component requires the prop:
nav-items: Array of NavItem
When used in the SubNavigation Component, the icon property on the NavItem Class is ignored.
Branding Component
This component displays the icon and app label in the Navigation Component.
This component requires the prop:
branding: Branding
This component has an optional slot.
<branding :branding="branding">
<p>Slot content goes here</p>
</branding>Search Component
This component allows a user to search items of different types and then navigate directly to them.
This component requires the prop:
options: Array of SearchNavOption
This component has the optional props:
label: Stringplaceholder: String
Page Components
Page Component
This component has the required prop:
title: String
This component has the optional props:
is-child: Booleanis-empty: Booleantoolbar-actions: Arrayaction-img-url: String
title will be displayed in the mobile navbar.
Set is-child to true if the parent component has a SubNavigation Component. If is-child is set to true, the title must also be set as it is shown in the mobile navbar.
Set is-empty to true if it is a custom variation of the EmptyState Component.
The toolbar-actions will be displayed as list items within a dropdown located in the right of the mobile navbar.
If action-img-url is set the image will be placed in the right of the mobile navbar instead of a dropdown containing toolbar-actions. When clicked the component will look at the toolbar-actions array's first item and invoke its callback. The action image is designed for a 24px by 24px image. If toolbar-actions is undefined the action image will not be displayed.
This component has a slot that will insert the content in between the opening and closing tags.
<page title="Example Page" :action-img-url="actionImgUrl" :toolbar-actions="toolbarActions">
<p>Content goes here</p>
</page>export default {
data() {
return {
actionImgUrl: "/img/btn-add-white.svg",
toolbarActions: [
new RowAction({
label: "Add",
callback: () => {
alert("Add Clicked");
}
})
]
}
}
};SubNavigationPage Component
This component requires the prop:
title: Stringnav-items: Array of NavItem
On larger screens, this component displays a sub-navigation on the left-hand side of the page. On mobile, it appears as its own page.
EmptyState Component
This component has the optional prop:
message: String
The message displayed is in the center of the page. If message is not passed to the component the default text will be displayed.
FourZeroFour Component
Use this component to catch urls that do not match any path in the router.
Loader Component
This component is positioned absolute with a height and width of 100%, which will fills the parent constraints.
This component requires the prop:
show-loader: Boolean
This component has the optional prop:
loader-label: String
show-loader shows a spinner and a loader-label overlaying the modal's content. This prevents any user interaction while saving or loading.
loader-label displays the text under the spinner and defaults to "Loading".
Modal Component
This component requires the prop:
title: String
This component has the optional props:
primary-btn: Stringsecondary-btn: Stringvalidation-options: Booleanerrors: Booleanprimary-btn-disabled: Booleanshow-loader: Booleanloader-label: String
This component has the optional actions:
primary-action: Functionsecondary-action: Functionclose-modal: Function
The title will be displayed at the top of the Modal.
A close "x" button will always be at the top right corner but there is the option to add two buttons in the modal's footer. primary-btn prop will be the text of the button. primary-action will be invoked when this button is clicked. This button will not be visible unless both primary-btn and primary-action have values.
The secondary-btn will be displayed to the left of the primary-btn. Again, this button will not be visible unless secondary-btn and secondary-action have values.
When close-modal is assigned a Function, it will override the current functionality. This is useful if you need to show a prompt before a user closes the modal.
The props validation-options and errors are properties of Validate.ts and can be used if Validate is declared as a mixin. See Form Validation for more information.
primary-btn-disabled overrides validation-options and errors or disables the primary button if they are not passed in as props.
show-loader shows a spinner and a loader-label overlaying the modal's content. This prevents any user interaction while saving or loading.
loader-label displays the text under the spinner and defaults to "Saving".
This component has a slot that will insert the content in between the opening and closing tags.
<modal title="Example Modal">
<p>Content goes here</p>
</modal>Showing and Closing Modals
ModalService provides three methods to show and close modals.
show(component, data = {})
componentis the component containing a Modal Component to be shown.datais an object that is passed to the component.
Example:
import { Component, Vue } from "vue-property-decorator";
import { ModalService } from "@explorerpipeline/vue-toolkit/Toolkit"
import ManageProfile from "./ManageProfile"
@Component
export default class Example extends Vue {
private openModal() {
ModalService.show(ManageProfile, {
name: "John Smith"
});
}
}showAnother(component, data = {})
This method closes the current modal and displays another modal.
componentis the component containing a Modal Component to be shown.datais an object that is passed to the component.
Example:
import { Component, Vue } from "vue-property-decorator";
import { ModalService } from "@explorerpipeline/vue-toolkit/Toolkit"
import SuccessModal from "./SuccessModal"
@Component
export default class Example extends Vue {
private openNextModal() {
ModalService.showAnother(SuccessModal);
}
}closeModal()
This method closes the current modal.
import { Component, Vue } from "vue-property-decorator";
import { ModalService } from "@explorerpipeline/vue-toolkit/Toolkit"
@Component
export default class Example extends Vue {
private close() {
ModalService.closeModal();
}
}Using the Modal Component
Every time a Modal Component is used, ensure that a prop called data is declared on the component containing the Modal Component.
This component has a Vue.js slot allowing content in between the opening and closing tag. The modal's slot is wrapped in a form. The form's submit action will be primary-action if it has a value.
The props validation-options and errors are derived from the mixin Validate. See Form Validation for more information.
Example:
<template>
<modal
:title="title"
primary-btn="Save"
secondary-btn="Next"
@primary-action="primaryAction"
@secondary-action="secondaryAction"
@close-modal="closeModal"
:errors="errors"
:validation-options="validationOptions">
<p>Modal content goes here</p>
<text-field
v-model="data.name"
label="Name"
validation="required" />
</modal>
</template>
<script lang="ts">
import { Component } from "vue-property-decorator";
import { ValidateMixin, ModalService } from "@explorerpipeline/vue-toolkit/Toolkit"
import SuccessModal from "./SuccessModal"
@Component({
props: ["data"]
})
export default class Example extends ValidateMixin {
private primaryAction() {
this.validate(ModalService.closeModal);
}
private secondaryAction() {
ModalService.showAnother(SuccessModal);
}
private closeModal() {
let result = confirm("Are you sure you want to close?");
if (result) {
ModalService.closeModal();
}
}
}
</script>Form Field Components
TextField Component
For two way binding, v-model accepts a String.
This component requires the prop:
label: String
This component has the optional prop:
type: Stringvalidation: String or Object
type is the input type to be used (text, email, etc).
Validation accepts the String "required" or an Object of validations. See the VeeValidate Documentation for more information.
<text-field
v-model="password"
label="Password"
type="password"
:validation="passwordValidation" />private password: string = "";
private passwordValidation {
required: true,
min: "6",
max: "8"
};TextArea Component
For two way binding, v-model accepts a String.
This component requires the prop:
label: String
This component has the optional prop:
required: Boolean
Required, if true, ensures that the textarea's value is not empty when the form is validated.
<text-area
v-model="bio"
label="Bio"
:required="true" />SelectField Component
For two way binding v-model accepts a String.
This component requires the props:
label: Stringoptions: Array of KeyDisplayPair
This component has the optional props:
placeholder: Stringmultiple: Booleanrequired: Booleandisabled: Booleandisable-search: Booleanadditional-class: Booleanshow-select-all: Boolean
required, if true, ensures that the select's value is not empty when the form is validated.
<select-field
v-model="role"
label="Role"
:options="roleOptions"
:multiple="true"
:required="true"
additional-class="drop-up users"/>private role: string = new KeyDisplayPair();
private roleOptions = [
new KeyDisplayPair({
key: 1,
display: "User"
}),
new KeyDisplayPair({
key: 2,
display: "Admin"
}),
new KeyDisplayPair({
key: 3,
display: "Super Admin"
})
];DatePicker Component
For two way binding, v-model accepts a String.
This component requires the prop:
label: String
This component has the optional prop:
required: Boolean
required, if true, ensures that the inputs's value is not empty when the form is validated.
It is required that v-once is added to ensure jQuery DatePicker isn't instantiated multiple times for one component.
<date-picker
v-model="birthDate"
label="Birth Date"
:required="true"
v-once />DateTimePicker Component
For two way binding, v-model accepts a String.
This component requires the prop:
label: String
This component has the optional props:
required: Booleandate-format: Stringtime-format: Stringtime-step: Numberdate-time-obj: Date
required, if true, ensures that the inputs's value is not empty when the form is validated.
date-format allows you to enter you to enter a moment format for date. By default this is set to M/D/YY.
time-format allows you to enter you to enter a moment format for time. By default this is set to h:mm a. For 24hr format set this to "H:mm"
time-step allows you to set how many minutes between each time selection. By default this is set to 5.
date-time-obj allows you to sync a JavaScript Date object of the DateTimePicker's value. To do this add .sync after the prop's name.
It is required that v-once is added to ensure DateTimePicker isn't instantiated multiple times for one component.
<date-time-picker
v-model="data.user.exactBirthDate"
label="Exact Birth Date"
:required="true"
date-format="M/D/YYYY"
time-format="H:mm"
:time-step="1"
:date-time-obj.sync="exactBdayDateObj"
v-once />Filter Components
When displaying a set of filters, wrap the filters in a <div> with the class="filters-wrapper".
Example:
<div class="filters-wrapper">
<dropdown-filter
label="Show"
:options="locationOptions"
@value-selected="filterByLocation" />
<date-range-filter
label="Date Range"
:values="[1,3,7]"
@range-selected="filterByDateRange" />
</div>DateRange Component
This component requires the props:
label: Stringvalues: Array of Number
This component has the optional props:
interval: Stringallow-custom-range: Boolean
This component requires the action:
range-selected: Function
values will display a list with each value followed by the interval.
interval defaults to "Day" and changes the time type. Interval examples are "Hour", "Day", "Week", etc...
allow-custom-range defaults to true. When "Custom Range" is selected, the dropdown displays Start Date and End Date pickers. Set to false if an interval smaller than a day is used.
range-selected returns a moment Object of the range's start and a moment Object of the range's end.
<date-range-filter
label="Date Range"
:values="[1,3,7]"
@range-selected="filterByDateRange" />private filterByDateRange(startRange, endRange) {
console.log(`startRange == ${startRange.format("l")} && endRange == ${endRange.format("l")}`);
}Dropdown Component
This component requires the props:
label: Stringoptions: Array of KeyDisplayPair
This component has the option prop:
selected-key: String
If selected-key is an empty string or it is not passed into the component, the first option will be selected from options. If selected-key has a value, the component will look through the options to see if one option has a key matching selected-key upon mounting. If selected-key is updated at a later time by the parent, the component will again look through the options to find an item with a matching key. If you want two way binding with selected-key in order to keep it in sync with the selected option's key, use the .sync modifier.
This component has the action:
value-selected: Function
value-selected returns the key of the selected option.
<dropdown-filter
label="Show"
:options="locationOptions"
:selected-key.sync="selectedLocationKey"
@value-selected="filterByLocation" />
<p>Selected Location Key: {{selectedLocationKey}}</p>import { Component, Vue } from "vue-property-decorator";
@Component
export default Example extends Vue {
// Data
private selectedLocationKey = "11";
private locationOptions: KeyDisplayPair[] = [
new KeyDisplayPair({
key: "10",
display: "Citywide"
}),
new KeyDisplayPair({
key: "11",
display: "Statewide (Preferred)",
selectedDisplay: "Statewide"
}),
new KeyDisplayPair({
key: "21",
display: "Nationwide"
})
];
// Lifecycle hooks
private mounted() {
this.selectedLocationKey = locationOptions[1].key;
// Update the selection possibly after an Ajax call.
setTimeout(() => {
this.selectedLocationKey = locationOptions[2].key
}, 1000);
}
// Methods
private filterByLocation(key: string) {
let option = this.locationOptions.find(o => o.key == key);
console.log(`Location changed to ${option.label}`);
}
}Text Component
This component requires the prop:
label: String
This component has the optional prop:
placeholder: String
This component requires the action:
text-changed: Function
text-changed returns the input's value as a String.
<text-filter @text-changed="filterText" />private filterText(value: string) {
console.log(value);
}TableList Component
This component requires the props:
headers: Array of TableHeaderrows: Array of Object
This component has the optional props:
row-action-label: Stringrow-actions: Array of RowAction
headers will be used to display the <th> of the table. These will not be visible on mobile. The TableHeader class not only contains a label but also a key. This key must be the same name as the object's property that will be displayed in the column.
rows will contain the information to be displayed. Typically, this is information returned from an API or other source.
row-action-label sets the label of a dropdown created as the last column of each row.
row-actions define the dropdown options shown when the row-action-label is clicked.
If row-actions only has one item in the array, the row-action-label will be ignored and the label from the row-actions item will be used. When row-actions has more that one item in the array, row-action-label is required to display the dropdown in the last column of the row.
<table-list
:headers="tableHeaders"
:rows="tableRows"
row-action-label="Actions"
:row-actions="tableRowActions"/>private tableHeaders = [
new TableHeader({
key: "location",
label: "Location"
}),
new TableHeader({
key: "name",
label: "Name"
}),
new TableHeader({
key: "forwardingNumber",
label: "Forwarding Number"
})
];
private tableRows = [
{
name: "Test 1",
location: "Location 1",
forwardingNumber: "123"
}, {
name: "Test 2",
location: "Location 2",
forwardingNumber: "456"
}, {
name: "Test 3",
location: "Location 3",
forwardingNumber: "789"
}
];
tableRowActions = [
new RowAction({
label: "Alert",
callback: () => {
alert("callback works");
}
}),
new RowAction({
label: "Console Log",
callback: () => {
console.log("callback works");
}
})
];Form Validation
Validate Mixin
If there are form fields in a modal or form that need to be validated, declaring Validate as a mixin is required. This will also provide the props validation-options and errors. errors contains the VeeValidate errors for all form fields. These values are passed into the Modal Component to update the UI if there are errors.
import { Component, Vue } from "vue-property-decorator";
import { ValidateMixin } from "@explorerpipeline/vue-toolkit/Toolkit"
@Component
export default Example extends ValidateMixin {
}Validation Options
There are two validation options that can be set with methods when Validate is declared as a mixin. These can be changed by calling the appropriate function within the mounted lifecycle hook.
The default validation configuration hides all errors until the form is submitted or the primary-action is invoked. If there are errors, they will show up inline with the form field label. Enabling instant validation will display errors even before the form has been submitted.
There is also a method to enable the form to be saved with errors.
import { Component, Vue } from "vue-property-decorator";
import { ValidateMixin } from "@explorerpipeline/vue-toolkit/Toolkit"
@Component
export default Example extends ValidateMixin {
private mounted() {
this.enableInstantValidation();
this.enableSaveBtnWithErrors();
}
}Setting Validation on TextField
Please refer to the VeeValidate Documentation for all validation options.
Validating a From
The Validate mixin provides the validate method.
validate(validAction: Function, errorAction: Function)
validActionis a Function fired on a successful validation.errorActionis a Function fired on an unsuccessful validation.
private submit() {
this.validate(() => {
alert("There are no errors");
}, () => {
alert("Validation failed");
});
}Classes
Branding Class
This class requires the properties:
iconWhite: StringiconGray: Stringlabel: String
icon and iconMobile should specify a file of type .svg. These icons will be displayed at a width and height of 22px. icon is used on a dark background and iconMobile is used on a light background.
Place the svg files in the public/img folder.
branding: new Branding({
iconWhite: "/img/brand-icon-light.svg",
iconGray: "/img/brand-icon-dark.svg",
label: "Expl App"
})DelayedLoader Class
This class requires the parameter:
delayDuration: Number of milliseconds
This class exposes the property:
isLoading: Boolean
This class has the methods:
show(): Sets the property isLoading to true after the delayDurationhide(): Sets the property isLoading to false
This is useful for using the Loader Component when loading ajax methods and prevents the Loader component from flickering if the method loads quickly.
<template>
<page title="DelayedLoader Example">
<loader :show-loader="loader.isLoading"/>
<table-list :headers="headers" :rows="rows"/>
</page>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { Page, TableList, Loader, TableHeader } from "@rzrdev/vue-toolkit/Toolkit";
@Component({
components: {
Page,
TableList,
Loader
}
})
export default class Example extends Vue {
// Data
private loader = new DelayedLoader(250);
private rows: object[] = [];
private headers = [
new TableHeader({
key: "fullName",
label: "Explorer Name"
}),
new TableHeader({
key: "description",
label: "Details"
})
];
// Lifecycle hooks
private mounted() {
this.loader.show();
this.rows = [];
getRowsPromise().then((rows: object) => {
this.rows = rows;
this.loader.hide();
});
}
}
</script>KeyDisplayPair Class
This class requires the properties:
key: Stringdisplay: String
This class has the optional property:
selectedDisplay: StringoriginalObj: Any
selectedDisplay is used in the Dropdown filter. display will be used in the list of dropdown items but selectedDisplay will be used for the selected text. If the selectedDisplay does not have a value then display will be used for the selected text.
NavItem Class
This class requires the property:
label: String
This class has the optional properties:
link: Stringicon: Stringcallback: Function
There is the ability to provide a link or a callback (useful for opening a modal). If a callback is provided, the link will be ignored.
Place the svg files in the public/img folder.
private navItems = [
new NavItem({
link: "/dashboard",
icon: "/img/nav-dashboard.svg",
label: "Dashboard"
}),
new NavItem({
icon: "/img/nav-click.svg",
label: "Click",
callback: () => {
alert("callback works");
}
})
];RowAction Class
This class is used in an Array when setting the row-actions prop in the TableList Component or when setting the toolbarActions prop in the Page Component. Each class will be converted into a dropdown option.
This class requires the properties:
label: Stringcallback: Function that returns the row Object
The function is invoked when the dropdown option is clicked.
SearchNavOption Class
This class requires the properties:
key: Stringdisplay: Stringlink: Stringtype: String
type is the category displayed to the right of the display text.
TableHeader Class
This class is used in an Array when setting the headers prop in the TableList Component. Each class will be converted into a <th> and the key will be used to order the row object's properties.
This class requires the properties:
key: Stringlabel: String
User Class
This class requires the properties:
firstName: StringlastName: String
This class provides the computed property:
fullName: String
7 years ago