You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by sc...@apache.org on 2018/04/30 19:01:38 UTC

[13/19] nifi-fds git commit: [NIFIREG-168] include webapp in src to facilitate local build and review process

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/components/fluid-design-system/fds-demo.js
----------------------------------------------------------------------
diff --git a/webapp/components/fluid-design-system/fds-demo.js b/webapp/components/fluid-design-system/fds-demo.js
new file mode 100644
index 0000000..a21dd6b
--- /dev/null
+++ b/webapp/components/fluid-design-system/fds-demo.js
@@ -0,0 +1,1066 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngCore = require('@angular/core');
+var covalentCore = require('@covalent/core');
+var ngRouter = require('@angular/router');
+var ngMaterial = require('@angular/material');
+var fdsAnimations = require('webapp/fds.animations.js');
+var fdsDialogsModule = require('@fluid-design-system/dialogs');
+var fdsSnackBarsModule = require('@fluid-design-system/snackbars');
+var FdsService = require('webapp/services/fds.service.js');
+var FdsDemoDialog = require('webapp/components/fluid-design-system/dialogs/demo/fds-demo-dialog.js');
+
+var NUMBER_FORMAT = function (v) {
+    return v;
+};
+var DECIMAL_FORMAT = function (v) {
+    return v.toFixed(2);
+};
+var date = new Date();
+
+/**
+ * FdsDemo constructor.
+ *
+ * @param FdsSnackBarService    The FDS snack bar service module.
+ * @param FdsService            The FDS service module.
+ * @param dialog                The angular material dialog module.
+ * @param TdDialogService       The covalent dialog service module.
+ * @param TdDataTableService    The covalent data table service module.
+ * @constructor
+ */
+function FdsDemo(FdsSnackBarService, FdsService, dialog, TdDataTableService, FdsDialogService) {
+
+    this.fdsService = FdsService;
+
+    //<editor-fold desc="Snack Bars">
+
+    this.snackBarService = FdsSnackBarService;
+
+    //</editor-fold>
+
+    //<editor-fold desc="Dialog">
+
+    this.dialog = dialog;
+
+    //</editor-fold>
+
+    //<editor-fold desc="Simple Dialogs">
+
+    this.dialogService = FdsDialogService;
+
+    //</editor-fold>
+
+    //<editor-fold desc="Expansion Panel">
+
+    this.expandCollapseExpansion1Msg = 'No expanded/collapsed detected yet';
+    this.expansion1 = false;
+    this.disabled = false;
+
+    //</editor-fold>
+
+    //<editor-fold desc="Autocomplete">
+
+    this.currentState = '';
+    this.reactiveStates = '';
+    this.tdStates = [];
+    this.tdDisabled = false;
+    this.states = [
+        {code: 'AL', name: 'Alabama'},
+        {code: 'AK', name: 'Alaska'},
+        {code: 'AZ', name: 'Arizona'},
+        {code: 'AR', name: 'Arkansas'},
+        {code: 'CA', name: 'California'},
+        {code: 'CO', name: 'Colorado'},
+        {code: 'CT', name: 'Connecticut'},
+        {code: 'DE', name: 'Delaware'},
+        {code: 'FL', name: 'Florida'},
+        {code: 'GA', name: 'Georgia'},
+        {code: 'HI', name: 'Hawaii'},
+        {code: 'ID', name: 'Idaho'},
+        {code: 'IL', name: 'Illinois'},
+        {code: 'IN', name: 'Indiana'},
+        {code: 'IA', name: 'Iowa'},
+        {code: 'KS', name: 'Kansas'},
+        {code: 'KY', name: 'Kentucky'},
+        {code: 'LA', name: 'Louisiana'},
+        {code: 'ME', name: 'Maine'},
+        {code: 'MD', name: 'Maryland'},
+        {code: 'MA', name: 'Massachusetts'},
+        {code: 'MI', name: 'Michigan'},
+        {code: 'MN', name: 'Minnesota'},
+        {code: 'MS', name: 'Mississippi'},
+        {code: 'MO', name: 'Missouri'},
+        {code: 'MT', name: 'Montana'},
+        {code: 'NE', name: 'Nebraska'},
+        {code: 'NV', name: 'Nevada'},
+        {code: 'NH', name: 'New Hampshire'},
+        {code: 'NJ', name: 'New Jersey'},
+        {code: 'NM', name: 'New Mexico'},
+        {code: 'NY', name: 'New York'},
+        {code: 'NC', name: 'North Carolina'},
+        {code: 'ND', name: 'North Dakota'},
+        {code: 'OH', name: 'Ohio'},
+        {code: 'OK', name: 'Oklahoma'},
+        {code: 'OR', name: 'Oregon'},
+        {code: 'PA', name: 'Pennsylvania'},
+        {code: 'RI', name: 'Rhode Island'},
+        {code: 'SC', name: 'South Carolina'},
+        {code: 'SD', name: 'South Dakota'},
+        {code: 'TN', name: 'Tennessee'},
+        {code: 'TX', name: 'Texas'},
+        {code: 'UT', name: 'Utah'},
+        {code: 'VT', name: 'Vermont'},
+        {code: 'VA', name: 'Virginia'},
+        {code: 'WA', name: 'Washington'},
+        {code: 'WV', name: 'West Virginia'},
+        {code: 'WI', name: 'Wisconsin'},
+        {code: 'WY', name: 'Wyoming'},
+    ];
+
+    //</editor-fold>
+
+    //<editor-fold desc="Searchable Expansion Panels">
+
+    this.dataTableService = TdDataTableService;
+
+    this.droplets = [{
+        id: '23f6cc59-0156-1000-09b4-2b0610089090',
+        name: "Decompression_Circular_Flow",
+        displayName: 'Decompressed Circular flow',
+        type: 'flow',
+        sublabel: 'A sublabel',
+        compliant: {
+            id: '25fd6vv87-3549-0001-05g6-4d4567890765',
+            label: 'Compliant',
+            type: 'certification'
+        },
+        fleet: {
+            id: '23f6cc59-3549-0001-05g6-4d4567890765',
+            label: 'Fleet',
+            type: 'certification'
+        },
+        prod: {
+            id: '52fd6vv87-3549-0001-05g6-4d4567890765',
+            label: 'Production Ready',
+            type: 'certification'
+        },
+        secure: {
+            id: '32f6cc59-3549-0001-05g6-4d4567890765',
+            label: 'Secure',
+            type: 'certification'
+        },
+        versions: [{
+            id: '23f6cc59-0156-1000-06b4-2b0810089090',
+            revision: '1',
+            dependentFlows: [{
+                id: '25fd6vv87-3549-0001-05g6-4d4567890765'
+            }],
+            created: date.setDate(date.getDate() - 1),
+            updated: new Date()
+        }, {
+            id: '25fd6vv87-3549-0001-05g6-4d4567890765',
+            revision: '2',
+            dependentFlows: [{
+                id: '23f6cc59-0156-1000-06b4-2b0810089090'
+            }],
+            created: new Date(),
+            updated: new Date()
+        }],
+        flows: [],
+        extensions: [],
+        assets: [],
+        actions: [{
+            'name': 'Delete',
+            'icon': 'fa fa-close',
+            'tooltip': 'Delete User'
+        }, {
+            'name': 'Manage',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage User'
+        }, {
+            'name': 'Action 3',
+            'icon': 'fa fa-question',
+            'tooltip': 'Whatever else we want to do...'
+        }]
+    }, {
+        id: '25fd6vv87-3249-0001-05g6-4d4767890765',
+        name: "DateConversion",
+        displayName: 'Date conversion',
+        type: 'asset',
+        sublabel: 'A sublabel',
+        compliant: {
+            id: '25fd6vv34-3549-0001-05g6-4d4567890765',
+            label: 'Compliant',
+            type: 'certification'
+        },
+        prod: {
+            id: '52vn6vv87-3549-0001-05g6-4d4567890765',
+            label: 'Production Ready',
+            type: 'certification'
+        },
+        versions: [{
+            id: '23f6ic59-0156-1000-06b4-2b0810089090',
+            revision: '1',
+            dependentFlows: [{
+                id: '23f6cc19-0156-1000-06b4-2b0810089090'
+            }],
+            created: new Date(),
+            updated: new Date()
+        }],
+        flows: [],
+        extensions: [],
+        assets: [],
+        actions: [{
+            'name': 'Delete',
+            'icon': 'fa fa-close',
+            'tooltip': 'Delete User'
+        }]
+    }, {
+        id: '52fd6vv87-3294-0001-05g6-4d4767890765',
+        name: "nifi-email-bundle",
+        displayName: 'nifi-email-bundle',
+        type: 'extension',
+        sublabel: 'A sublabel',
+        compliant: {
+            id: '33fd6vv87-3549-0001-05g6-4d4567890765',
+            label: 'Compliant',
+            test: {
+                label: 'test'
+            },
+            type: 'certification'
+        },
+        versions: [{
+            id: '23d3cc59-0156-1000-06b4-2b0810089090',
+            revision: '1',
+            dependentFlows: [{
+                id: '23f6cc89-0156-1000-06b4-2b0810089090'
+            }],
+            created: new Date(),
+            updated: new Date()
+        }],
+        flows: [],
+        extensions: [],
+        assets: [],
+        actions: [{
+            'name': 'Delete',
+            'icon': 'fa fa-close',
+            'tooltip': 'Delete User'
+        }, {
+            'name': 'Manage',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage User'
+        },]
+    }];
+
+    this.filteredDroplets = [];
+
+    this.dropletColumns = [
+        {name: 'id', label: 'ID', sortable: true},
+        {name: 'name', label: 'Name', sortable: true,},
+        {name: 'displayName', label: 'Display Name', sortable: true},
+        {name: 'sublabel', label: 'Label', sortable: true},
+        {name: 'type', label: 'Type', sortable: true}
+    ];
+    this.activeDropletColumn = this.dropletColumns[0];
+
+    this.autoCompleteDroplets = [];
+    this.dropletsSearchTerms = [];
+
+    //</editor-fold>
+
+    //<editor-fold desc="Data Tables">
+
+    this.data = [{
+        'id': 1,
+        'name': 'Frozen yogurt',
+        'type': 'Ice cream',
+        'calories': 159.0,
+        'fat': 6.0,
+        'carbs': 24.0,
+        'protein': 4.0,
+        'sodium': 87.0,
+        'calcium': 14.0,
+        'iron': 1.0,
+        'comments': 'I love froyo!',
+        'actions': [{
+            'name': 'Action 1',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage Users'
+        }, {
+            'name': 'Action 2',
+            'icon': 'fa fa-key',
+            'tooltip': 'Manage Permissions'
+        }]
+    }, {
+        'id': 2,
+        'name': 'Ice cream sandwich',
+        'type': 'Ice cream',
+        'calories': 237.0,
+        'fat': 9.0,
+        'carbs': 37.0,
+        'protein': 4.3,
+        'sodium': 129.0,
+        'calcium': 8.0,
+        'iron': 1.0,
+        'actions': [{
+            'name': 'Action 1',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage Users'
+        }, {
+            'name': 'Action 2',
+            'icon': 'fa fa-key',
+            'tooltip': 'Manage Permissions'
+        }, {
+            'name': 'Action 3',
+            'tooltip': 'Action 3'
+        }, {
+            'name': 'Action 4',
+            'disabled': true,
+            'tooltip': 'Action 4'
+        }, {
+            'name': 'Action 5',
+            'tooltip': 'Action 5'
+        }]
+    }, {
+        'id': 3,
+        'name': 'Eclair',
+        'type': 'Pastry',
+        'calories': 262.0,
+        'fat': 16.0,
+        'carbs': 24.0,
+        'protein': 6.0,
+        'sodium': 337.0,
+        'calcium': 6.0,
+        'iron': 7.0,
+        'actions': [{
+            'name': 'Action 1',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage Users'
+        }, {
+            'name': 'Action 2',
+            'icon': 'fa fa-key',
+            'tooltip': 'Manage Permissions'
+        }, {
+            'name': 'Action 3',
+            'tooltip': 'Action 3'
+        }, {
+            'name': 'Action 4',
+            'disabled': true,
+            'tooltip': 'Action 4'
+        }, {
+            'name': 'Action 5',
+            'tooltip': 'Action 5'
+        }],
+    }, {
+        'id': 4,
+        'name': 'Cupcake',
+        'type': 'Pastry',
+        'calories': 305.0,
+        'fat': 3.7,
+        'carbs': 67.0,
+        'protein': 4.3,
+        'sodium': 413.0,
+        'calcium': 3.0,
+        'iron': 8.0,
+        'actions': [{
+            'name': 'Action 1',
+            'icon': 'fa fa-user',
+            'tooltip': 'Manage Users'
+        }, {
+            'name': 'Action 2',
+            'icon': 'fa fa-key',
+            'tooltip': 'Manage Permissions'
+        }, {
+            'name': 'Action 3',
+            'tooltip': 'Action 3'
+        }, {
+            'name': 'Action 4',
+            'disabled': true,
+            'tooltip': 'Action 4'
+        }, {
+            'name': 'Action 5',
+            'tooltip': 'Action 5'
+        }],
+    }, {
+        'id': 5,
+        'name': 'Jelly bean',
+        'type': 'Candy',
+        'calories': 375.0,
+        'fat': 0.0,
+        'carbs': 94.0,
+        'protein': 0.0,
+        'sodium': 50.0,
+        'calcium': 0.0,
+        'iron': 0.0,
+    }, {
+        'id': 6,
+        'name': 'Lollipop',
+        'type': 'Candy',
+        'calories': 392.0,
+        'fat': 0.2,
+        'carbs': 98.0,
+        'protein': 0.0,
+        'sodium': 38.0,
+        'calcium': 0.0,
+        'iron': 2.0,
+    }, {
+        'id': 7,
+        'name': 'Honeycomb',
+        'type': 'Other',
+        'calories': 408.0,
+        'fat': 3.2,
+        'carbs': 87.0,
+        'protein': 6.5,
+        'sodium': 562.0,
+        'calcium': 0.0,
+        'iron': 45.0,
+    }, {
+        'id': 8,
+        'name': 'Donut',
+        'type': 'Pastry',
+        'calories': 452.0,
+        'fat': 25.0,
+        'carbs': 51.0,
+        'protein': 4.9,
+        'sodium': 326.0,
+        'calcium': 2.0,
+        'iron': 22.0,
+    }, {
+        'id': 9,
+        'name': 'KitKat',
+        'type': 'Candy',
+        'calories': 518.0,
+        'fat': 26.0,
+        'carbs': 65.0,
+        'protein': 7.0,
+        'sodium': 54.0,
+        'calcium': 12.0,
+        'iron': 6.0,
+    }, {
+        'id': 10,
+        'name': 'Chocolate',
+        'type': 'Candy',
+        'calories': 518.0,
+        'fat': 26.0,
+        'carbs': 65.0,
+        'protein': 7.0,
+        'sodium': 54.0,
+        'calcium': 12.0,
+        'iron': 6.0,
+    }, {
+        'id': 11,
+        'name': 'Chamoy',
+        'type': 'Candy',
+        'calories': 518.0,
+        'fat': 26.0,
+        'carbs': 65.0,
+        'protein': 7.0,
+        'sodium': 54.0,
+        'calcium': 12.0,
+        'iron': 6.0,
+    },];
+
+    this.filteredData = this.data;
+    this.filteredTotal = this.data.length;
+
+    this.columns = [
+        {name: 'comments', label: 'Comments', width: 10},
+        {name: 'name', label: 'Dessert (100g serving)', sortable: true, width: 10},
+        {name: 'type', label: 'Type', sortable: true, width: 10},
+        {name: 'calories', label: 'Calories', numeric: true, format: NUMBER_FORMAT, sortable: true, width: 10},
+        {name: 'fat', label: 'Fat (g)', numeric: true, format: DECIMAL_FORMAT, sortable: true, width: 10},
+        {name: 'carbs', label: 'Carbs (g)', numeric: true, format: NUMBER_FORMAT, sortable: true, width: 10},
+        {name: 'protein', label: 'Protein (g)', numeric: true, format: DECIMAL_FORMAT, sortable: true, width: 10},
+        {name: 'sodium', label: 'Sodium (mg)', numeric: true, format: NUMBER_FORMAT, sortable: true, width: 10},
+        {name: 'calcium', label: 'Calcium (%)', numeric: true, format: NUMBER_FORMAT, sortable: true, width: 10},
+        {name: 'iron', label: 'Iron (%)', numeric: true, format: NUMBER_FORMAT, width: 10},
+    ];
+
+    this.allRowsSelected = false;
+    this.autoCompleteData = [];
+    this.selectedRows = [];
+
+    this.searchTerm = [];
+    this.fromRow = 1;
+    this.currentPage = 1;
+    this.pageSize = 5;
+    this.pageCount = 0;
+
+    //</editor-fold>
+
+    //<editor-fold desc="Chips $ Autocomplete">
+
+    this.readOnly = false;
+
+    this.items = [
+        'stepper',
+        'expansion-panel',
+        'markdown',
+        'highlight',
+        'loading',
+        'media',
+        'chips',
+        'http',
+        'json-formatter',
+        'pipes',
+        'need more?',
+    ];
+
+    this.itemsRequireMatch = this.items.slice(0, 6);
+
+    //</editor-fold>
+
+    //<editor-fold desc="Radios">
+
+    this.favoriteSeason = 'Autumn';
+
+    this.seasonOptions = [
+        'Winter',
+        'Spring',
+        'Summer',
+        'Autumn',
+    ];
+
+    //</editor-fold>
+
+    //<editor-fold desc="Select">
+
+    this.selectedValue = '';
+
+    this.foods = [
+        {value: 'steak-0', viewValue: 'Steak'},
+        {value: 'pizza-1', viewValue: 'Pizza'},
+        {value: 'tacos-2', viewValue: 'Tacos'},
+    ];
+
+    //</editor-fold>
+
+    //<editor-fold desc="Checkbox">
+
+    this.user = {
+        agreesToTOS: false
+    };
+
+    this.groceries = [{
+        bought: true,
+        name: 'Seitan',
+    }, {
+        bought: false,
+        name: 'Almond Meal Flour',
+    }, {
+        bought: false,
+        name: 'Organic Eggs',
+    },];
+
+    //</editor-fold>
+
+    //<editor-fold desc="Slide Toggle">
+
+    this.systems = [{
+        name: 'Lights',
+        on: false,
+        color: 'primary',
+    }, {
+        name: 'Surround Sound',
+        on: true,
+        color: 'accent',
+    }, {
+        name: 'T.V.',
+        on: true,
+        color: 'warn',
+    },];
+
+    this.house = {
+        lockHouse: false,
+    };
+
+    //</editor-fold>
+};
+
+FdsDemo.prototype = {
+    constructor: FdsDemo,
+
+    //<editor-fold desc="Autocomplete">
+
+    displayFn: function (value) {
+        return value && typeof value === 'object' ? value.name : value;
+    },
+
+    filterStates: function (val) {
+        return val ? this.states.filter(function (s) {
+            return s.name.match(new RegExp(val, 'gi'));
+        }) : this.states;
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Snack Bars">
+
+    showSuccessSnackBar: function () {
+        var snackBarRef = this.snackBarService.openCoaster({
+            title: 'Success',
+            message: 'Some help text regarding the successful event.',
+            verticalPosition: 'top',
+            horizontalPosition: 'right',
+            icon: 'fa fa-check-circle-o',
+            color: '#1EB475',
+            duration: 3000
+        });
+    },
+
+    showWarnSnackBar: function () {
+        var snackBarRef = this.snackBarService.openCoaster({
+            title: 'Warning',
+            message: 'Some help text regarding the warning.',
+            verticalPosition: 'top',
+            horizontalPosition: 'left',
+            icon: 'fa fa-exclamation-triangle',
+            color: '#E98A40',
+            duration: 3000
+        });
+    },
+
+    showErrorSnackBar: function () {
+        var snackBarRef = this.snackBarService.openCoaster({
+            title: 'Error',
+            message: 'Some help text regarding the critical error. This coaster will stay open until closed with the `x` or if another coaster is created.',
+            verticalPosition: 'bottom',
+            horizontalPosition: 'right',
+            icon: 'fa fa-times-circle-o',
+            color: '#EF6162'
+        });
+    },
+
+    showRegularSnackBar: function () {
+        var snackBarRef = this.snackBarService.openCoaster({
+            title: 'Regular',
+            message: 'Something interesting.',
+            verticalPosition: 'bottom',
+            horizontalPosition: 'left',
+            color: '#808793',
+            duration: 3000
+        });
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Dialog">
+
+    openDialog: function () {
+        this.dialog.open(FdsDemoDialog);
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Expansion Panel">
+
+    toggleExpansion1: function () {
+        if (!this.disabled) {
+            this.expansion1 = !this.expansion1;
+        }
+    },
+
+    toggleDisabled: function () {
+        this.disabled = !this.disabled;
+    },
+
+    expandExpansion1Event: function () {
+        this.expandCollapseExpansion1Msg = 'Expand event emitted.';
+    },
+
+    collapseExpansion1Event: function () {
+        this.expandCollapseExpansion1Msg = 'Collapse event emitted.';
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Simple Dialogs">
+
+    openAlert: function () {
+        this.dialogService.openAlert({
+            title: 'Alert',
+            disableClose: true,
+            message: 'This is how simple it is to create an alert with this wrapper service.',
+        });
+    },
+
+    openConfirm: function () {
+        this.dialogService.openConfirm({
+            title: 'Confirm',
+            message: 'This is how simple it is to create a confirm with this wrapper service. Do you agree?',
+            cancelButton: 'Disagree',
+            acceptButton: 'Agree',
+        });
+    },
+
+    openPrompt: function () {
+        this.dialogService.openPrompt({
+            title: 'Prompt',
+            message: 'This is how simple it is to create a prompt with this wrapper service. Prompt something.',
+            value: 'Populated value',
+            cancelButton: 'Cancel',
+            acceptButton: 'Ok',
+        });
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Searchable Expansion Panels">
+
+    isDropletFilterChecked: function (term) {
+        return (this.dropletsSearchTerms.indexOf(term) > -1);
+    },
+
+    getDropletTypeCount: function (type) {
+        return this.filteredDroplets.filter(function (droplet) {
+            return droplet.type === type;
+        }).length;
+    },
+
+    getDropletCertificationCount: function (certification) {
+        return this.filteredDroplets.filter(function (droplet) {
+            return Object.keys(droplet).find(function (key) {
+                if (key === certification && droplet[certification].type === 'certification') {
+                    return droplet;
+                }
+            });
+        }).length;
+    },
+
+    getSortBy: function () {
+        var sortByColumnLabel;
+        var arrayLength = this.dropletColumns.length;
+        for (var i = 0; i < arrayLength; i++) {
+            if (this.dropletColumns[i].active === true) {
+                sortByColumnLabel = this.dropletColumns[i].label;
+                break;
+            }
+        }
+        return sortByColumnLabel;
+    },
+
+    sortDroplets: function (column) {
+        if (column.sortable === true) {
+            // toggle column sort order
+            var sortOrder = column.sortOrder = (column.sortOrder === 'ASC') ? 'DESC' : 'ASC';
+            this.filterDroplets(column.name, sortOrder);
+            //only one column can be actively sorted so we reset all to inactive
+            this.dropletColumns.forEach(function (c) {
+                c.active = false;
+            });
+            //and set this column as the actively sorted column
+            column.active = true;
+            this.activeDropletColumn = column;
+        }
+    },
+
+    toggleDropletsFilter: function (searchTerm) {
+        var applySearchTerm = true;
+        // check if the search term is already applied and remove it if true
+        if (this.dropletsSearchTerms.length > 0) {
+            var arrayLength = this.dropletsSearchTerms.length;
+            for (var i = 0; i < arrayLength; i++) {
+                var index = this.dropletsSearchTerms.indexOf(searchTerm);
+                if (index > -1) {
+                    this.dropletsSearchTerms.splice(index, 1);
+                    applySearchTerm = false;
+                }
+            }
+        }
+
+        // if we just removed the search term do NOT apply it again
+        if (applySearchTerm) {
+            this.dropletsSearchTerms.push(searchTerm);
+        }
+
+        this.filterDroplets(this.activeDropletColumn.name, this.activeDropletColumn.sortOrder);
+    },
+
+    filterDroplets: function (sortBy, sortOrder) {
+        // if `sortOrder` is `undefined` then use 'ASC'
+        if (sortOrder === undefined) {
+            sortOrder = 'ASC'
+        }
+        // if `sortBy` is `undefined` then find the first sortable column in this.dropletColumns
+        if (sortBy === undefined) {
+            var arrayLength = this.dropletColumns.length;
+            for (var i = 0; i < arrayLength; i++) {
+                if (this.dropletColumns[i].sortable === true) {
+                    sortBy = this.dropletColumns[i].name;
+                    this.activeDropletColumn = this.dropletColumns[i];
+                    //only one column can be actively sorted so we reset all to inactive
+                    this.dropletColumns.forEach(function (c) {
+                        c.active = false;
+                    });
+                    //and set this column as the actively sorted column
+                    this.dropletColumns[i].active = true;
+                    this.dropletColumns[i].sortOrder = sortOrder;
+                    break;
+                }
+            }
+        }
+
+        var newData = this.droplets;
+
+        for (var i = 0; i < this.dropletsSearchTerms.length; i++) {
+            newData = this.filterData(newData, this.dropletsSearchTerms[i], true, this.activeDropletColumn.name);
+        }
+
+        newData = this.dataTableService.sortData(newData, sortBy, sortOrder);
+        this.filteredDroplets = newData;
+        this.getAutoCompleteDroplets();
+    },
+
+    getAutoCompleteDroplets: function () {
+        var self = this;
+        this.autoCompleteDroplets = [];
+        this.dropletColumns.forEach(function (c) {
+            self.filteredDroplets.forEach(function (r) {
+                (r[c.name.toLowerCase()]) ? self.autoCompleteDroplets.push(r[c.name.toLowerCase()].toString()) : '';
+            });
+        });
+    },
+
+    //</editor-fold>
+
+    filterData: function (data, searchTerm, ignoreCase) {
+        var field = '';
+        if (searchTerm.indexOf(":") > -1) {
+            field = searchTerm.split(':')[0].trim();
+            searchTerm = searchTerm.split(':')[1].trim();
+        }
+        var filter = searchTerm ? (ignoreCase ? searchTerm.toLowerCase() : searchTerm) : '';
+
+        if (filter) {
+            data = data.filter(function (item) {
+                var res = Object.keys(item).find(function (key) {
+                    if (field.indexOf(".") > -1) {
+                        var objArray = field.split(".");
+                        var obj = item;
+                        var arrayLength = objArray.length;
+                        for (var i = 0; i < arrayLength; i++) {
+                            try {
+                                obj = obj[objArray[i]];
+                            } catch (e) {
+                                return false;
+                            }
+                        }
+                        var preItemValue = ('' + obj);
+                        var itemValue = ignoreCase ? preItemValue.toLowerCase() : preItemValue;
+                        return itemValue.indexOf(filter) > -1;
+                    } else {
+                        if (key !== field && field !== '') {
+                            return false;
+                        }
+                        var preItemValue = ('' + item[key]);
+                        var itemValue = ignoreCase ? preItemValue.toLowerCase() : preItemValue;
+                        return itemValue.indexOf(filter) > -1;
+                    }
+                });
+                return !(typeof res === 'undefined');
+            });
+        }
+        return data;
+    },
+
+    //<editor-fold desc="Data Tables">
+
+    sort: function (sortEvent, column) {
+        if (column.sortable) {
+            var sortBy = column.name;
+            var sortOrder = column.sortOrder = (column.sortOrder === 'ASC') ? 'DESC' : 'ASC';
+            this.filter(sortBy, sortOrder);
+
+            //only one column can be actively sorted so we reset all to inactive
+            this.columns.forEach(function (c) {
+                c.active = false;
+            });
+            //and set this column as the actively sorted column
+            column.active = true;
+        }
+    },
+
+    searchRemove: function (searchTerm) {
+        //only remove the first occurrence of the search term
+        var index = this.searchTerm.indexOf(searchTerm);
+        if (index !== -1) {
+            this.searchTerm.splice(index, 1);
+        }
+        this.fromRow = 1;
+        this.currentPage = 1;
+        this.filter();
+    },
+
+    searchAdd: function (searchTerm) {
+        this.searchTerm.push(searchTerm);
+        this.fromRow = 1;
+        this.currentPage = 1;
+        this.filter();
+    },
+
+    page: function (pagingEvent) {
+        this.fromRow = pagingEvent.fromRow;
+        this.currentPage = pagingEvent.page;
+        this.pageSize = pagingEvent.pageSize;
+        this.allRowsSelected = false;
+        this.filter();
+    },
+
+    filter: function (sortBy, sortOrder) {
+        if (this.allRowsSelected) {
+            this.toggleSelectAll();
+        }
+        this.deselectAll();
+        var newData = this.data;
+
+        for (var i = 0; i < this.searchTerm.length; i++) {
+            newData = this.filterData(newData, this.searchTerm[i], true);
+        }
+        this.filteredTotal = newData.length;
+        newData = this.dataTableService.sortData(newData, sortBy, sortOrder);
+        this.pageCount = newData.length;
+        newData = this.dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize);
+        this.filteredData = newData;
+        this.getAutoCompleteData();
+    },
+
+    toggleSelect: function (row) {
+        if (this.allFilteredRowsSelected()) {
+            this.allRowsSelected = true;
+        } else {
+            this.allRowsSelected = false;
+        }
+    },
+
+    toggleSelectAll: function () {
+        if (this.allRowsSelected) {
+            this.selectAll();
+        } else {
+            this.deselectAll();
+        }
+    },
+
+    selectAll: function () {
+        this.filteredData.forEach(function (c) {
+            c.checked = true;
+        });
+    },
+
+    deselectAll: function () {
+        this.filteredData.forEach(function (c) {
+            c.checked = false;
+        });
+    },
+
+    allFilteredRowsSelected: function () {
+        var allFilteredRowsSelected = true;
+        this.filteredData.forEach(function (c) {
+            if (c.checked === undefined || c.checked === false) {
+                allFilteredRowsSelected = false;
+            }
+        });
+
+        return allFilteredRowsSelected;
+    },
+
+    areTooltipsOn: function () {
+        return this.columns[0].hasOwnProperty('tooltip');
+    },
+
+    toggleTooltips: function () {
+        if (this.columns[0].tooltip) {
+            this.columns.forEach(function (c) {
+                delete c.tooltip;
+            });
+        } else {
+            this.columns.forEach(function (c) {
+                c.tooltip = 'This is ' + c.label + '!';
+            });
+        }
+    },
+
+    openDataTablePrompt: function (row, name) {
+        this.dialogService.openPrompt({
+            message: 'Enter comment?',
+            value: row[name],
+        }).afterClosed().subscribe(function (value) {
+            if (value !== undefined) {
+                row[name] = value;
+            }
+        })
+    },
+
+    getAutoCompleteData: function () {
+        var self = this;
+        this.autoCompleteData = [];
+        this.columns.forEach(function (c) {
+            self.filteredData.forEach(function (r) {
+                (r[c.name.toLowerCase()]) ? self.autoCompleteData.push(r[c.name.toLowerCase()].toString()) : '';
+            });
+        });
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Chips $ Autocomplete">
+
+    toggleReadOnly: function () {
+        this.readOnly = !this.readOnly;
+    },
+
+    //</editor-fold>
+
+    //<editor-fold desc="Life Cycle Listeners">
+
+    /**
+     * Initialize the component
+     */
+    ngOnInit: function () {
+        this.filter();
+        this.filterDroplets();
+    },
+
+    /**
+     * Respond after Angular checks the component's views and child views
+     */
+    ngAfterViewChecked: function () {
+        this.fdsService.inProgress = false;
+    }
+
+    //</editor-fold>
+};
+
+FdsDemo.annotations = [
+    new ngCore.Component({
+        template: require('./fds-demo.html!text'),
+        animations: [fdsAnimations.slideInLeftAnimation],
+        host: {
+            '[@routeAnimation]': 'routeAnimation'
+        }
+    })
+];
+
+FdsDemo.parameters = [
+    fdsSnackBarsModule.FdsSnackBarService,
+    FdsService,
+    ngMaterial.MatDialog,
+    covalentCore.TdDataTableService,
+    fdsDialogsModule.FdsDialogService
+];
+
+module.exports = FdsDemo;

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds-bootstrap.js
----------------------------------------------------------------------
diff --git a/webapp/fds-bootstrap.js b/webapp/fds-bootstrap.js
new file mode 100644
index 0000000..d853f67
--- /dev/null
+++ b/webapp/fds-bootstrap.js
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+require('core-js');
+require('zone.js');
+require('hammerjs');
+var $ = require('jquery');
+var FdsModule = require('webapp/fds.module.js');
+var ngPlatformBrowserDynamic = require('@angular/platform-browser-dynamic');
+var ngCore = require('@angular/core');
+
+// Comment out this line when developing to assert for unidirectional data flow
+ngCore.enableProdMode();
+
+// Get the locale id from the global
+var locale = navigator.language;
+
+var providers = [];
+
+// No locale or U.S. English: no translation providers so go ahead and bootstrap the app
+if (!locale || locale === 'en-US') {
+    ngPlatformBrowserDynamic.platformBrowserDynamic().bootstrapModule(FdsModule, {providers: providers});
+} else { //load the translation providers and bootstrap the module
+    var translationFile = './webapp/messages.' + locale + '.xlf';
+
+    $.ajax({
+        url: translationFile
+    }).done(function (translations) {
+        // add providers if translation file for locale is loaded
+        if (translations) {
+            providers.push({provide: ngCore.TRANSLATIONS, useValue: translations});
+            providers.push({provide: ngCore.TRANSLATIONS_FORMAT, useValue: 'xlf'});
+            providers.push({provide: ngCore.LOCALE_ID, useValue: locale});
+        }
+        ngPlatformBrowserDynamic.platformBrowserDynamic().bootstrapModule(FdsModule, {providers: providers});
+    }).fail(function () {
+        ngPlatformBrowserDynamic.platformBrowserDynamic().bootstrapModule(FdsModule, {providers: providers});
+    });
+}

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds.animations.js
----------------------------------------------------------------------
diff --git a/webapp/fds.animations.js b/webapp/fds.animations.js
new file mode 100644
index 0000000..5f7d0cb
--- /dev/null
+++ b/webapp/fds.animations.js
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngAnimate = require('@angular/animations');
+
+/**
+ * NfAnimations constructor.
+ *
+ * @constructor
+ */
+function NfAnimations() {
+};
+
+NfAnimations.prototype = {
+    constructor: NfAnimations,
+
+    /**
+     * Fade animation
+     */
+    fadeAnimation: ngAnimate.trigger('routeAnimation', [
+        ngAnimate.state('*',
+            ngAnimate.style({
+                opacity: 1
+            })
+        ),
+        ngAnimate.transition(':enter', [
+            ngAnimate.style({
+                opacity: 0
+            }),
+            ngAnimate.animate('0.5s ease-in')
+        ]),
+        ngAnimate.transition(':leave', [
+            ngAnimate.animate('0.5s ease-out', ngAnimate.style({
+                opacity: 0
+            }))
+        ])
+    ]),
+
+    /**
+     * Slide in from the left animation
+     */
+    slideInLeftAnimation: ngAnimate.trigger('routeAnimation', [
+        ngAnimate.state('*',
+            ngAnimate.style({
+                opacity: 1,
+                transform: 'translateX(0)'
+            })
+        ),
+        ngAnimate.transition(':enter', [
+            ngAnimate.style({
+                opacity: 0,
+                transform: 'translateX(-100%)'
+            }),
+            ngAnimate.animate('0.5s ease-in')
+        ]),
+        ngAnimate.transition(':leave', [
+            ngAnimate.animate('0.5s ease-out', ngAnimate.style({
+                opacity: 0,
+                transform: 'translateX(100%)'
+            }))
+        ])
+    ]),
+
+    /**
+     * Slide in from the top animation
+     */
+    slideInDownAnimation: ngAnimate.trigger('routeAnimation', [
+        ngAnimate.state('*',
+            ngAnimate.style({
+                opacity: 1,
+                transform: 'translateY(0)'
+            })
+        ),
+        ngAnimate.transition(':enter', [
+            ngAnimate.style({
+                opacity: 0,
+                transform: 'translateY(-100%)'
+            }),
+            ngAnimate.animate('0.5s ease-in')
+        ]),
+        ngAnimate.transition(':leave', [
+            ngAnimate.animate('0.5s ease-out', ngAnimate.style({
+                opacity: 0,
+                transform: 'translateY(100%)'
+            }))
+        ])
+    ]),
+
+    /**
+     * Fly in/out animation
+     */
+    flyInOutAnimation: ngAnimate.trigger('flyInOut', [
+        ngAnimate.state('in',
+            ngAnimate.style({transform: 'translateX(0)'})
+        ),
+        ngAnimate.transition('void => *', [
+            ngAnimate.style({transform: 'translateX(100%)'}),
+            ngAnimate.animate('0.4s 0.1s ease-in')
+        ]),
+        ngAnimate.transition('* => void', ngAnimate.animate('0.2s ease-out', ngAnimate.style({transform: 'translateX(-100%)'})))
+    ]),
+
+    /**
+     * Fly in/out animation
+     */
+    fadeInOutAnimation: ngAnimate.trigger('fadeInOut', [
+        ngAnimate.state('in',
+            ngAnimate.style({opacity: 1})
+        ),
+        ngAnimate.transition('void => *', [
+            ngAnimate.style({opacity: 0}),
+            ngAnimate.animate('0.5s 0.1s ease-in')
+        ]),
+        ngAnimate.transition('* => void', ngAnimate.animate('0.5s ease-out', ngAnimate.style({opacity: 0})))
+    ])
+
+};
+
+module.exports = new NfAnimations();

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds.html
----------------------------------------------------------------------
diff --git a/webapp/fds.html b/webapp/fds.html
new file mode 100644
index 0000000..98a3f58
--- /dev/null
+++ b/webapp/fds.html
@@ -0,0 +1,34 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<mat-progress-spinner id="loading-spinner" *ngIf="fdsService.inProgress" mode="indeterminate"></mat-progress-spinner>
+<mat-sidenav-container>
+    <mat-sidenav #sidenav mode="over" align="end" opened="false" disableClose="true">
+        <router-outlet name="sidenav"></router-outlet>
+    </mat-sidenav>
+    <div id="fds-app-container">
+        <mat-toolbar id="fds-toolbar">
+            <!-- <img id="fds-logo" src="fds/images/fds-logo-web-app.svg"> -->
+            <div *ngIf="fdsService.perspective !== 'login' && fdsService.perspective !== 'not-found'" fxFlex="1 1 auto" class="pad-left-xl" [@flyInOut]="fdsService.breadCrumbState">
+                <span class="pointer">{{fdsService.title}}</span>
+            </div>
+        </mat-toolbar>
+        <div id="fds-perspectives-container">
+            <router-outlet></router-outlet>
+        </div>
+    </div>
+</mat-sidenav-container>

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds.js
----------------------------------------------------------------------
diff --git a/webapp/fds.js b/webapp/fds.js
new file mode 100644
index 0000000..175f9d7
--- /dev/null
+++ b/webapp/fds.js
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngCore = require('@angular/core');
+var ngCommonHttp = require('@angular/common/http');
+var FdsService = require('webapp/services/fds.service.js');
+var fdsAnimations = require('webapp/fds.animations.js');
+var ngRouter = require('@angular/router');
+var MILLIS_PER_SECOND = 1000;
+
+/**
+ * Fds constructor.
+ *
+ * @param fdsService            The fds service.
+ * @param changeDetectorRef     The change detector ref.
+ * @constructor
+ */
+function Fds(fdsService, changeDetectorRef) {
+    this.fdsService = fdsService;
+    this.cd = changeDetectorRef;
+};
+
+Fds.prototype = {
+    constructor: Fds,
+
+    /**
+     * Initialize the component
+     */
+    ngOnInit: function () {
+        var self = this;
+        this.fdsService.sidenav = this.sidenav; //ngCore.ViewChild
+    },
+
+    /**
+     * Since the child views are updating the fdsService values that are used to display
+     * the breadcrumbs in this component's view we need to manually detect changes at the correct
+     * point in the lifecycle.
+     */
+    ngAfterViewChecked: function () {
+        this.cd.detectChanges();
+    }
+};
+
+Fds.annotations = [
+    new ngCore.Component({
+        selector: 'fds-app',
+        template: require('./fds.html!text'),
+        queries: {
+            sidenav: new ngCore.ViewChild('sidenav')
+        },
+        animations: [fdsAnimations.flyInOutAnimation]
+    })
+];
+
+Fds.parameters = [
+    FdsService,
+    ngCore.ChangeDetectorRef
+];
+
+module.exports = Fds;

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds.module.js
----------------------------------------------------------------------
diff --git a/webapp/fds.module.js b/webapp/fds.module.js
new file mode 100644
index 0000000..bc8e80b
--- /dev/null
+++ b/webapp/fds.module.js
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngCore = require('@angular/core');
+var FdsRoutes = require('webapp/fds.routes.js');
+var fdsCore = require('@fluid-design-system/core');
+var Fds = require('webapp/fds.js');
+var FdsDemo = require('webapp/components/fluid-design-system/fds-demo.js');
+var FdsDemoDialog = require('webapp/components/fluid-design-system/dialogs/demo/fds-demo-dialog.js');
+var FdsService = require('webapp/services/fds.service.js');
+var ngCommonHttp = require('@angular/common/http');
+
+function FdsModule() {
+};
+
+FdsModule.prototype = {
+    constructor: FdsModule
+};
+
+FdsModule.annotations = [
+    new ngCore.NgModule({
+        imports: [
+            fdsCore,
+            FdsRoutes
+        ],
+        declarations: [
+            Fds,
+            FdsDemo,
+            FdsDemoDialog
+        ],
+        entryComponents: [
+            FdsDemoDialog
+        ],
+        providers: [
+            FdsService
+        ],
+        bootstrap: [Fds]
+    })
+];
+
+module.exports = FdsModule;

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/fds.routes.js
----------------------------------------------------------------------
diff --git a/webapp/fds.routes.js b/webapp/fds.routes.js
new file mode 100644
index 0000000..fa7b10e
--- /dev/null
+++ b/webapp/fds.routes.js
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ngRouter = require('@angular/router');
+var FdsDemo = require('webapp/components/fluid-design-system/fds-demo.js');
+
+var FdsRoutes = new ngRouter.RouterModule.forRoot([{
+    path: '',
+    component: FdsDemo
+}]);
+
+module.exports = FdsRoutes;

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/services/fds.service.js
----------------------------------------------------------------------
diff --git a/webapp/services/fds.service.js b/webapp/services/fds.service.js
new file mode 100644
index 0000000..26e6686
--- /dev/null
+++ b/webapp/services/fds.service.js
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var covalentCore = require('@covalent/core');
+var fdsDialogsModule = require('@fluid-design-system/dialogs');
+var fdsSnackBarsModule = require('@fluid-design-system/snackbars');
+
+/**
+ * FdsService constructor.
+ *
+ * @param tdDataTableService    The covalent data table service module.
+ * @param fdsDialogService      The FDS dialog service.
+ * @param fdsSnackBarService    The FDS snack bar service module.
+ * @constructor
+ */
+function FdsService(tdDataTableService, fdsDialogService, fdsSnackBarService) {
+    // Services
+    this.dialogService = fdsDialogService;
+    this.snackBarService = fdsSnackBarService;
+    this.dataTableService = tdDataTableService;
+
+    // General
+    this.title = "Apache NiFi Fluid Design System Demo";
+    this.inProgress = true;
+    this.perspective = '';
+};
+
+FdsService.prototype = {
+    constructor: FdsService,
+};
+
+FdsService.parameters = [
+    covalentCore.TdDataTableService,
+    fdsDialogsModule.FdsDialogService,
+    fdsSnackBarsModule.FdsSnackBarService
+];
+
+module.exports = FdsService;

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/systemjs-angular-loader.js
----------------------------------------------------------------------
diff --git a/webapp/systemjs-angular-loader.js b/webapp/systemjs-angular-loader.js
new file mode 100644
index 0000000..8e33bb5
--- /dev/null
+++ b/webapp/systemjs-angular-loader.js
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*)/gm;
+var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
+var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
+
+module.exports.translate = function (load) {
+    if (load.source.indexOf('moduleId') != -1) return load;
+
+    var url = document.createElement('a');
+    url.href = load.address;
+
+    var basePathParts = url.pathname.split('/');
+
+    basePathParts.pop();
+    var basePath = basePathParts.join('/');
+
+    var baseHref = document.createElement('a');
+    baseHref.href = this.baseURL;
+    baseHref = baseHref.pathname;
+
+    if (!baseHref.startsWith('/base/')) { // it is not karma
+        basePath = basePath.replace(baseHref, '');
+    }
+
+    load.source = load.source
+        .replace(templateUrlRegex, function (match, quote, url) {
+            var resolvedUrl = url;
+
+            if (url.startsWith('.')) {
+                resolvedUrl = basePath + url.substr(1);
+            }
+
+            return 'templateUrl: "' + resolvedUrl + '"';
+        })
+        .replace(stylesRegex, function (match, relativeUrls) {
+            var urls = [];
+
+            while ((match = stringRegex.exec(relativeUrls)) !== null) {
+                if (match[2].startsWith('.')) {
+                    urls.push('"' + basePath + match[2].substr(1) + '"');
+                } else {
+                    urls.push('"' + match[2] + '"');
+                }
+            }
+
+            return "styleUrls: [" + urls.join(', ') + "]";
+        });
+
+    return load;
+};

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/systemjs.config.extras.js
----------------------------------------------------------------------
diff --git a/webapp/systemjs.config.extras.js b/webapp/systemjs.config.extras.js
new file mode 100644
index 0000000..e2a256a
--- /dev/null
+++ b/webapp/systemjs.config.extras.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Add barrels and stuff
+ */
+(function (global) {
+    System.config({
+        packages: {
+            // add packages here
+        }
+    });
+})(this);

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/systemjs.config.js
----------------------------------------------------------------------
diff --git a/webapp/systemjs.config.js b/webapp/systemjs.config.js
new file mode 100644
index 0000000..24857de
--- /dev/null
+++ b/webapp/systemjs.config.js
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function (global) {
+    System.config({
+        paths: {
+            // paths serve as alias
+            'npm:': 'node_modules/'
+        },
+        // map tells the System loader where to look for things
+        map: {
+            'text': 'npm:systemjs-plugin-text/text.js',
+            'app': './webapp',
+
+            // jquery
+            'jquery': 'npm:jquery/dist/jquery.min.js',
+
+            // Angular
+            '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
+            '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
+            '@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
+            '@angular/common/http/testing': 'npm:@angular/common/bundles/common-http-testing.umd.js',
+            '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
+            '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
+            '@angular/http': 'npm:@angular/http/bundles/http.umd.js',
+            '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
+            '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
+            '@angular/flex-layout': 'npm:@angular/flex-layout/bundles/flex-layout.umd.js',
+            '@angular/flex-layout/core': 'npm:@angular/flex-layout/bundles/flex-layout-core.umd.js',
+            '@angular/flex-layout/extended': 'npm:@angular/flex-layout/bundles/flex-layout-extended.umd.js',
+            '@angular/flex-layout/flex': 'npm:@angular/flex-layout/bundles/flex-layout-flex.umd.js',
+            '@angular/material': 'npm:@angular/material/bundles/material.umd.js',
+            '@angular/material/core': 'npm:@angular/material/bundles/material-core.umd.js',
+            '@angular/material/card': 'npm:@angular/material/bundles/material-card.umd.js',
+            '@angular/material/divider': 'npm:@angular/material/bundles/material-divider.umd.js',
+            '@angular/material/progress-bar': 'npm:@angular/material/bundles/material-progress-bar.umd.js',
+            '@angular/material/progress-spinner': 'npm:@angular/material/bundles/material-progress-spinner.umd.js',
+            '@angular/material/chips': 'npm:@angular/material/bundles/material-chips.umd.js',
+            '@angular/material/input': 'npm:@angular/material/bundles/material-input.umd.js',
+            '@angular/material/icon': 'npm:@angular/material/bundles/material-icon.umd.js',
+            '@angular/material/button': 'npm:@angular/material/bundles/material-button.umd.js',
+            '@angular/material/checkbox': 'npm:@angular/material/bundles/material-checkbox.umd.js',
+            '@angular/material/tooltip': 'npm:@angular/material/bundles/material-tooltip.umd.js',
+            '@angular/material/dialog': 'npm:@angular/material/bundles/material-dialog.umd.js',
+            '@angular/material/sidenav': 'npm:@angular/material/bundles/material-sidenav.umd.js',
+            '@angular/material/menu': 'npm:@angular/material/bundles/material-menu.umd.js',
+            '@angular/material/form-field': 'npm:@angular/material/bundles/material-form-field.umd.js',
+            '@angular/material/toolbar': 'npm:@angular/material/bundles/material-toolbar.umd.js',
+            '@angular/material/autocomplete': 'npm:@angular/material/bundles/material-autocomplete.umd.js',
+            '@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
+            '@angular/cdk': 'npm:@angular/cdk/bundles/cdk.umd.js',
+            '@angular/cdk/a11y': 'npm:@angular/cdk/bundles/cdk-a11y.umd.js',
+            '@angular/cdk/accordion': 'npm:@angular/cdk/bundles/cdk-accordion.umd.js',
+            '@angular/cdk/layout': 'npm:@angular/cdk/bundles/cdk-layout.umd.js',
+            '@angular/cdk/collections': 'npm:@angular/cdk/bundles/cdk-collections.umd.js',
+            '@angular/cdk/observers': 'npm:@angular/cdk/bundles/cdk-observers.umd.js',
+            '@angular/cdk/overlay': 'npm:@angular/cdk/bundles/cdk-overlay.umd.js',
+            '@angular/cdk/platform': 'npm:@angular/cdk/bundles/cdk-platform.umd.js',
+            '@angular/cdk/portal': 'npm:@angular/cdk/bundles/cdk-portal.umd.js',
+            '@angular/cdk/keycodes': 'npm:@angular/cdk/bundles/cdk-keycodes.umd.js',
+            '@angular/cdk/bidi': 'npm:@angular/cdk/bundles/cdk-bidi.umd.js',
+            '@angular/cdk/coercion': 'npm:@angular/cdk/bundles/cdk-coercion.umd.js',
+            '@angular/cdk/table': 'npm:@angular/cdk/bundles/cdk-table.umd.js',
+            '@angular/cdk/rxjs': 'npm:@angular/cdk/bundles/cdk-rxjs.umd.js',
+            '@angular/cdk/scrolling': 'npm:@angular/cdk/bundles/cdk-scrolling.umd.js',
+            '@angular/cdk/stepper': 'npm:@angular/cdk/bundles/cdk-stepper.umd.js',
+            '@angular/animations': 'npm:@angular/animations/bundles/animations.umd.js',
+            '@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
+            '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
+
+            // needed to support gestures for angular material
+            'hammerjs': 'npm:hammerjs/hammer.min.js',
+
+            // Covalent
+            '@covalent/core': 'npm:@covalent/core/bundles/covalent-core.umd.min.js',
+            '@covalent/core/common': 'npm:@covalent/core/bundles/covalent-core-common.umd.min.js',
+
+            // other libraries
+            'rxjs': 'npm:rxjs',
+            'zone.js': 'npm:zone.js/dist/zone.js',
+            'core-js': 'npm:core-js/client/shim.min.js',
+            'superagent': 'npm:superagent/superagent.js',
+            'querystring': 'npm:querystring',
+            'tslib': 'npm:tslib/tslib.js',
+
+            // Fluid Design System
+            '@fluid-design-system/core': 'platform/core/fluid-design-system.module.js',
+            '@fluid-design-system/dialogs': 'platform/core/dialogs/fds-dialogs.module.js',
+            '@fluid-design-system/dialog-component': 'platform/core/dialogs/fds-dialog.component.js',
+            '@fluid-design-system/dialog-service': 'platform/core/dialogs/services/dialog.service.js',
+            '@fluid-design-system/confirm-dialog-component': 'platform/core/dialogs/confirm-dialog/confirm-dialog.component.js',
+            '@fluid-design-system/snackbars': 'platform/core/snackbars/fds-snackbars.module.js',
+            '@fluid-design-system/snackbar-component': 'platform/core/snackbars/fds-snackbar.component.js',
+            '@fluid-design-system/snackbar-service': 'platform/core/snackbars/services/snackbar.service.js',
+            '@fluid-design-system/coaster-component': 'platform/core/snackbars/coaster/coaster.component.js',
+            '@fluid-design-system/common/storage-service': 'platform/core/common/services/fds-storage.service.js'
+        },
+        // packages tells the System loader how to load when no filename and/or no extension
+        packages: {
+            app: {
+                defaultExtension: 'js',
+                meta: {
+                    './*.js': {
+                        loader: 'webapp/systemjs-angular-loader.js'
+                    }
+                }
+            },
+            'webapp/systemjs-angular-loader.js': {
+                loader: false
+            },
+            'rxjs': {
+                defaultExtension: 'js'
+            },
+            'querystring': {
+                main: './index.js',
+                defaultExtension: 'js'
+            },
+            'moment': {
+                main: './moment.js',
+                defaultExtension: 'js'
+            },
+            'angular2-moment': {
+                main: './index.js',
+                defaultExtension: 'js'
+            }
+        }
+    });
+})(this);

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/theming/_helperClasses.scss
----------------------------------------------------------------------
diff --git a/webapp/theming/_helperClasses.scss b/webapp/theming/_helperClasses.scss
new file mode 100644
index 0000000..6918db4
--- /dev/null
+++ b/webapp/theming/_helperClasses.scss
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the 'License'); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.fa-rotate-45 {
+    -webkit-transform: rotate(45deg);
+    -moz-transform: rotate(45deg);
+    -ms-transform: rotate(45deg);
+    -o-transform: rotate(45deg);
+    transform: rotate(45deg);
+}
+
+.capitalize {
+    text-transform: capitalize;
+}
+
+.uppercase {
+    text-transform: uppercase;
+}
+
+.ellipsis {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: block !important;
+}
+
+.info {
+    color: $blue8;
+}
+
+.authorized {
+    color: $red2;
+}
+
+.suspended {
+    color: $green3;
+}
+
+.nonconfigurable {
+    background: $grey9;
+}
+
+.selected-nonconfigurable {
+    background: repeating-linear-gradient(-45deg, $grey5, $grey5 10px, #fff 10px, #fff 20px) !important;
+}
+
+.fds-button-container {
+    position: absolute;
+    bottom: 0px;
+    height: 64px;
+    left: 0px;
+    right: 0px;
+    border-top: 1px solid $grey4;
+}
+
+.fds-button-container .done-button {
+    float: right;
+    margin-right: 15px;
+    margin-top: 15px;
+}
+
+.td-expansion-content {
+    background: $grey6;
+}

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/theming/_structureElements.scss
----------------------------------------------------------------------
diff --git a/webapp/theming/_structureElements.scss b/webapp/theming/_structureElements.scss
new file mode 100644
index 0000000..b84c60a
--- /dev/null
+++ b/webapp/theming/_structureElements.scss
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+body {
+  background: $grey12;
+  background-size: 40%;
+}
+
+#fds-app-container {
+  margin: 0;
+  width: 100%;
+  height: 100%;
+}
+
+#fds-toolbar {
+  min-width: 1045px;
+  background-color: #FFFFFF;
+  position: absolute;
+  z-index: 1000;
+  background: $grey11;
+}
+
+#fds-toolbar .mat-icon-button {
+  color: $grey5;
+  font-size: 20px;
+  margin: 0;
+}
+
+#fds-toolbar .mat-select-value-text, #fds-toolbar .mat-select-arrow {
+  color: white;
+}
+
+#fds-toolbar span, #fds-toolbar .link {
+  color: $grey5;
+  font-weight: lighter;
+}
+
+#fds-perspectives-container {
+  position: absolute;
+  top: 64px;
+  left: 0px;
+  right: 0px;
+  bottom: 0px;
+  min-height: 370px;
+  min-width: 1045px;
+  overflow: auto;
+  background: $grey12;
+  background-size: 40%;
+}
+
+#loading-spinner {
+  position: absolute;
+  top: calc(50% - 50px);
+  left: calc(50% - 50px);
+  z-index: 2;
+}
+
+mat-sidenav {
+  width: 40%;
+  min-width: 418px;
+}
+
+.sidenav-content {
+  position: absolute;
+  bottom: 60px;
+  top: 80px;
+  right: 0px;
+  left: 0px;
+  overflow: auto;
+}

http://git-wip-us.apache.org/repos/asf/nifi-fds/blob/b2b1baa3/webapp/theming/fds-demo.scss
----------------------------------------------------------------------
diff --git a/webapp/theming/fds-demo.scss b/webapp/theming/fds-demo.scss
new file mode 100644
index 0000000..c7730fa
--- /dev/null
+++ b/webapp/theming/fds-demo.scss
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the 'License'); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Welcome to Compass.
+ * In this file you should centralize your imports. After compilation simply import this file using the following HTML or equivalent:
+ * <link href='webapp/css/fds-demo.min.css' media='screen, projection' rel='stylesheet' type='text/css' /> */
+
+@import '../../platform/core/common/styles/globalVars';
+@import '../../platform/core/theming/all-theme';
+@import 'structureElements';
+@import 'helperClasses';
+
+//Change these
+$primaryColor: $rose1;
+$primaryColorHover: $rose2;
+$accentColor: $blue7;
+$accentColorHover: $grey4;
+
+// Include the base styles for Angular Material core. We include this here so that you only
+// have to load a single css file for Angular Material in your app.
+@include mat-core;
+
+// Define the palettes
+$fds-base-palette: (50: #89df79, 100: $primaryColorHover, 200: #65d550, 300: #53d03b, 400: #46c32f, 500: $primaryColor, 600: $primaryColor, 700: #89df79, 800: #29701b, 900: #215c16, A100: #9be48d, A200: #ade9a2, A400: #bfedb6, A700: #1a4711, contrast: (50: $black-87-opacity, 100: $black-87-opacity, 200: $black-87-opacity, 300: white, 400: white, 500: $white-87-opacity, 600: $white-87-opacity, 700: $white-87-opacity, 800: $white-87-opacity, 900: $white-87-opacity, A100: $black-87-opacity, A200: white, A400: white, A700: $white-87-opacity));
+$fds-accent-palette: (50: #89df79, 100: $accentColorHover, 200: #65d550, 300: #53d03b, 400: #46c32f, 500: $accentColor, 600: $accentColor, 700: #89df79, 800: #29701b, 900: #215c16, A100: #9be48d, A200: #ade9a2, A400: #bfedb6, A700: #1a4711, contrast: (50: $black-87-opacity, 100: $black-87-opacity, 200: $black-87-opacity, 300: white, 400: white, 500: $white-87-opacity, 600: $white-87-opacity, 700: $white-87-opacity, 800: $white-87-opacity, 900: $white-87-opacity, A100: $black-87-opacity, A200: white, A400: white, A700: $white-87-opacity));
+$fds-warn-palette: (50: #81410f, 100: #D14A50, 200: #af5814, 300: #c66317, 400: #dd6f19, 500: $warnColor, 600: $warnColor, 700: #eea66e, 800: #f1b485, 900: #f4c29b, A100: #ec9857, A200: #89df79, A400: #89df79, A700: #f6d0b2, contrast: (50: $black-87-opacity, 100: $black-87-opacity, 200: $black-87-opacity, 300: white, 400: white, 500: $white-87-opacity, 600: $white-87-opacity, 700: $white-87-opacity, 800: $white-87-opacity, 900: $white-87-opacity, A100: $black-87-opacity, A200: white, A400: white, A700: $white-87-opacity));
+$fds-primary: mat-palette($fds-base-palette, 500, 100, 500);
+$fds-accent: mat-palette($fds-accent-palette, 500, 100, 500);
+$fds-warn: mat-palette($fds-warn-palette, 500, 100, 500);
+
+// Define the theme (Optionally specify a default, lighter, and darker hue.)
+$fds-theme: mat-light-theme($fds-primary, $fds-accent, $fds-warn);
+
+// FDS theme mixin
+@include fds-theme($fds-theme);