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/cli
Then run:
vue ui
After 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 serve
This 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 login
Finally, install the Explorer Vue Toolkit by running:
npm install --save @explorerpipeline/vue-toolkit
Setup 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.scss
Imports
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 = {})
component
is the component containing a Modal Component to be shown.data
is 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.
component
is the component containing a Modal Component to be shown.data
is 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)
validAction
is a Function fired on a successful validation.errorAction
is 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
5 years ago