From 8d56acfeac3086283b2aa10d6eef477868d5c870 Mon Sep 17 00:00:00 2001 From: Thomas O'Connor Date: Wed, 13 Apr 2016 17:09:04 -0700 Subject: [PATCH] Convert Fabric Components to TypeScript (#489) * Add TypeScript to the gulp build tools. * Add tslint to build process for Component Samples. * Convert all component JavScript files over to TypeScript. - Components are no longer implemented as jQuery plugins, as all components are now classes within the fabric namespace. - jQuery removal from all of the components using it is still ongoing work. --- README.md | 4 +- gulp/ComponentJS.js | 97 + gulp/ComponentSamples.js | 31 +- gulp/FabricComponents.js | 36 +- gulp/modules/Config.js | 20 +- gulp/modules/ErrorHandling.js | 7 +- gulp/modules/Plugins.js | 24 +- gulpfile.js | 10 +- .../DatePicker => lib}/PickaDate.js | 0 package.json | 4 + src/components/Breadcrumb/Breadcrumb.js | 236 -- src/components/Breadcrumb/Breadcrumb.ts | 235 ++ .../CommandBar/Jquery.CommandBar.js | 163 - .../CommandBar/Jquery.CommandBar.ts | 178 + .../ContextualMenu/Jquery.ContextualMenu.js | 59 - .../ContextualMenu/Jquery.ContextualMenu.ts | 57 + .../DatePicker/Jquery.DatePicker.js | 286 -- .../DatePicker/Jquery.DatePicker.ts | 286 ++ src/components/Dialog/jquery.Dialog.js | 44 - src/components/Dialog/jquery.Dialog.ts | 53 + src/components/Dropdown/Jquery.Dropdown.js | 157 - src/components/Dropdown/Jquery.Dropdown.ts | 162 + src/components/Facepile/Jquery.Facepile.js | 207 -- src/components/Facepile/Jquery.Facepile.ts | 218 ++ src/components/ListItem/Jquery.ListItem.js | 27 - src/components/ListItem/Jquery.ListItem.ts | 28 + src/components/MessageBanner/MessageBanner.js | 162 - src/components/MessageBanner/MessageBanner.ts | 163 + src/components/NavBar/Jquery.NavBar.js | 95 - src/components/NavBar/Jquery.NavBar.ts | 102 + src/components/Panel/Jquery.Panel.js | 91 - src/components/Panel/Jquery.Panel.ts | 97 + .../PeoplePicker/Jquery.PeoplePicker.js | 398 -- .../PeoplePicker/Jquery.PeoplePicker.ts | 417 +++ src/components/PeoplePicker/PeoplePicker.json | 3 +- .../PersonaCard/Jquery.PersonaCard.js | 57 - .../PersonaCard/Jquery.PersonaCard.ts | 60 + src/components/Pivot/jquery.Pivot.js | 37 - src/components/Pivot/jquery.Pivot.ts | 40 + .../ProgressIndicator/ProgressIndicator.js | 103 - .../ProgressIndicator/ProgressIndicator.ts | 101 + src/components/SearchBox/Jquery.SearchBox.js | 69 - src/components/SearchBox/Jquery.SearchBox.ts | 77 + src/components/Spinner/Spinner.js | 151 - src/components/Spinner/Spinner.ts | 159 + src/components/TextField/Jquery.TextField.js | 51 - src/components/TextField/Jquery.TextField.ts | 57 + src/templates/componentSampleTemplate.html | 19 +- .../individual-component-example.html | 16 +- tslint.json | 75 + typings/jquery.d.ts | 3210 +++++++++++++++++ typings/pickadate.d.ts | 545 +++ 52 files changed, 6477 insertions(+), 2507 deletions(-) create mode 100644 gulp/ComponentJS.js rename {src/components/DatePicker => lib}/PickaDate.js (100%) delete mode 100644 src/components/Breadcrumb/Breadcrumb.js create mode 100644 src/components/Breadcrumb/Breadcrumb.ts delete mode 100644 src/components/CommandBar/Jquery.CommandBar.js create mode 100644 src/components/CommandBar/Jquery.CommandBar.ts delete mode 100644 src/components/ContextualMenu/Jquery.ContextualMenu.js create mode 100644 src/components/ContextualMenu/Jquery.ContextualMenu.ts delete mode 100644 src/components/DatePicker/Jquery.DatePicker.js create mode 100644 src/components/DatePicker/Jquery.DatePicker.ts delete mode 100644 src/components/Dialog/jquery.Dialog.js create mode 100644 src/components/Dialog/jquery.Dialog.ts delete mode 100644 src/components/Dropdown/Jquery.Dropdown.js create mode 100644 src/components/Dropdown/Jquery.Dropdown.ts delete mode 100644 src/components/Facepile/Jquery.Facepile.js create mode 100644 src/components/Facepile/Jquery.Facepile.ts delete mode 100644 src/components/ListItem/Jquery.ListItem.js create mode 100644 src/components/ListItem/Jquery.ListItem.ts delete mode 100644 src/components/MessageBanner/MessageBanner.js create mode 100644 src/components/MessageBanner/MessageBanner.ts delete mode 100644 src/components/NavBar/Jquery.NavBar.js create mode 100644 src/components/NavBar/Jquery.NavBar.ts delete mode 100644 src/components/Panel/Jquery.Panel.js create mode 100644 src/components/Panel/Jquery.Panel.ts delete mode 100644 src/components/PeoplePicker/Jquery.PeoplePicker.js create mode 100644 src/components/PeoplePicker/Jquery.PeoplePicker.ts delete mode 100644 src/components/PersonaCard/Jquery.PersonaCard.js create mode 100644 src/components/PersonaCard/Jquery.PersonaCard.ts delete mode 100644 src/components/Pivot/jquery.Pivot.js create mode 100644 src/components/Pivot/jquery.Pivot.ts delete mode 100644 src/components/ProgressIndicator/ProgressIndicator.js create mode 100644 src/components/ProgressIndicator/ProgressIndicator.ts delete mode 100644 src/components/SearchBox/Jquery.SearchBox.js create mode 100644 src/components/SearchBox/Jquery.SearchBox.ts delete mode 100644 src/components/Spinner/Spinner.js create mode 100644 src/components/Spinner/Spinner.ts delete mode 100644 src/components/TextField/Jquery.TextField.js create mode 100644 src/components/TextField/Jquery.TextField.ts create mode 100644 tslint.json create mode 100644 typings/jquery.d.ts create mode 100644 typings/pickadate.d.ts diff --git a/README.md b/README.md index 5fb136f2..af0b3990 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Fabric is a responsive, mobile-first collection of styles and tools designed to ##Get started -For a quick start, reference the latest release of Fabric from a CDN or add a copy to your project. See [Getting Started](http://dev.office.com/fabric/getting-started) on the [Office UI Fabric site](http://dev.office.com/fabric) for full details. +For a quick start, reference the latest release of Fabric from a CDN or add a copy to your project. See [Get Started](http://dev.office.com/fabric/get-started) on the [Office UI Fabric site](http://dev.office.com/fabric) for full details. Want to customize Fabric for your project? See [Building Fabric](https://github.com/OfficeDev/Office-UI-Fabric/blob/master/ghdocs/BUILDING.md) to learn about the build process. @@ -37,4 +37,4 @@ All files on the Office UI Fabric GitHub repository are subject to the MIT licen ##Changelog -We use [GitHub Releases](https://github.com/blog/1547-release-your-software) to manage our releases, including the changelog between every release. View a complete list of additions, fixes, and changes since 1.0 on the [Releases page](https://github.com/OfficeDev/Office-UI-Fabric/releases). +We use [GitHub Releases](https://github.com/blog/1547-release-your-software) to manage our releases, including the changelog between every release. View a complete list of additions, fixes, and changes since 1.0 on the [releases](https://github.com/OfficeDev/Office-UI-Fabric/releases) page. diff --git a/gulp/ComponentJS.js b/gulp/ComponentJS.js new file mode 100644 index 00000000..1015265a --- /dev/null +++ b/gulp/ComponentJS.js @@ -0,0 +1,97 @@ +var gulp = require('gulp'); +var Banners = require('./modules/Banners'); +var Config = require('./modules/Config'); +var ErrorHandling = require('./modules/ErrorHandling'); +var Plugins = require('./modules/Plugins'); + +// +// Clean/Delete Tasks +// ---------------------------------------------------------------------------- + +gulp.task('ComponentJS-nuke', function () { + return Plugins.del.sync([Config.paths.distJS, Config.paths.distLibPath]); +}); + + +// +// Library tasks +// ---------------------------------------------------------------------------- +gulp.task('ComponentJS-copyLib', function() { + return gulp.src(Config.paths.srcLibPath + '/**/*') + .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) + .pipe(Plugins.changed(Config.paths.distLibPath)) + .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ + title: "Copying /lib folder to dist folder" + }))) + .pipe(gulp.dest(Config.paths.distLibPath)); +}); + +// +// Typescript tasks +// ---------------------------------------------------------------------------- + +gulp.task('ComponentJS-typescript', function() { + var tscResult = gulp.src(Config.paths.componentsPath + '/**/*.ts') + + // only process TS files that have changed since last compiled to /dist/Components + .pipe(Plugins.changed(Config.paths.distComponents, {extension: '.js'})) + .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) + + // tslint options set by tslint.json + .pipe(Plugins.tslint()) + .pipe(Plugins.tslint.report("verbose")) + + // Typescript project is set to give us both definitions and javascript + .pipe(Plugins.tsc(Config.typescriptProject)); + + return Plugins.mergeStream( [ + + // place .d.ts output in both the Samples folder and the Components folder + tscResult.dts.pipe(gulp.dest(Config.paths.distSamples + '/Components')) + .pipe(gulp.dest(Config.paths.distComponents)) + .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ + title: "Output Fabric Component .d.ts built from TypeScript" + }))), + + // place .js output in both the Samples folder and the Components folder + tscResult.js.pipe(gulp.dest(Config.paths.distSamples + '/Components')) + .pipe(gulp.dest(Config.paths.distComponents)) + .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ + title: "Output Fabric Component .js built from TypeScript" + }))) + ]); +}); + +// +// Concat and minify the output files into a single fabric.js file +gulp.task('ComponentJS-concatJS', ['ComponentJS-typescript'], function() { + + return gulp.src(Config.paths.distComponents + '/**/*.js') + .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) + .pipe(Plugins.concat('fabric.js')) + .pipe(Plugins.header(Banners.getJSCopyRight())) + .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ + title: "Concat Fabric Component JS" + }))) + .pipe(gulp.dest(Config.paths.distJS)) + .pipe(Plugins.rename('fabric.min.js')) + .pipe(Plugins.uglify()) + .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ + title: "Minify Fabric Component JS" + }))) + .pipe(gulp.dest(Config.paths.distJS)); +}); + + +// +// Rolled up Build tasks +// ---------------------------------------------------------------------------- + +var ComponentJSTasks = [ + 'ComponentJS-copyLib', + 'ComponentJS-typescript', + 'ComponentJS-concatJS' +]; + +//Build Fabric Component Samples +gulp.task('ComponentJS', ComponentJSTasks); diff --git a/gulp/ComponentSamples.js b/gulp/ComponentSamples.js index 1ca11259..40a39e9d 100644 --- a/gulp/ComponentSamples.js +++ b/gulp/ComponentSamples.js @@ -1,6 +1,7 @@ var gulp = require('gulp'); var fs = require('fs'); var Utilities = require('./modules/Utilities'); +var Banners = require('./modules/Banners'); var Config = require('./modules/Config'); var BuildConfig = require('./modules/BuildConfig'); var ConsoleHelper = require('./modules/ConsoleHelper'); @@ -51,24 +52,6 @@ gulp.task('ComponentSamples-copyAssets', function() { .pipe(gulp.dest(Config.paths.distSamples + '/Components')); }); -gulp.task('ComponentSamples-moveJS', function() { - var paths; - var newPaths; - paths = Utilities.setIgnoreFlagOnFiles(Config.ignoreComponentJSLinting); - newPaths = paths.concat([Config.paths.componentsPath + '/**/*.js']); - - return gulp.src(newPaths) - .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) - .pipe(Plugins.jshint()) - .pipe(ErrorHandling.JSHintErrors()) - .pipe(Plugins.changed(Config.paths.distSamples + '/Components')) - .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ - title: "Copying Component Assets" - }))) - .pipe(Plugins.fileinclude()) - .pipe(gulp.dest(Config.paths.distSamples + '/Components')); -}); - gulp.task('ComponentSamples-styleHinting', function() { return gulp.src(Config.paths.componentsPath + '/**/*.scss') @@ -131,12 +114,7 @@ gulp.task('ComponentSamples-build', function() { var filesArray = manifest.fileOrder; var componentPipe; var fileGlob = Utilities.getManifestFileList(filesArray, Config.paths.componentsPath + '/' + folderName); - var jsFiles = Utilities.getFilesByExtension(srcFolderName, '.js'); - var jsLinks = ''; - - for (var x = 0; x < jsFiles.length; x++) { - jsLinks += '' + "\r\n"; - } + componentPipe = gulp.src(fileGlob) .pipe(Plugins.plumber(ErrorHandling.oneErrorInPipe)) .pipe(Plugins.gulpif(manifest.wrapBranches, Plugins.wrap('
<%= contents %>
'))) @@ -148,9 +126,6 @@ gulp.task('ComponentSamples-build', function() { }, { componentName: folderName - }, - { - jsLinks: jsLinks } )) .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ @@ -178,7 +153,7 @@ var ComponentSamplesTasks = [ 'ComponentSamples-build', 'ComponentSamples-copyAssets', 'ComponentSamples-buildStyles', - 'ComponentSamples-moveJS', + 'ComponentJS', 'ComponentSamples-copyIgnoredFiles' // 'ComponentSamples-styleHinting' Commented out until warnings are resolved ]; diff --git a/gulp/FabricComponents.js b/gulp/FabricComponents.js index 956417e1..2eed0f27 100644 --- a/gulp/FabricComponents.js +++ b/gulp/FabricComponents.js @@ -25,7 +25,7 @@ gulp.task('FabricComponents-nuke', function () { gulp.task('FabricComponents-copyAssets', function () { // Copy all Components files. - return gulp.src([Config.paths.componentsPath + '/**', '!' + Config.paths.componentsPath + '/**/*.js']) + return gulp.src([Config.paths.componentsPath + '/**', '!' + Config.paths.componentsPath + '/**/*.js', '!' + Config.paths.componentsPath + '/**/*.ts']) .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) .pipe(Plugins.changed(Config.paths.distComponents)) .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ @@ -34,18 +34,6 @@ gulp.task('FabricComponents-copyAssets', function () { .pipe(gulp.dest(Config.paths.distComponents)); }); -gulp.task('FabricComponents-moveJs', function () { - // Copy all Components files. - return gulp.src([Config.paths.componentsPath + '/**/*.js']) - .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) - .pipe(Plugins.changed(Config.paths.distComponents)) - .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ - title: "Moving Fabric Component Assets to Dist" - }))) - .pipe(Plugins.fileinclude()) - .pipe(gulp.dest(Config.paths.distComponents)); -}); - // // Sass tasks // ---------------------------------------------------------------------------- @@ -116,26 +104,6 @@ gulp.task('FabricComponents-buildStyles', function () { }); }); -// -// JS Only tasks -// ---------------------------------------------------------------------------- - - -gulp.task('FabricComponents-moveJs', function() { - return gulp.src(Config.paths.componentsPath + '/**/*.js') - .pipe(Plugins.plumber(ErrorHandling.onErrorInPipe)) - .pipe(Plugins.concat('jquery.fabric.js')) - .pipe(Plugins.header(Banners.getJSCopyRight())) - .pipe(Plugins.changed(Config.paths.distJS)) - .pipe(Plugins.gulpif(Config.debugMode, Plugins.debug({ - title: "Moving Fabric Component JS" - }))) - .pipe(gulp.dest(Config.paths.distJS)) - .pipe(Plugins.rename('jquery.fabric.min.js')) - .pipe(Plugins.uglify()) - .pipe(gulp.dest(Config.paths.distJS)); -}); - // // Rolled up Build tasks // ---------------------------------------------------------------------------- @@ -145,7 +113,7 @@ gulp.task('FabricComponents', [ 'FabricComponents-buildAndCombineStyles', 'FabricComponents-buildStyles', 'FabricComponents-copyAssets', - 'FabricComponents-moveJs' + 'ComponentJS' ] ); diff --git a/gulp/modules/Config.js b/gulp/modules/Config.js index 864f377f..f75e6660 100644 --- a/gulp/modules/Config.js +++ b/gulp/modules/Config.js @@ -1,5 +1,6 @@ var path = require('path'); var pkg = require('../../package.json'); +var Plugins = require('./Plugins'); /** * Configuration class containing all properties to be used throughout the build @@ -24,7 +25,9 @@ var Config = function() { srcSamples: srcPath + '/samples', srcSass: srcPath + '/sass', componentsPath : 'src/components', - templatePath : srcPath + '/templates' + templatePath : srcPath + '/templates', + srcLibPath: 'lib', + distLibPath: distPath + '/lib' }; this.port = process.env.PORT || 2020; this.projectURL = "http://localhost"; @@ -33,8 +36,23 @@ var Config = function() { { 'urlPath': '/css', 'folderPath': '../css' + }, + { + 'urlPath': '/js', + 'folderPath': '../js' + }, + { + 'urlPath': '/lib', + 'folderPath': '../lib' } ]; + this.typescriptConfig = { + module: 'commonjs', + declaration: true, + target: 'ES5', + noEmitOnError: true + }; + this.typescriptProject = Plugins.tsc.createProject(this.typescriptConfig); this.nugetConfig = { id: "OfficeUIFabric", title: "Office UI Fabric", diff --git a/gulp/modules/ErrorHandling.js b/gulp/modules/ErrorHandling.js index 6ab738e6..d2e1566f 100644 --- a/gulp/modules/ErrorHandling.js +++ b/gulp/modules/ErrorHandling.js @@ -140,9 +140,12 @@ var ErrorHandling = function() { case 'gulp-sass': this.emit('end'); break; + case 'gulp-tslint': + this.emit('end'); + break; default: - that.generateBuildError(error[0]); - that.addError(error[0]); + that.generateBuildError(error); + that.addError(error); break; } return; diff --git a/gulp/modules/Plugins.js b/gulp/modules/Plugins.js index c2460ac0..e1a72502 100644 --- a/gulp/modules/Plugins.js +++ b/gulp/modules/Plugins.js @@ -33,17 +33,19 @@ var Plugins = function() { this.nugetpack = require('gulp-nuget-pack'); this.requireDir = require('require-dir'); this.debug = require('gulp-debug'); - this.gulpif = require('gulp-if'); - this.changed = require('gulp-changed'); - this.sass = require('gulp-sass'); - this.jshint = require('gulp-jshint'); - this.plumber = require('gulp-plumber'); - this.replace = require('gulp-replace'); - this.walkSync = require('walk-sync'); - this.size = require('gulp-size'); - this.fs = require('fs'); - this.sasslint = require('gulp-sass-lint'); - this.fileinclude = require('gulp-file-include'); + this.gulpif = require('gulp-if'); + this.changed = require('gulp-changed'); + this.sass = require('gulp-sass'); + this.jshint = require('gulp-jshint'); + this.plumber = require('gulp-plumber'); + this.replace = require('gulp-replace'); + this.walkSync = require('walk-sync'); + this.size = require('gulp-size'); + this.fs = require('fs'); + this.sasslint = require('gulp-sass-lint'); + this.fileinclude = require('gulp-file-include'); + this.tsc = require('gulp-typescript'); + this.tslint = require("gulp-tslint"); }; module.exports = new Plugins(); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 03205083..22a431f3 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,6 +9,7 @@ var ErrorHandling = require('./gulp/modules/ErrorHandling'); var watchTasks = [ 'Fabric', + 'ComponentJS', 'FabricComponents', 'ComponentSamples', 'Samples', @@ -17,6 +18,7 @@ var watchTasks = [ var buildTasks = [ 'Fabric', + 'ComponentJS', 'FabricComponents', 'ComponentSamples', 'Samples', @@ -50,22 +52,22 @@ gulp.task('FabricServer', function() { // // Nuke Tasks // --------------------------------------------------------------------------- -gulp.task('nuke', ['Fabric-nuke', 'FabricComponents-nuke', 'ComponentSamples-nuke', 'Samples-nuke']); +gulp.task('nuke', ['Fabric-nuke', 'ComponentJS-nuke', 'FabricComponents-nuke', 'ComponentSamples-nuke', 'Samples-nuke']); // // Watch Tasks // ---------------------------------------------------------------------------- // Watch Sass -gulp.task('watch-build', ['ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-server'], function () { +gulp.task('watch-build', ['ComponentJS', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-server'], function () { gulp.watch(Config.paths.srcPath + '/**/*', Plugins.batch(function (events, done) { - Plugins.runSequence(['Fabric', 'FabricComponents', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-updated'], done); + Plugins.runSequence(['Fabric', 'ComponentJS', 'FabricComponents', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-updated'], done); })); }); gulp.task('watch', ['watch-build']); -gulp.task('watch-debug-build', ['Fabric', 'FabricComponents', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-server'], function () { +gulp.task('watch-debug-build', ['ComponentJS', 'Fabric', 'FabricComponents', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-server'], function () { gulp.watch(Config.paths.srcPath + '/**/*', Plugins.batch(function (events, done) { Plugins.runSequence(['Fabric', 'FabricComponents', 'ComponentSamples', 'Samples', 'FabricDemoPage', 'FabricServer', 'All-updated'], done); })); diff --git a/src/components/DatePicker/PickaDate.js b/lib/PickaDate.js similarity index 100% rename from src/components/DatePicker/PickaDate.js rename to lib/PickaDate.js diff --git a/package.json b/package.json index f347defc..6600dff7 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,8 @@ "gulp-size": "^1.0.0", "gulp-tap": "^0.1.3", "gulp-template": "^3.0.0", + "gulp-tslint": "^4.3.2", + "gulp-typescript": "^2.10.0", "gulp-uglify": "^1.5.2", "gulp-util": "^3.0.1", "gulp-wrap": "^0.11.0", @@ -63,6 +65,8 @@ "run-sequence": "^1.1.0", "swig": "^1.4.2", "through2": "^2.0.0", + "tslint": "^3.3.0", + "typescript": "^1.7.5", "walk-sync": "^0.2.6" } } diff --git a/src/components/Breadcrumb/Breadcrumb.js b/src/components/Breadcrumb/Breadcrumb.js deleted file mode 100644 index 8db08cb8..00000000 --- a/src/components/Breadcrumb/Breadcrumb.js +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Breadcrumb component - * - * Shows the user's current location in a hierarchy and provides a means of navigating upward. - * - */ - -/** - * @namespace fabric - */ -var fabric = fabric || {}; -/** - * - * @param {HTMLElement} container - the target container for an instance of Breadcrumb - * @constructor - * - * If dynamically populating a list run the constructor after the list has been populated - * in the DOM. - */ -fabric.Breadcrumb = function(container) { - this.container = container; - this.init(); -}; - -fabric.Breadcrumb.prototype = (function() { - - //medium breakpoint - var MEDIUM = 639; - - //cached DOM elements - var _breadcrumb; - var _listItems; - var _contextMenu; - var _overflowButton; - var _overflowMenu; - var _breadcrumbList; - - var _currentMaxItems = 0; - var _itemCollection = []; - - /** - * create internal model of list items from DOM - */ - var _createItemCollection = function() { - var length = _listItems.length; - var i = 0; - var item; - var text; - var link; - var tabIndex; - - for(i; i < length; i++) { - item = _listItems[i].querySelector('.ms-Breadcrumb-itemLink'); - text = item.textContent; - link = item.getAttribute('href'); - tabIndex = parseInt(item.getAttribute('tabindex'), 10); - _itemCollection.push({text: text, link: link, tabIndex: tabIndex}); - } - }; - - /** - * Re-render lists on resize - * - */ - var _onResize = function() { - _closeOverflow(null); - _renderList(); - }; - - /** - * render breadcrumbs and overflow menus - */ - var _renderList = function() { - var maxItems = window.innerWidth > MEDIUM ? 4 : 2; - - if(maxItems !== _currentMaxItems) { - if(_itemCollection.length > maxItems) { - _breadcrumb.className += ' is-overflow'; - } else { - _removeClass(_breadcrumb, ' is-overflow'); - } - _addBreadcrumbItems(maxItems); - _addItemsToOverflow(maxItems); - } - - _currentMaxItems = maxItems; - }; - - /** - * creates the overflow menu - */ - var _addItemsToOverflow = function(maxItems) { - _resetList(_contextMenu); - var end = _itemCollection.length - maxItems; - var overflowItems = _itemCollection.slice(0, end); - - overflowItems.forEach(function(item) { - var li = document.createElement('li'); - li.className = 'ms-ContextualMenu-item'; - if(!isNaN(item.tabIndex)) { - li.setAttribute('tabindex', item.tabIndex); - } - var a = document.createElement('a'); - a.className = 'ms-ContextualMenu-link'; - if (item.link !== null) { - a.setAttribute('href', item.link); - } - a.textContent = item.text; - li.appendChild(a); - _contextMenu.appendChild(li); - }); - }; - - /** - * creates the breadcrumbs - */ - var _addBreadcrumbItems = function(maxItems) { - _resetList(_breadcrumbList); - var i = _itemCollection.length - maxItems; - - if(i >= 0) { - for(i; i < _itemCollection.length; i++) { - var listItem = document.createElement('li'); - var item = _itemCollection[i]; - var a = document.createElement('a'); - var chevron = document.createElement('i'); - listItem.className = 'ms-Breadcrumb-listItem'; - a.className = 'ms-Breadcrumb-itemLink'; - if (item.link !== null) { - a.setAttribute('href', item.link); - } - if(!isNaN(item.tabIndex)) { - a.setAttribute('tabindex', item.tabIndex); - } - a.textContent = item.text; - chevron.className = 'ms-Breadcrumb-chevron ms-Icon ms-Icon--chevronRight'; - listItem.appendChild(a); - listItem.appendChild(chevron); - _breadcrumbList.appendChild(listItem); - } - } - }; - - /** - * resets a list by removing its children - */ - var _resetList = function(list) { - while (list.firstChild) { - list.removeChild(list.firstChild); - } - }; - - /** - * opens the overflow menu - */ - var _openOverflow = function(event) { - if(_overflowMenu.className.indexOf(' is-open') === -1) { - _overflowMenu.className += ' is-open'; - removeOutlinesOnClick(event); - // force focus rect onto overflow button - _overflowButton.focus(); - } - }; - - var _overflowKeyPress = function(event) { - if(event.keyCode === 13) { - _openOverflow(event); - } - }; - - /** - * closes the overflow menu - */ - var _closeOverflow = function(event) { - if(!event || event.target !== _overflowButton) { - _removeClass(_overflowMenu, ' is-open'); - } - }; - - /** - * utility that removes a class from an element - */ - var _removeClass = function (element, value) { - var index = element.className.indexOf(value); - if(index > -1) { - element.className = element.className.substring(0, index); - } - }; - - /** - * caches elements and values of the component - */ - var _cacheDOM = function(context) { - _breadcrumb = context.container; - _breadcrumbList = _breadcrumb.querySelector('.ms-Breadcrumb-list'); - _listItems = _breadcrumb.querySelectorAll('.ms-Breadcrumb-listItem'); - _contextMenu = _breadcrumb.querySelector('.ms-ContextualMenu'); - _overflowButton = _breadcrumb.querySelector('.ms-Breadcrumb-overflowButton'); - _overflowMenu = _breadcrumb.querySelector('.ms-Breadcrumb-overflowMenu'); - }; - - /** - * sets handlers for resize and button click events - */ - var _setListeners = function() { - window.addEventListener('resize', _onResize); - _overflowButton.addEventListener('click', _openOverflow, false); - _overflowButton.addEventListener('keypress', _overflowKeyPress, false); - document.addEventListener('click', _closeOverflow, false); - _breadcrumbList.addEventListener('click', removeOutlinesOnClick, false); - }; - - /** - * removes focus outlines so they don't linger after click - */ - var removeOutlinesOnClick = function(event) { - event.target.blur(); - }; - - /** - * initializes component - */ - var init = function() { - _cacheDOM(this); - _setListeners(); - _createItemCollection(); - _onResize(null); - }; - - return { - init: init - }; - -}()); diff --git a/src/components/Breadcrumb/Breadcrumb.ts b/src/components/Breadcrumb/Breadcrumb.ts new file mode 100644 index 00000000..b81516ba --- /dev/null +++ b/src/components/Breadcrumb/Breadcrumb.ts @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. + +/** + * @namespace fabric + */ +namespace fabric { + "use strict"; + + /** + * Breadcrumb component + * + * Shows the user"s current location in a hierarchy and provides a means of navigating upward. + * + */ + export class Breadcrumb { + // medium breakpoint + private static MEDIUM: number = 639; + + public container: HTMLElement; + + // cached DOM elements + private _breadcrumb: HTMLElement; + private _listItems: NodeListOf; + private _contextMenu: HTMLElement; + private _overflowButton: HTMLElement; + private _overflowMenu: HTMLElement; + private _breadcrumbList: HTMLElement; + + private _currentMaxItems: number = 0; + private _itemCollection: Array = []; + + /** + * + * @param {HTMLElement} container - the target container for an instance of Breadcrumb + * @constructor + * + * If dynamically populating a list run the constructor after the list has been populated + * in the DOM. + */ + constructor(container: HTMLElement) { + this.container = container; + this.init(); + } + + /** + * removes focus outlines so they don"t linger after click + */ + public removeOutlinesOnClick(): void { + this._breadcrumbList.blur(); + } + + /** + * initializes component + */ + public init(): void { + this._cacheDOM(); + this._setListeners(); + this._createItemCollection(); + this._onResize(); + } + + /** + * create internal model of list items from DOM + */ + private _createItemCollection(): void { + let length = this._listItems.length; + let i = 0; + let item; + let text; + let link; + let tabIndex; + + for (i; i < length; i++) { + item = this._listItems[i].querySelector(".ms-Breadcrumb-itemLink"); + text = item.textContent; + link = item.getAttribute("href"); + tabIndex = parseInt(item.getAttribute("tabindex"), 10); + this._itemCollection.push({link: link, tabIndex: tabIndex, text: text}); + } + } + + /** + * Re-render lists on resize + * + */ + private _onResize(): void { + this._closeOverflow(null); + this._renderList(); + } + + /** + * render breadcrumbs and overflow menus + */ + private _renderList(): void { + let maxItems = window.innerWidth > Breadcrumb.MEDIUM ? 4 : 2; + + if (maxItems !== this._currentMaxItems) { + if (this._itemCollection.length > maxItems) { + this._breadcrumb.className += " is-overflow"; + } else { + this._removeClass(this._breadcrumb, " is-overflow"); + } + this._addBreadcrumbItems(maxItems); + this._addItemsToOverflow(maxItems); + } + + this._currentMaxItems = maxItems; + } + + /** + * creates the overflow menu + */ + private _addItemsToOverflow(maxItems: number): void { + this._resetList(this._contextMenu); + let end = this._itemCollection.length - maxItems; + let overflowItems = this._itemCollection.slice(0, end); + + overflowItems.forEach( (item) => { + let li = document.createElement("li"); + li.className = "ms-ContextualMenu-item"; + if (!isNaN(item.tabIndex)) { + li.setAttribute("tabindex", item.tabIndex); + } + let a = document.createElement("a"); + a.className = "ms-ContextualMenu-link"; + if (item.link !== null) { + a.setAttribute("href", item.link); + } + a.textContent = item.text; + li.appendChild(a); + this._contextMenu.appendChild(li); + }); + } + + /** + * creates the breadcrumbs + */ + private _addBreadcrumbItems(maxItems: number): void { + this._resetList(this._breadcrumbList); + let i = this._itemCollection.length - maxItems; + + i = i < 0 ? 0 : i; + if (i >= 0) { + for (i; i < this._itemCollection.length; i++) { + let listItem = document.createElement("li"); + let item = this._itemCollection[i]; + let a = document.createElement("a"); + let chevron = document.createElement("i"); + listItem.className = "ms-Breadcrumb-listItem"; + a.className = "ms-Breadcrumb-itemLink"; + if (item.link !== null) { + a.setAttribute("href", item.link); + } + if (!isNaN(item.tabIndex)) { + a.setAttribute("tabindex", item.tabIndex); + } + a.textContent = item.text; + chevron.className = "ms-Breadcrumb-chevron ms-Icon ms-Icon--chevronRight"; + listItem.appendChild(a); + listItem.appendChild(chevron); + this._breadcrumbList.appendChild(listItem); + } + } + } + + /** + * resets a list by removing its children + */ + private _resetList(list: HTMLElement): void { + while (list.firstChild) { + list.removeChild(list.firstChild); + } + } + + /** + * opens the overflow menu + */ + private _openOverflow(event: KeyboardEvent): void { + if (this._overflowMenu.className.indexOf(" is-open") === -1) { + this._overflowMenu.className += " is-open"; + this.removeOutlinesOnClick(); + // force focus rect onto overflow button + this._overflowButton.focus(); + } + } + + private _overflowKeyPress(event: KeyboardEvent): void { + if (event.keyCode === 13) { + this._openOverflow(event); + } + } + + /** + * closes the overflow menu + */ + private _closeOverflow(event: Event): void { + if (!event || event.target !== this._overflowButton) { + this._removeClass(this._overflowMenu, " is-open"); + } + } + + /** + * utility that removes a class from an element + */ + private _removeClass(element: HTMLElement, value: string): void { + let index = element.className.indexOf(value); + if (index > -1) { + element.className = element.className.substring(0, index); + } + } + + /** + * caches elements and values of the component + */ + private _cacheDOM(): void { + this._breadcrumb = this.container; + this._breadcrumbList = this._breadcrumb.querySelector(".ms-Breadcrumb-list"); + this._listItems = >this._breadcrumb.querySelectorAll(".ms-Breadcrumb-listItem"); + this._contextMenu = this._breadcrumb.querySelector(".ms-ContextualMenu"); + this._overflowButton = this._breadcrumb.querySelector(".ms-Breadcrumb-overflowButton"); + this._overflowMenu = this._breadcrumb.querySelector(".ms-Breadcrumb-overflowMenu"); + } + + /** + * sets handlers for resize and button click events + */ + private _setListeners(): void { + window.addEventListener("resize", this._onResize.bind(this)); + this._overflowButton.addEventListener("click", this._openOverflow.bind(this), false); + this._overflowButton.addEventListener("keypress", this._overflowKeyPress.bind(this), false); + document.addEventListener("click", this._closeOverflow.bind(this), false); + this._breadcrumbList.addEventListener("click", this.removeOutlinesOnClick, false); + } + } +} // end fabric namespace diff --git a/src/components/CommandBar/Jquery.CommandBar.js b/src/components/CommandBar/Jquery.CommandBar.js deleted file mode 100644 index 5a63d928..00000000 --- a/src/components/CommandBar/Jquery.CommandBar.js +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Command Bar Plugin - */ - -(function ($) { - $.fn.CommandBar = function () { - - var createMenuItem = function(text) { - var item = '
  • '; - item += text; - item += '
  • '; - - return item; - }; - - var saveCommands = function($commands, $commandWidth, $commandarea) { - var commands = []; - $commands.each(function() { - var $Item = $(this); - var $rightOffset = ($Item.position().left + $Item.outerWidth() + $commandWidth + 10) - $commandarea.position().left; // Added padding of 10 - commands.push({ jquery: $Item, rightOffset: $rightOffset}); - }); - - return commands; - }; - - var processCommands = function(commands, width, overflowwidth) { - var overFlowCommands = []; - - for(var i=0; i < commands.length; i++) { - var $Item = commands[i].jquery; - var rightOffset = commands[i].rightOffset; - - // If the command is outside the right boundaries add to overflow items - if(!$Item.hasClass('ms-CommandBarItem-overflow')) { - if((rightOffset + overflowwidth) > width) { - overFlowCommands.push($Item); - } else { - // Make sure item is displayed - $Item.removeClass('is-hidden'); - } - } - } - return overFlowCommands; - }; - - var processOverflow = function(overFlowCommands, $oCommand, $menu) { - var overflowStrings = ''; - - if(overFlowCommands.length > 0) { - $oCommand.addClass("is-visible"); - // Empty menu - $menu.html(''); - - // Add overflowed commands to ContextualMenu - for(var i = 0; i < overFlowCommands.length; i++) { - var $Item = $(overFlowCommands[i]); - // Hide Element in CommandBar - $Item.addClass('is-hidden'); - var commandBarItemText = $Item.find('.ms-CommandBarItem-commandText').text(); - overflowStrings += createMenuItem(commandBarItemText); - } - $menu.html(overflowStrings); - } else { - $oCommand.removeClass("is-visible"); - } - }; - - /** Go through each CommandBar we've been given. */ - return this.each(function () { - var $CommandBar = $(this); - var $CommandMainArea = $CommandBar.find('.ms-CommandBar-mainArea'); - var $CommandBarItems = $CommandMainArea.find('.ms-CommandBarItem').not('.ms-CommandBarItem-overflow'); - var $OverflowCommand = $CommandBar.find('.ms-CommandBarItem-overflow'); - var $OverflowCommandWidth = $CommandBar.find('.ms-CommandBarItem-overflow').outerWidth(); - var $OverflowMenu = $CommandBar.find('.ms-CommandBar-overflowMenu'); - var $SearchBox = $CommandBar.find('.ms-CommandBarSearch'); - var mobileSwitch = false; - var overFlowCommands; - var allCommands; - - // Go through process and save commands - allCommands = saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); - - // Initiate process commands and add commands to overflow on load - overFlowCommands = processCommands(allCommands, $CommandMainArea.innerWidth(), $OverflowCommandWidth); - processOverflow(overFlowCommands, $OverflowCommand, $OverflowMenu); - - // Set Search Behavior - if($(window).width() < 640) { - - $('.ms-CommandBarSearch-iconSearchWrapper').click(function() { - $(this).closest('.ms-CommandBarSearch').addClass('is-active'); - }); - - } - - // Add resize event handler on commandBar - $(window).resize(function() { - var overFlowCommands; - - if($(window).width() < 640 && mobileSwitch === false) { - // Go through process and save commands - allCommands = saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); - - mobileSwitch = true; - - // Search Behavior - $('.ms-CommandBarSearch-iconSearchWrapper').unbind(); - $('.ms-CommandBarSearch-iconSearchWrapper').click(function() { - $(this).closest('.ms-CommandBarSearch').addClass('is-active'); - }); - - } else if($(window).width() > 639 && mobileSwitch === true) { - // Go through process and save commands - allCommands = saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); - - mobileSwitch = false; - $('.ms-CommandBarSearch').unbind(); - - } - - // Initiate process commands and add commands to overflow on load - overFlowCommands = processCommands(allCommands, $CommandMainArea.innerWidth(), $OverflowCommandWidth); - processOverflow(overFlowCommands, $OverflowCommand, $OverflowMenu); - - }); - - // Hook up contextual menu - $OverflowCommand.click(function() { - $OverflowMenu.toggleClass('is-open'); - }); - - $OverflowCommand.focusout(function() { - $OverflowMenu.removeClass('is-open'); - }); - - $SearchBox.find('.ms-CommandBarSearch-input').click(function() { - $(this).closest('.ms-CommandBarSearch').addClass('is-active'); - }); - - $SearchBox.find('.ms-CommandBarSearch-input').on('focus', function() { - $(this).closest('.ms-CommandBarSearch').addClass('is-active'); - }); - - // When clicking the x clear the SearchBox and put state back to normal - $SearchBox.find('.ms-CommandBarSearch-iconClearWrapper').click(function() { - var $input = $(this).parent().find('.ms-CommandBarSearch-input'); - $input.val(''); - $input.parent().removeClass('is-active'); - }); - - $SearchBox.parent().find('.ms-CommandBarSearch-input').blur(function() { - var $input = $(this); - $input.val(''); - $input.parent().removeClass('is-active'); - }); - - }); - }; -})(jQuery); \ No newline at end of file diff --git a/src/components/CommandBar/Jquery.CommandBar.ts b/src/components/CommandBar/Jquery.CommandBar.ts new file mode 100644 index 00000000..c06b502c --- /dev/null +++ b/src/components/CommandBar/Jquery.CommandBar.ts @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. + +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +module fabric { + + class CommandItem { + public jquery: JQuery; + public rightOffset: number; + + constructor(jquery, rightOffset) { + this.jquery = jquery; + this.rightOffset = rightOffset; + } + } + + /** + * Command Bar Plugin + */ + export class CommandBar { + + /** + * + * @param {HTMLElement} container - the target container for an instance of CommandBar + * @constructor + */ + constructor(container: HTMLElement) { + let $CommandBar = $(container); + let $CommandMainArea = $CommandBar.find(".ms-CommandBar-mainArea"); + let $CommandBarItems = $CommandMainArea.find(".ms-CommandBarItem").not(".ms-CommandBarItem-overflow"); + let $OverflowCommand = $CommandBar.find(".ms-CommandBarItem-overflow"); + let $OverflowCommandWidth = $CommandBar.find(".ms-CommandBarItem-overflow").outerWidth(); + let $OverflowMenu = $CommandBar.find(".ms-CommandBar-overflowMenu"); + let $SearchBox = $CommandBar.find(".ms-CommandBarSearch"); + let mobileSwitch = false; + let overFlowCommands; + let allCommands; + + // Go through process and save commands + allCommands = this._saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); + + // Initiate process commands and add commands to overflow on load + overFlowCommands = this._processCommands(allCommands, $CommandMainArea.innerWidth(), $OverflowCommandWidth); + this._processOverflow(overFlowCommands, $OverflowCommand, $OverflowMenu); + + // Set Search Behavior + if ($(window).width() < 640) { + + $(".ms-CommandBarSearch-iconSearchWrapper").click(function() { + $(this).closest(".ms-CommandBarSearch").addClass("is-active"); + }); + + } + + // Add resize event handler on commandBar + $(window).resize(() => { + if ($(window).width() < 640 && mobileSwitch === false) { + // Go through process and save commands + allCommands = this._saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); + + mobileSwitch = true; + + // Search Behavior + $(".ms-CommandBarSearch-iconSearchWrapper").unbind(); + $(".ms-CommandBarSearch-iconSearchWrapper").click(function() { + $(this).closest(".ms-CommandBarSearch").addClass("is-active"); + }); + + } else if ($(window).width() > 639 && mobileSwitch === true) { + // Go through process and save commands + allCommands = this._saveCommands($CommandBarItems, $OverflowCommandWidth, $CommandMainArea); + + mobileSwitch = false; + $(".ms-CommandBarSearch").unbind(); + + } + + // Initiate process commands and add commands to overflow on load + let newOverFlowCommands = this._processCommands(allCommands, $CommandMainArea.innerWidth(), $OverflowCommandWidth); + this._processOverflow(newOverFlowCommands, $OverflowCommand, $OverflowMenu); + }); + + // Hook up contextual menu + $OverflowCommand.click(function(): void { + $OverflowMenu.toggleClass("is-open"); + }); + + $OverflowCommand.focusout(function(): void { + $OverflowMenu.removeClass("is-open"); + }); + + $SearchBox.find(".ms-CommandBarSearch-input").click(function(): void { + $(this).closest(".ms-CommandBarSearch").addClass("is-active"); + }); + + $SearchBox.find(".ms-CommandBarSearch-input").on("focus", function(): void { + $(this).closest(".ms-CommandBarSearch").addClass("is-active"); + }); + + // When clicking the x clear the SearchBox and put state back to normal + $SearchBox.find(".ms-CommandBarSearch-iconClearWrapper").click(function(): void { + let $input = $(this).parent().find(".ms-CommandBarSearch-input"); + $input.val(""); + $input.parent().removeClass("is-active"); + }); + + $SearchBox.parent().find(".ms-CommandBarSearch-input").blur(function(): void { + let $input = $(this); + $input.val(""); + $input.parent().removeClass("is-active"); + }); + } + + private _createMenuItem(text: string): string { + let item = "
  • "; + item += text; + item += "
  • "; + return item; + } + + private _saveCommands($commands: JQuery, $commandWidth: number, $commandarea: JQuery): Array { + let commands = []; + $commands.each(function() { + let $Item = $(this); + // Added padding of 10 + let $rightOffset = ($Item.position().left + $Item.outerWidth() + $commandWidth + 10) - $commandarea.position().left; + commands.push( new CommandItem( $Item, $rightOffset) ); + }); + + return commands; + } + + private _processCommands(commands: Array, width: number, overflowwidth: number): Array { + let overFlowCommands = []; + + for (let i = 0; i < commands.length; i++) { + let $Item = commands[i].jquery; + let rightOffset = commands[i].rightOffset; + + // If the command is outside the right boundaries add to overflow items + if (!$Item.hasClass("ms-CommandBarItem-overflow")) { + if ((rightOffset + overflowwidth) > width) { + overFlowCommands.push($Item); + } else { + // Make sure item is displayed + $Item.removeClass("is-hidden"); + } + } + } + return overFlowCommands; + } + + private _processOverflow(overFlowCommands: Array, $oCommand: JQuery, $menu: JQuery): void { + let overflowStrings = ""; + + if (overFlowCommands.length > 0) { + $oCommand.addClass("is-visible"); + // Empty menu + $menu.html(""); + + // Add overflowed commands to ContextualMenu + for (let i = 0; i < overFlowCommands.length; i++) { + let $Item = $(overFlowCommands[i]); + // Hide Element in CommandBar + $Item.addClass("is-hidden"); + let commandBarItemText = $Item.find(".ms-CommandBarItem-commandText").text(); + overflowStrings += this._createMenuItem(commandBarItemText); + } + $menu.html(overflowStrings); + } else { + $oCommand.removeClass("is-visible"); + } + } + } // end CommandBar +} // end fabric namespace diff --git a/src/components/ContextualMenu/Jquery.ContextualMenu.js b/src/components/ContextualMenu/Jquery.ContextualMenu.js deleted file mode 100644 index 2a27de24..00000000 --- a/src/components/ContextualMenu/Jquery.ContextualMenu.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Contextual Menu Plugin - */ -(function ($) { - $.fn.ContextualMenu = function () { - - /** Go through each nav bar we've been given. */ - return this.each(function () { - - var $contextualMenu = $(this); - - - // Set selected states. - $contextualMenu.on('click', '.ms-ContextualMenu-link:not(.is-disabled)', function(event) { - event.preventDefault(); - - // Check if multiselect - set selected states - if ( $contextualMenu.hasClass('ms-ContextualMenu--multiselect') ) { - - // If already selected, remove selection; if not, add selection - if ( $(this).hasClass('is-selected') ) { - $(this).removeClass('is-selected'); - } - else { - $(this).addClass('is-selected'); - } - - } - // All other contextual menu variants - else { - - // Deselect all of the items and close any menus. - $('.ms-ContextualMenu-link') - .removeClass('is-selected') - .siblings('.ms-ContextualMenu') - .removeClass('is-open'); - - // Select this item. - $(this).addClass('is-selected'); - - // If this item has a menu, open it. - if ($(this).hasClass('ms-ContextualMenu-link--hasMenu')) { - $(this).siblings('.ms-ContextualMenu:first').addClass('is-open'); - - // Open the menu without bubbling up the click event, - // which can cause the menu to close. - event.stopPropagation(); - } - - } - - - }); - - }); - }; -})(jQuery); diff --git a/src/components/ContextualMenu/Jquery.ContextualMenu.ts b/src/components/ContextualMenu/Jquery.ContextualMenu.ts new file mode 100644 index 00000000..0a424afe --- /dev/null +++ b/src/components/ContextualMenu/Jquery.ContextualMenu.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + + /** + * Contextual Menu Plugin + */ + export class ContextualMenu { + + /** + * + * @param {HTMLDivElement} container - the target container for an instance of ContextualMenu + * @constructor + */ + constructor(container: HTMLElement) { + let $contextualMenu = $(container); + // Set selected states. + $contextualMenu.on("click", ".ms-ContextualMenu-link:not(.is-disabled)", function(event) { + event.preventDefault(); + + // Check if multiselect - set selected states + if ( $contextualMenu.hasClass("ms-ContextualMenu--multiselect") ) { + + // If already selected, remove selection; if not, add selection + if ( $(this).hasClass("is-selected") ) { + $(this).removeClass("is-selected"); + } else { + $(this).addClass("is-selected"); + } + } else { // All other contextual menu variants + // Deselect all of the items and close any menus. + $(".ms-ContextualMenu-link") + .removeClass("is-selected") + .siblings(".ms-ContextualMenu") + .removeClass("is-open"); + + // Select this item. + $(this).addClass("is-selected"); + + // If this item has a menu, open it. + if ($(this).hasClass("ms-ContextualMenu-link--hasMenu")) { + $(this).siblings(".ms-ContextualMenu:first").addClass("is-open"); + + // Open the menu without bubbling up the click event, + // which can cause the menu to close. + event.stopPropagation(); + } + + } + }); + } + } +} diff --git a/src/components/DatePicker/Jquery.DatePicker.js b/src/components/DatePicker/Jquery.DatePicker.js deleted file mode 100644 index 74b4b674..00000000 --- a/src/components/DatePicker/Jquery.DatePicker.js +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -(function ($) { - - /** - * DatePicker Plugin - */ - - $.fn.DatePicker = function (options) { - - return this.each(function () { - - /** Set up variables and run the Pickadate plugin. */ - var $datePicker = $(this); - var $dateField = $datePicker.find('.ms-TextField-field').pickadate($.extend({ - // Strings and translations. - weekdaysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], - - // Don't render the buttons - today: '', - clear: '', - close: '', - - // Events - onStart: function() { - initCustomView($datePicker); - }, - - // Classes - klass: { - - // The element states - input: 'ms-DatePicker-input', - active: 'ms-DatePicker-input--active', - - // The root picker and states - picker: 'ms-DatePicker-picker', - opened: 'ms-DatePicker-picker--opened', - focused: 'ms-DatePicker-picker--focused', - - // The picker holder - holder: 'ms-DatePicker-holder', - - // The picker frame, wrapper, and box - frame: 'ms-DatePicker-frame', - wrap: 'ms-DatePicker-wrap', - box: 'ms-DatePicker-dayPicker', - - // The picker header - header: 'ms-DatePicker-header', - - // Month & year labels - month: 'ms-DatePicker-month', - year: 'ms-DatePicker-year', - - // Table of dates - table: 'ms-DatePicker-table', - - // Weekday labels - weekdays: 'ms-DatePicker-weekday', - - // Day states - day: 'ms-DatePicker-day', - disabled: 'ms-DatePicker-day--disabled', - selected: 'ms-DatePicker-day--selected', - highlighted: 'ms-DatePicker-day--highlighted', - now: 'ms-DatePicker-day--today', - infocus: 'ms-DatePicker-day--infocus', - outfocus: 'ms-DatePicker-day--outfocus', - - } - },options||{})); - var $picker = $dateField.pickadate('picker'); - - /** Respond to built-in picker events. */ - $picker.on({ - render: function() { - updateCustomView($datePicker); - }, - open: function() { - scrollUp($datePicker); - } - }); - - }); - }; - - /** - * After the Pickadate plugin starts, this function - * adds additional controls to the picker view. - */ - function initCustomView($datePicker) { - - /** Get some variables ready. */ - var $monthControls = $datePicker.find('.ms-DatePicker-monthComponents'); - var $goToday = $datePicker.find('.ms-DatePicker-goToday'); - var $monthPicker = $datePicker.find('.ms-DatePicker-monthPicker'); - var $yearPicker = $datePicker.find('.ms-DatePicker-yearPicker'); - var $pickerWrapper = $datePicker.find('.ms-DatePicker-wrap'); - var $picker = $datePicker.find('.ms-TextField-field').pickadate('picker'); - - /** Move the month picker into position. */ - $monthControls.appendTo($pickerWrapper); - $goToday.appendTo($pickerWrapper); - $monthPicker.appendTo($pickerWrapper); - $yearPicker.appendTo($pickerWrapper); - - /** Update the custom view. */ - updateCustomView($datePicker); - - /** Move back one month. */ - $monthControls.on('click', '.js-prevMonth', function(event) { - event.preventDefault(); - var newMonth = $picker.get('highlight').month - 1; - changeHighlightedDate($picker, null, newMonth, null); - }); - - /** Move ahead one month. */ - $monthControls.on('click', '.js-nextMonth', function(event) { - event.preventDefault(); - var newMonth = $picker.get('highlight').month + 1; - changeHighlightedDate($picker, null, newMonth, null); - }); - - /** Move back one year. */ - $monthPicker.on('click', '.js-prevYear', function(event) { - event.preventDefault(); - var newYear = $picker.get('highlight').year - 1; - changeHighlightedDate($picker, newYear, null, null); - }); - - /** Move ahead one year. */ - $monthPicker.on('click', '.js-nextYear', function(event) { - event.preventDefault(); - var newYear = $picker.get('highlight').year + 1; - changeHighlightedDate($picker, newYear, null, null); - }); - - /** Move back one decade. */ - $yearPicker.on('click', '.js-prevDecade', function(event) { - event.preventDefault(); - var newYear = $picker.get('highlight').year - 10; - changeHighlightedDate($picker, newYear, null, null); - }); - - /** Move ahead one decade. */ - $yearPicker.on('click', '.js-nextDecade', function(event) { - event.preventDefault(); - var newYear = $picker.get('highlight').year + 10; - changeHighlightedDate($picker, newYear, null, null); - }); - - /** Go to the current date, shown in the day picking view. */ - $goToday.click(function(event) { - event.preventDefault(); - - /** Select the current date, while keeping the picker open. */ - var now = new Date(); - $picker.set('select', [now.getFullYear(), now.getMonth(), now.getDate()]); - - /** Switch to the default (calendar) view. */ - $datePicker.removeClass('is-pickingMonths').removeClass('is-pickingYears'); - - }); - - /** Change the highlighted month. */ - $monthPicker.on('click', '.js-changeDate', function(event) { - event.preventDefault(); - - /** Get the requested date from the data attributes. */ - var newYear = $(this).attr('data-year'); - var newMonth = $(this).attr('data-month'); - var newDay = $(this).attr('data-day'); - - /** Update the date. */ - changeHighlightedDate($picker, newYear, newMonth, newDay); - - /** If we've been in the "picking months" state on mobile, remove that state so we show the calendar again. */ - if ($datePicker.hasClass('is-pickingMonths')) { - $datePicker.removeClass('is-pickingMonths'); - } - }); - - /** Change the highlighted year. */ - $yearPicker.on('click', '.js-changeDate', function(event) { - event.preventDefault(); - - /** Get the requested date from the data attributes. */ - var newYear = $(this).attr('data-year'); - var newMonth = $(this).attr('data-month'); - var newDay = $(this).attr('data-day'); - - /** Update the date. */ - changeHighlightedDate($picker, newYear, newMonth, newDay); - - /** If we've been in the "picking years" state on mobile, remove that state so we show the calendar again. */ - if ($datePicker.hasClass('is-pickingYears')) { - $datePicker.removeClass('is-pickingYears'); - } - }); - - /** Switch to the default state. */ - $monthPicker.on('click', '.js-showDayPicker', function() { - $datePicker.removeClass('is-pickingMonths'); - $datePicker.removeClass('is-pickingYears'); - }); - - /** Switch to the is-pickingMonths state. */ - $monthControls.on('click', '.js-showMonthPicker', function() { - $datePicker.toggleClass('is-pickingMonths'); - }); - - /** Switch to the is-pickingYears state. */ - $monthPicker.on('click', '.js-showYearPicker', function() { - $datePicker.toggleClass('is-pickingYears'); - }); - - } - - /** Change the highlighted date. */ - function changeHighlightedDate($picker, newYear, newMonth, newDay) { - - /** All letiables are optional. If not provided, default to the current value. */ - if (typeof newYear === "undefined" || newYear === null) { - newYear = $picker.get("highlight").year; - } - if (typeof newMonth === "undefined" || newMonth === null) { - newMonth = $picker.get("highlight").month; - } - if (typeof newDay === "undefined" || newDay === null) { - newDay = $picker.get("highlight").date; - } - - /** Update it. */ - $picker.set('highlight', [newYear, newMonth, newDay]); - - } - - - /** Whenever the picker renders, do our own rendering on the custom controls. */ - function updateCustomView($datePicker) { - - /** Get some variables ready. */ - var $monthPicker = $datePicker.find('.ms-DatePicker-monthPicker'); - var $yearPicker = $datePicker.find('.ms-DatePicker-yearPicker'); - var $picker = $datePicker.find('.ms-TextField-field').pickadate('picker'); - - /** Set the correct year. */ - $monthPicker.find('.ms-DatePicker-currentYear').text($picker.get('view').year); - - /** Highlight the current month. */ - $monthPicker.find('.ms-DatePicker-monthOption').removeClass('is-highlighted'); - $monthPicker.find('.ms-DatePicker-monthOption[data-month="' + $picker.get('highlight').month + '"]').addClass('is-highlighted'); - - /** Generate the grid of years for the year picker view. */ - - // Start by removing any existing generated output. */ - $yearPicker.find('.ms-DatePicker-currentDecade').remove(); - $yearPicker.find('.ms-DatePicker-optionGrid').remove(); - - // Generate the output by going through the years. - var startingYear = $picker.get('highlight').year - 11; - var decadeText = startingYear + " - " + (startingYear + 11); - var output = '
    ' + decadeText + '
    '; - output += '
    '; - for (var year = startingYear; year < (startingYear + 12); year++) { - output += '' + year +''; - } - output += '
    '; - - // Output the title and grid of years generated above. - $yearPicker.append(output); - - /** Highlight the current year. */ - $yearPicker.find('.ms-DatePicker-yearOption').removeClass('is-highlighted'); - $yearPicker.find('.ms-DatePicker-yearOption[data-year="' + $picker.get('highlight').year + '"]').addClass('is-highlighted'); - } - - /** Scroll the page up so that the field the date picker is attached to is at the top. */ - function scrollUp($datePicker) { - $('html, body').animate({ - scrollTop: $datePicker.offset().top - }, 367); - } - -})(jQuery); diff --git a/src/components/DatePicker/Jquery.DatePicker.ts b/src/components/DatePicker/Jquery.DatePicker.ts new file mode 100644 index 00000000..c2265d6e --- /dev/null +++ b/src/components/DatePicker/Jquery.DatePicker.ts @@ -0,0 +1,286 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// +/// + +namespace fabric { + /** + * DatePicker Plugin + */ + export class DatePicker { + + constructor(container, options) { + /** Set up letiables and run the Pickadate plugin. */ + let $datePicker = $(container); + let $dateField: any = $datePicker.find(".ms-TextField-field").pickadate($.extend({ + // Strings and translations. + weekdaysShort: ["S", "M", "T", "W", "T", "F", "S"], + + // Don't render the buttons + clear: "", + close: "", + today: "", + + // Events + onStart: () => { + this.initCustomView($datePicker); + }, + + // Classes + klass: { + + // The element states + input: "ms-DatePicker-input", + active: "ms-DatePicker-input--active", + + // The root picker and states + picker: "ms-DatePicker-picker", + opened: "ms-DatePicker-picker--opened", + focused: "ms-DatePicker-picker--focused", + + // The picker holder + holder: "ms-DatePicker-holder", + + // The picker frame, wrapper, and box + frame: "ms-DatePicker-frame", + wrap: "ms-DatePicker-wrap", + box: "ms-DatePicker-dayPicker", + + // The picker header + header: "ms-DatePicker-header", + + // Month & year labels + month: "ms-DatePicker-month", + year: "ms-DatePicker-year", + + // Table of dates + table: "ms-DatePicker-table", + + // Weekday labels + weekdays: "ms-DatePicker-weekday", + + // Day states + day: "ms-DatePicker-day", + disabled: "ms-DatePicker-day--disabled", + selected: "ms-DatePicker-day--selected", + highlighted: "ms-DatePicker-day--highlighted", + now: "ms-DatePicker-day--today", + infocus: "ms-DatePicker-day--infocus", + outfocus: "ms-DatePicker-day--outfocus", + }, + }, options || {})); + let $picker = $dateField.pickadate("picker"); + + /** Respond to built-in picker events. */ + $picker.on({ + render: () => { + this.updateCustomView($datePicker); + }, + open: () => { + this.scrollUp($datePicker); + }, + }); + } + + /** + * After the Pickadate plugin starts, this function + * adds additional controls to the picker view. + */ + public initCustomView($datePicker) { + + /** Get some letiables ready. */ + let $monthControls = $datePicker.find(".ms-DatePicker-monthComponents"); + let $goToday = $datePicker.find(".ms-DatePicker-goToday"); + let $monthPicker = $datePicker.find(".ms-DatePicker-monthPicker"); + let $yearPicker = $datePicker.find(".ms-DatePicker-yearPicker"); + let $pickerWrapper = $datePicker.find(".ms-DatePicker-wrap"); + let $picker = $datePicker.find(".ms-TextField-field").pickadate("picker"); + + /** Move the month picker into position. */ + $monthControls.appendTo($pickerWrapper); + $goToday.appendTo($pickerWrapper); + $monthPicker.appendTo($pickerWrapper); + $yearPicker.appendTo($pickerWrapper); + + /** Update the custom view. */ + this.updateCustomView($datePicker); + + /** Move back one month. */ + $monthControls.on("click", ".js-prevMonth", (event) => { + event.preventDefault(); + let newMonth = $picker.get("highlight").month - 1; + this.changeHighlightedDate($picker, null, newMonth, null); + }); + + /** Move ahead one month. */ + $monthControls.on("click", ".js-nextMonth", (event) => { + event.preventDefault(); + let newMonth = $picker.get("highlight").month + 1; + this.changeHighlightedDate($picker, null, newMonth, null); + }); + + /** Move back one year. */ + $monthPicker.on("click", ".js-prevYear", (event) => { + event.preventDefault(); + let newYear = $picker.get("highlight").year - 1; + this.changeHighlightedDate($picker, newYear, null, null); + }); + + /** Move ahead one year. */ + $monthPicker.on("click", ".js-nextYear", (event) => { + event.preventDefault(); + let newYear = $picker.get("highlight").year + 1; + this.changeHighlightedDate($picker, newYear, null, null); + }); + + /** Move back one decade. */ + $yearPicker.on("click", ".js-prevDecade", (event) => { + event.preventDefault(); + let newYear = $picker.get("highlight").year - 10; + this.changeHighlightedDate($picker, newYear, null, null); + }); + + /** Move ahead one decade. */ + $yearPicker.on("click", ".js-nextDecade", (event) => { + event.preventDefault(); + let newYear = $picker.get("highlight").year + 10; + this.changeHighlightedDate($picker, newYear, null, null); + }); + + /** Go to the current date, shown in the day picking view. */ + $goToday.click((event) => { + event.preventDefault(); + + /** Select the current date, while keeping the picker open. */ + let now = new Date(); + $picker.set("select", [now.getFullYear(), now.getMonth(), now.getDate()]); + + /** Switch to the default (calendar) view. */ + $datePicker.removeClass("is-pickingMonths").removeClass("is-pickingYears"); + + }); + + /** Change the highlighted month. */ + $monthPicker.on("click", ".js-changeDate", (event) => { + event.preventDefault(); + + let $changeDate = $(event.toElement); + + /** Get the requested date from the data attributes. */ + let newYear = $changeDate.attr("data-year"); + let newMonth = $changeDate.attr("data-month"); + let newDay = $changeDate.attr("data-day"); + + /** Update the date. */ + this.changeHighlightedDate($picker, newYear, newMonth, newDay); + + /** If we"ve been in the "picking months" state on mobile, remove that state so we show the calendar again. */ + if ($datePicker.hasClass("is-pickingMonths")) { + $datePicker.removeClass("is-pickingMonths"); + } + }); + + /** Change the highlighted year. */ + $yearPicker.on("click", ".js-changeDate", (event) => { + event.preventDefault(); + let $changeDate = $(event.toElement); + + /** Get the requested date from the data attributes. */ + let newYear = $changeDate.attr("data-year"); + let newMonth = $changeDate.attr("data-month"); + let newDay = $changeDate.attr("data-day"); + + /** Update the date. */ + this.changeHighlightedDate($picker, newYear, newMonth, newDay); + + /** If we"ve been in the "picking years" state on mobile, remove that state so we show the calendar again. */ + if ($datePicker.hasClass("is-pickingYears")) { + $datePicker.removeClass("is-pickingYears"); + } + }); + + /** Switch to the default state. */ + $monthPicker.on("click", ".js-showDayPicker", function() { + $datePicker.removeClass("is-pickingMonths"); + $datePicker.removeClass("is-pickingYears"); + }); + + /** Switch to the is-pickingMonths state. */ + $monthControls.on("click", ".js-showMonthPicker", function() { + $datePicker.toggleClass("is-pickingMonths"); + }); + + /** Switch to the is-pickingYears state. */ + $monthPicker.on("click", ".js-showYearPicker", function() { + $datePicker.toggleClass("is-pickingYears"); + }); + } + + /** Change the highlighted date. */ + public changeHighlightedDate($picker, newYear, newMonth, newDay) { + + /** All letiables are optional. If not provided, default to the current value. */ + if (typeof newYear === "undefined" || newYear === null) { + newYear = $picker.get("highlight").year; + } + if (typeof newMonth === "undefined" || newMonth === null) { + newMonth = $picker.get("highlight").month; + } + if (typeof newDay === "undefined" || newDay === null) { + newDay = $picker.get("highlight").date; + } + + /** Update it. */ + $picker.set("highlight", [newYear, newMonth, newDay]); + } + + + /** Whenever the picker renders, do our own rendering on the custom controls. */ + public updateCustomView($datePicker) { + + /** Get some letiables ready. */ + let $monthPicker = $datePicker.find(".ms-DatePicker-monthPicker"); + let $yearPicker = $datePicker.find(".ms-DatePicker-yearPicker"); + let $picker = $datePicker.find(".ms-TextField-field").pickadate("picker"); + + /** Set the correct year. */ + $monthPicker.find(".ms-DatePicker-currentYear").text($picker.get("view").year); + + /** Highlight the current month. */ + $monthPicker.find(".ms-DatePicker-monthOption").removeClass("is-highlighted"); + $monthPicker.find(".ms-DatePicker-monthOption[data-month='" + $picker.get("highlight").month + "']").addClass("is-highlighted"); + + /** Generate the grid of years for the year picker view. */ + + // Start by removing any existing generated output. */ + $yearPicker.find(".ms-DatePicker-currentDecade").remove(); + $yearPicker.find(".ms-DatePicker-optionGrid").remove(); + + // Generate the output by going through the years. + let startingYear = $picker.get("highlight").year - 11; + let decadeText = startingYear + " - " + (startingYear + 11); + let output = "
    " + decadeText + "
    "; + output += "
    "; + for (let year = startingYear; year < (startingYear + 12); year++) { + output += "" + year + ""; + } + output += "
    "; + + // Output the title and grid of years generated above. + $yearPicker.append(output); + + /** Highlight the current year. */ + $yearPicker.find(".ms-DatePicker-yearOption").removeClass("is-highlighted"); + $yearPicker.find(".ms-DatePicker-yearOption[data-year='" + $picker.get("highlight").year + "']").addClass("is-highlighted"); + } + + /** Scroll the page up so that the field the date picker is attached to is at the top. */ + public scrollUp($datePicker) { + $("html, body").animate({ + scrollTop: $datePicker.offset().top, + }, 367); + } + } +} diff --git a/src/components/Dialog/jquery.Dialog.js b/src/components/Dialog/jquery.Dialog.js deleted file mode 100644 index 0d647332..00000000 --- a/src/components/Dialog/jquery.Dialog.js +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Dialog Plugin - * - * Adds basic demonstration functionality to .ms-Dialog components. - * - * @param {jQuery Object} One or more .ms-Dialog components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.Dialog = function () { - - /** Iterate through the sample buttons, which can be used to open the Dialogs. */ - $(".js-DialogAction--open").each(function () { - /** Open the associated dialog on click. */ - $(this).on('click', function () { - var target = $(this).data("target"); - $(target).show(); - }); - }); - - - return this.each(function () { - var dialog = this; - - /** Have the dialogs hidden for their initial state */ - $(dialog).hide(); - - /** Have the close buttons close the Dialog. */ - $(dialog).find(".js-DialogAction--close").each(function() { - $(this).on('click', function () { - $(dialog).hide(); - }); - }); - - /** Have the action buttons close the Dialog, though you would usually do some specific action per button. */ - $(dialog).find(".ms-Dialog-action").on('click', function () { - $(dialog).hide(); - }); - - }); - }; -})(jQuery); diff --git a/src/components/Dialog/jquery.Dialog.ts b/src/components/Dialog/jquery.Dialog.ts new file mode 100644 index 00000000..d938d28e --- /dev/null +++ b/src/components/Dialog/jquery.Dialog.ts @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + /** + * Dialog Plugin + * + * Adds basic demonstration functionality to .ms-Dialog components. + */ + export class Dialog { + + /** + * + * @param {HTMLElement} container - the target container for an instance of Dialog + * @constructor + */ + constructor(container: HTMLElement) { + let dialog = container; + + /** Have the dialogs hidden for their initial state */ + $(dialog).hide(); + + /** Have the close buttons close the Dialog. */ + $(dialog).find(".js-DialogAction--close").each(function() { + $(this).on("click", function () { + $(dialog).hide(); + }); + }); + + /** Have the action buttons close the Dialog, though you would usually do some specific action per button. */ + $(dialog).find(".ms-Dialog-action").on("click", function () { + $(dialog).hide(); + }); + } + } // end Dialog +} // end Fabric namespace + + +/* presentation code for the samples - + configure any sample buttons so that they open the associated Dialog */ +$(document).ready(function() { + /** Iterate through the sample buttons, which can be used to open the Dialogs. */ + $(".js-DialogAction--open").each(function () { + /** Open the associated dialog on click. */ + $(this).on("click", function () { + let target = $(this).data("target"); + $(target).show(); + }); + }); +}); diff --git a/src/components/Dropdown/Jquery.Dropdown.js b/src/components/Dropdown/Jquery.Dropdown.js deleted file mode 100644 index 30b806c5..00000000 --- a/src/components/Dropdown/Jquery.Dropdown.js +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Dropdown Plugin - * - * Given .ms-Dropdown containers with generic elements inside, this plugin hides the original + * dropdown and creates a new "fake" dropdown that can more easily be styled across browsers. + * + */ + export class Dropdown { + + /** + * + * @param {HTMLElement} container - the target container for an instance of Dropdown + * @constructor + */ + constructor(container: HTMLElement) { + let $dropdownWrapper = $(container), + $originalDropdown = $dropdownWrapper.children(".ms-Dropdown-select"), + $originalDropdownOptions = $originalDropdown.children("option"), + newDropdownTitle = "", + newDropdownItems = "", + newDropdownSource = ""; + + /** Go through the options to fill up newDropdownTitle and newDropdownItems. */ + $originalDropdownOptions.each(function (index, option: any) { + + /** If the option is selected, it should be the new dropdown's title. */ + if (option.selected) { + newDropdownTitle = option.text; + } + + /** Add this option to the list of items. */ + newDropdownItems += "
  • "; + }); + + /** Insert the replacement dropdown. */ + newDropdownSource = "" + newDropdownTitle + ""; + newDropdownSource += "
      " + newDropdownItems + "
    "; + $dropdownWrapper.append(newDropdownSource); + + function _openDropdown(evt: Event): void { + if (!$dropdownWrapper.hasClass("is-disabled")) { + + /** First, let"s close any open dropdowns on this page. */ + $dropdownWrapper.find(".is-open").removeClass("is-open"); + + /** Stop the click event from propagating, which would just close the dropdown immediately. */ + evt.stopPropagation(); + + /** Before opening, size the items list to match the dropdown. */ + let dropdownWidth = $(this).parents(".ms-Dropdown").width(); + $(this).next(".ms-Dropdown-items").css("width", dropdownWidth + "px"); + + /** Go ahead and open that dropdown. */ + $dropdownWrapper.toggleClass("is-open"); + $(".ms-Dropdown").each(function(){ + if ($(this)[0] !== $dropdownWrapper[0]) { + $(this).removeClass("is-open"); + } + }); + + /** Temporarily bind an event to the document that will close this dropdown when clicking anywhere. */ + $(document).bind("click.dropdown", function() { + $dropdownWrapper.removeClass("is-open"); + $(document).unbind("click.dropdown"); + }); + } + } + + /** Toggle open/closed state of the dropdown when clicking its title. */ + $dropdownWrapper.on("click", ".ms-Dropdown-title", function(event) { + _openDropdown(event); + }); + + /** Keyboard accessibility */ + $dropdownWrapper.on("keyup", function(event) { + let keyCode = event.keyCode || event.which; + // Open dropdown on enter or arrow up or arrow down and focus on first option + if (!$(this).hasClass("is-open")) { + if (keyCode === 13 || keyCode === 38 || keyCode === 40) { + _openDropdown(event); + if (!$(this).find(".ms-Dropdown-item").hasClass("is-selected")) { + $(this).find(".ms-Dropdown-item:first").addClass("is-selected"); + } + } + } else if ($(this).hasClass("is-open")) { + // Up arrow focuses previous option + if (keyCode === 38) { + if ($(this).find(".ms-Dropdown-item.is-selected").prev().siblings().length > 0) { + $(this).find(".ms-Dropdown-item.is-selected").removeClass("is-selected").prev().addClass("is-selected"); + } + } + // Down arrow focuses next option + if (keyCode === 40) { + if ($(this).find(".ms-Dropdown-item.is-selected").next().siblings().length > 0) { + $(this).find(".ms-Dropdown-item.is-selected").removeClass("is-selected").next().addClass("is-selected"); + } + } + // Enter to select item + if (keyCode === 13) { + if (!$dropdownWrapper.hasClass("is-disabled")) { + + // Item text + let selectedItemText = $(this).find(".ms-Dropdown-item.is-selected").text(); + + $(this).find(".ms-Dropdown-title").html(selectedItemText); + + /** Update the original dropdown. */ + $originalDropdown.find("option").each(function(key, value: any) { + if (value.text === selectedItemText) { + $(this).prop("selected", true); + } else { + $(this).prop("selected", false); + } + }); + $originalDropdown.change(); + + $(this).removeClass("is-open"); + } + } + } + + // Close dropdown on esc + if (keyCode === 27) { + $(this).removeClass("is-open"); + } + }); + + /** Select an option from the dropdown. */ + $dropdownWrapper.on("click", ".ms-Dropdown-item", function () { + if (!$dropdownWrapper.hasClass("is-disabled") && !$(this).hasClass("is-disabled")) { + + /** Deselect all items and select this one. */ + $(this).siblings(".ms-Dropdown-item").removeClass("is-selected"); + $(this).addClass("is-selected"); + + /** Update the replacement dropdown's title. */ + $(this).parents().siblings(".ms-Dropdown-title").html($(this).text()); + + /** Update the original dropdown. */ + let selectedItemText = $(this).text(); + $originalDropdown.find("option").each(function(key, value: any) { + if (value.text === selectedItemText) { + $(this).prop("selected", true); + } else { + $(this).prop("selected", false); + } + }); + $originalDropdown.change(); + } + }); + } + } +} diff --git a/src/components/Facepile/Jquery.Facepile.js b/src/components/Facepile/Jquery.Facepile.js deleted file mode 100644 index 37690540..00000000 --- a/src/components/Facepile/Jquery.Facepile.js +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Facepile Plugin - * - * Adds basic demonstration functionality to .ms-Facepile components. - * - * @param {jQuery Object} One or more .ms-Facepile components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.Facepile = function () { - - /** Iterate through each Facepile provided. */ - return this.each(function () { - $('.ms-PeoplePicker').PeoplePicker(); - $('.ms-Panel').Panel(); - - var $Facepile = $(this); - var $membersList = $(".ms-Facepile-members"); - var $membersCount = $(".ms-Facepile-members > .ms-Facepile-itemBtn").length; - var $panel = $('.ms-Facepile-panel.ms-Panel'); - var $panelMain = $panel.find(".ms-Panel-main"); - var $picker = $('.ms-PeoplePicker.ms-PeoplePicker--Facepile'); - var $pickerMembers = $picker.find('.ms-PeoplePicker-selectedPeople'); - var $personaCard = $('.ms-Facepile').find('.ms-PersonaCard'); - - - /** Increment member count and show/hide overflow text */ - var incrementMembers = function() { - /** Increment person count by one */ - $membersCount += 1; - - /** Display a maxiumum of 5 people */ - $(".ms-Facepile-members").children(":gt(4)").hide(); - - /** Display counter after 5 people are present */ - if ($membersCount > 5) { - $(".ms-Facepile-itemBtn--overflow").addClass("is-active"); - - var remainingMembers = $membersCount - 5; - $(".ms-Facepile-overflowText").text("+" + remainingMembers); - } - }; - - /** Open panel with people picker */ - $Facepile.on("click", ".js-addPerson", function() { - $panelMain.css({display: "block"}); - $panel.toggleClass("is-open") - .addClass("ms-Panel-animateIn") - .removeClass('ms-Facepile-panel--overflow ms-Panel--right') - .addClass('ms-Facepile-panel--addPerson'); - - /** Close any open persona cards */ - $personaCard.removeClass('is-active').hide(); - }); - - $panel.on("click", ".js-togglePanel", function() { - $panel.toggleClass("is-open") - .addClass("ms-Panel-animateIn"); - }); - - /** Open oveflow panel with list of members */ - $Facepile.on("click", ".js-overflowPanel", function() { - $panelMain.css({display: "block"}); - $panel.toggleClass("is-open") - .addClass("ms-Panel-animateIn") - .removeClass('ms-Facepile-panel--addPerson') - .addClass('ms-Facepile-panel--overflow ms-Panel--right'); - }); - - /** Display person count on page load */ - $(document).ready(function() { - $(".ms-Facepile-overflowText").text("+" + $membersCount); - }); - - /** Show selected members from PeoplePicker in the Facepile */ - $('.ms-PeoplePicker-result').on('click', function() { - var $this = $(this); - var name = $this.find(".ms-Persona-primaryText").html(); - var title = $this.find(".ms-Persona-secondaryText").html(); - var selectedInitials = (function() { - var nameArray = name.split(' '); - var nameInitials = ''; - for (var i = 0; i < nameArray.length; i++) { - nameInitials += nameArray[i].charAt(0); - } - - return nameInitials.substring(0,2); - })(); - var selectedClasses = $this.find('.ms-Persona-initials').attr('class'); - var selectedImage = (function() { - if ($this.find('.ms-Persona-image').length) { - var selectedImageSrc = $this.find('.ms-Persona-image').attr('src'); - return 'Persona image'; - } else { - return ''; - } - })(); - - var FacepileItem = - ''; - - /** Add new item to members list in Facepile */ - $membersList.prepend(FacepileItem); - - /** Increment member count */ - incrementMembers(); - }); - - /** Remove members in panel people picker */ - $pickerMembers.on('click', '.js-selectedRemove', function() { - var memberText = $(this).parent().find('.ms-Persona-primaryText').text(); - - var $FacepileMember = $membersList.find(".ms-Persona-primaryText:contains(" + memberText + ")").first(); - - if ($FacepileMember) { - $FacepileMember.parent().closest('.ms-Facepile-itemBtn').remove(); - - $membersCount -= 1; - - /** Display a maxiumum of 5 people */ - $(".ms-Facepile-members").children(":lt(5)").show(); - - /** Display counter after 5 people are present */ - if ($membersCount <= 5) { - $(".ms-Facepile-itemBtn--overflow").removeClass("is-active"); - } else { - var remainingMembers = $membersCount - 5; - $(".ms-Facepile-overflowText").text("+" + remainingMembers); - } - } - }); - - /** Show persona card when selecting a Facepile item */ - $membersList.on('click', '.ms-Facepile-itemBtn', function() { - var selectedName = $(this).find(".ms-Persona-primaryText").html(); - var selectedTitle = $(this).find(".ms-Persona-secondaryText").html(); - var selectedInitials = (function() { - var name = selectedName.split(' '); - var nameInitials = ''; - for (var i = 0; i < name.length; i++) { - nameInitials += name[i].charAt(0); - } - - return nameInitials.substring(0,2); - })(); - var selectedClasses = $(this).find('.ms-Persona-initials').attr('class'); - var selectedImage = $(this).find('.ms-Persona-image').attr('src'); - var $card = $('.ms-PersonaCard'); - var $cardName = $card.find('.ms-Persona-primaryText'); - var $cardTitle = $card.find('.ms-Persona-secondaryText'); - var $cardInitials = $card.find('.ms-Persona-initials'); - var $cardImage = $card.find('.ms-Persona-image'); - - /** Close any open persona cards */ - $personaCard.removeClass('is-active'); - - /** Add data to persona card */ - $cardName.text(selectedName); - $cardTitle.text(selectedTitle); - $cardInitials.text(selectedInitials); - $cardInitials.removeClass(); - $cardInitials.addClass(selectedClasses); - $cardImage.attr('src', selectedImage); - - /** Show persona card */ - setTimeout(function() { $personaCard.addClass('is-active'); }, 100); - - /** Align persona card on md and above screens */ - if ($(window).width() > 480) { - var itemPosition = $(this).offset().left; - var correctedPosition = itemPosition - 26; - - $personaCard.css({'left': correctedPosition}); - } else { - $personaCard.css({'left': 0, 'top': 'auto', 'position': 'fixed'}); - } - }); - - /** Dismiss persona card when clicking on the document */ - $(document).on('click', function(e) { - var $memberBtn = $('.ms-Facepile-itemBtn--member'); - - if (!$memberBtn.is(e.target) && $memberBtn.has(e.target).length === 0 && !$personaCard.is(e.target) && $personaCard.has(e.target).length === 0) { - $personaCard.removeClass('is-active'); - $personaCard.removeAttr('style'); - } else { - $personaCard.addClass('is-active'); - } - }); - - }); - }; -})(jQuery); \ No newline at end of file diff --git a/src/components/Facepile/Jquery.Facepile.ts b/src/components/Facepile/Jquery.Facepile.ts new file mode 100644 index 00000000..431a1aae --- /dev/null +++ b/src/components/Facepile/Jquery.Facepile.ts @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// +/// +/// + +namespace fabric { + + + /** + * Facepile Plugin + * + * Adds basic demonstration functionality to .ms-Facepile components. + * + */ + export class Facepile { + + /** + * + * @param {HTMLElement} container - the target container for an instance of Facepile + * @constructor + */ + constructor(container: HTMLElement) { + new fabric.PeoplePicker(document.querySelector(".ms-PeoplePicker")); + new fabric.Panel(document.querySelector(".ms-Panel")); + + let $Facepile = $(container); + let $membersList = $(".ms-Facepile-members"); + let $membersCount = $(".ms-Facepile-members > .ms-Facepile-itemBtn").length; + let $panel = $(".ms-Facepile-panel.ms-Panel"); + let $panelMain = $panel.find(".ms-Panel-main"); + let $picker = $(".ms-PeoplePicker.ms-PeoplePicker--Facepile"); + let $pickerMembers = $picker.find(".ms-PeoplePicker-selectedPeople"); + let $personaCard = $(".ms-Facepile").find(".ms-PersonaCard"); + + + /** Increment member count and show/hide overflow text */ + let incrementMembers = function() { + /** Increment person count by one */ + $membersCount += 1; + + /** Display a maxiumum of 5 people */ + $(".ms-Facepile-members").children(":gt(4)").hide(); + + /** Display counter after 5 people are present */ + if ($membersCount > 5) { + $(".ms-Facepile-itemBtn--overflow").addClass("is-active"); + + let remainingMembers = $membersCount - 5; + $(".ms-Facepile-overflowText").text("+" + remainingMembers); + } + }; + + /** Open panel with people picker */ + $Facepile.on("click", ".js-addPerson", function() { + $panelMain.css({display: "block"}); + $panel.toggleClass("is-open") + .addClass("ms-Panel-animateIn") + .removeClass("ms-Facepile-panel--overflow ms-Panel--right") + .addClass("ms-Facepile-panel--addPerson"); + + /** Close any open persona cards */ + $personaCard.removeClass("is-active").hide(); + }); + + $panel.on("click", ".js-togglePanel", function() { + $panel.toggleClass("is-open") + .addClass("ms-Panel-animateIn"); + }); + + /** Open oveflow panel with list of members */ + $Facepile.on("click", ".js-overflowPanel", function() { + $panelMain.css({display: "block"}); + $panel.toggleClass("is-open") + .addClass("ms-Panel-animateIn") + .removeClass("ms-Facepile-panel--addPerson") + .addClass("ms-Facepile-panel--overflow ms-Panel--right"); + }); + + /** Display person count on page load */ + $(document).ready(function() { + $(".ms-Facepile-overflowText").text("+" + $membersCount); + }); + + /** Show selected members from PeoplePicker in the Facepile */ + $(".ms-PeoplePicker-result").on("click", function() { + let $this = $(this); + let name = $this.find(".ms-Persona-primaryText").html(); + let title = $this.find(".ms-Persona-secondaryText").html(); + let selectedInitials = (function() { + let nameArray = name.split(" "); + let nameInitials = ""; + for (let i = 0; i < nameArray.length; i++) { + nameInitials += nameArray[i].charAt(0); + } + + return nameInitials.substring(0, 2); + })(); + let selectedClasses = $this.find(".ms-Persona-initials").attr("class"); + let selectedImage = (function() { + if ($this.find(".ms-Persona-image").length) { + let selectedImageSrc = $this.find(".ms-Persona-image").attr("src"); + return "\"Persona"; + } else { + return ""; + } + })(); + + let FacepileItem = + ""; + + /** Add new item to members list in Facepile */ + $membersList.prepend(FacepileItem); + + /** Increment member count */ + incrementMembers(); + }); + + /** Remove members in panel people picker */ + $pickerMembers.on("click", ".js-selectedRemove", function() { + let memberText = $(this).parent().find(".ms-Persona-primaryText").text(); + + let $FacepileMember = $membersList.find(".ms-Persona-primaryText:contains(" + memberText + ")").first(); + + if ($FacepileMember) { + $FacepileMember.parent().closest(".ms-Facepile-itemBtn").remove(); + + $membersCount -= 1; + + /** Display a maxiumum of 5 people */ + $(".ms-Facepile-members").children(":lt(5)").show(); + + /** Display counter after 5 people are present */ + if ($membersCount <= 5) { + $(".ms-Facepile-itemBtn--overflow").removeClass("is-active"); + } else { + let remainingMembers = $membersCount - 5; + $(".ms-Facepile-overflowText").text("+" + remainingMembers); + } + } + }); + + /** Show persona card when selecting a Facepile item */ + $membersList.on("click", ".ms-Facepile-itemBtn", function() { + let selectedName = $(this).find(".ms-Persona-primaryText").html(); + let selectedTitle = $(this).find(".ms-Persona-secondaryText").html(); + let selectedInitials = (function() { + let name = selectedName.split(" "); + let nameInitials = ""; + for (let i = 0; i < name.length; i++) { + nameInitials += name[i].charAt(0); + } + + return nameInitials.substring(0, 2); + })(); + let selectedClasses = $(this).find(".ms-Persona-initials").attr("class"); + let selectedImage = $(this).find(".ms-Persona-image").attr("src"); + let $card = $(".ms-PersonaCard"); + let $cardName = $card.find(".ms-Persona-primaryText"); + let $cardTitle = $card.find(".ms-Persona-secondaryText"); + let $cardInitials = $card.find(".ms-Persona-initials"); + let $cardImage = $card.find(".ms-Persona-image"); + + /** Close any open persona cards */ + $personaCard.removeClass("is-active"); + + /** Add data to persona card */ + $cardName.text(selectedName); + $cardTitle.text(selectedTitle); + $cardInitials.text(selectedInitials); + $cardInitials.removeClass(); + $cardInitials.addClass(selectedClasses); + $cardImage.attr("src", selectedImage); + + /** Show persona card */ + setTimeout(function() { $personaCard.addClass("is-active"); }, 100); + + /** Align persona card on md and above screens */ + if ($(window).width() > 480) { + let itemPosition = $(this).offset().left; + let correctedPosition = itemPosition - 26; + + $personaCard.css({"left": correctedPosition}); + } else { + $personaCard.css({"left": 0, "top": "auto", "position": "fixed"}); + } + }); + + /** Dismiss persona card when clicking on the document */ + $(document).on("click", function(e) { + let $memberBtn = $(".ms-Facepile-itemBtn--member"); + + if (!$memberBtn.is(e.target) && $memberBtn.has(e.target).length === 0 && + !$personaCard.is(e.target) && $personaCard.has(e.target).length === 0) { + $personaCard.removeClass("is-active"); + $personaCard.removeAttr("style"); + } else { + $personaCard.addClass("is-active"); + } + }); + + } + } +} diff --git a/src/components/ListItem/Jquery.ListItem.js b/src/components/ListItem/Jquery.ListItem.js deleted file mode 100644 index 8248bfc2..00000000 --- a/src/components/ListItem/Jquery.ListItem.js +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * List Item Plugin - * - * Adds basic demonstration functionality to .ms-ListItem components. - * - * @param {jQuery Object} One or more .ms-ListItem components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.ListItem = function () { - - /** Go through each panel we've been given. */ - return this.each(function () { - - var $listItem = $(this); - - /** Detect clicks on selectable list items. */ - $listItem.on('click', '.js-toggleSelection', function() { - $(this).parents('.ms-ListItem').toggleClass('is-selected'); - }); - - }); - - }; -})(jQuery); diff --git a/src/components/ListItem/Jquery.ListItem.ts b/src/components/ListItem/Jquery.ListItem.ts new file mode 100644 index 00000000..6ac1b5bd --- /dev/null +++ b/src/components/ListItem/Jquery.ListItem.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + /** + * List Item Plugin + * + * Adds basic demonstration functionality to .ms-ListItem components. + * + */ + export class ListItem { + + /** + * + * @param {HTMLElement} container - the target container for an instance of ListItem + * @constructor + */ + constructor(container: HTMLElement) { + /** Detect clicks on selectable list items. */ + $(container).on("click", ".js-toggleSelection", function() { + $(this).parents(".ms-ListItem").toggleClass("is-selected"); + }); + } + } +} diff --git a/src/components/MessageBanner/MessageBanner.js b/src/components/MessageBanner/MessageBanner.js deleted file mode 100644 index 71edf977..00000000 --- a/src/components/MessageBanner/MessageBanner.js +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * MessageBanner component - * - * A component to display error messages - * - */ - -/** - * @namespace fabric - */ -var fabric = fabric || {}; -/** - * - * @param {HTMLElement} container - the target container for an instance of MessageBanner - * @constructor - */ -fabric.MessageBanner = function(container) { - this.container = container; - this.init(); -}; - -fabric.MessageBanner.prototype = (function() { - - var _clipper; - var _bufferSize; - var _textContainerMaxWidth = 700; - var _clientWidth; - var _textWidth; - var _initTextWidth; - var _chevronButton; - var _errorBanner; - var _actionButton; - var _closeButton; - var _bufferElementsWidth = 88; - var _bufferElementsWidthSmall = 35; - var SMALL_BREAK_POINT = 480; - - /** - * sets styles on resize - */ - var _onResize = function() { - _clientWidth = _errorBanner.offsetWidth; - if(window.innerWidth >= SMALL_BREAK_POINT ) { - _resizeRegular(); - } else { - _resizeSmall(); - } - }; - - /** - * resize above 480 pixel breakpoint - */ - var _resizeRegular = function() { - if ((_clientWidth - _bufferSize) > _initTextWidth && _initTextWidth < _textContainerMaxWidth) { - _textWidth = "auto"; - _chevronButton.className = "ms-MessageBanner-expand"; - _collapse(); - } else { - _textWidth = Math.min((_clientWidth - _bufferSize), _textContainerMaxWidth) + "px"; - if(_chevronButton.className.indexOf("is-visible") === -1) { - _chevronButton.className += " is-visible"; - } - } - _clipper.style.width = _textWidth; - }; - - /** - * resize below 480 pixel breakpoint - */ - var _resizeSmall = function() { - if (_clientWidth - (_bufferElementsWidthSmall + _closeButton.offsetWidth) > _initTextWidth) { - _textWidth = "auto"; - _collapse(); - } else { - _textWidth = (_clientWidth - (_bufferElementsWidthSmall + _closeButton.offsetWidth)) + "px"; - } - _clipper.style.width = _textWidth; - }; - /** - * caches elements and values of the component - */ - var _cacheDOM = function(context) { - _errorBanner = context.container; - _clipper = context.container.querySelector('.ms-MessageBanner-clipper'); - _chevronButton = context.container.querySelector('.ms-MessageBanner-expand'); - _actionButton = context.container.querySelector('.ms-MessageBanner-action'); - _bufferSize = _actionButton.offsetWidth + _bufferElementsWidth; - _closeButton = context.container.querySelector('.ms-MessageBanner-close'); - }; - - /** - * expands component to show full error message - */ - var _expand = function() { - var icon = _chevronButton.querySelector('.ms-Icon'); - _errorBanner.className += " is-expanded"; - icon.className = "ms-Icon ms-Icon--chevronsUp"; - }; - - /** - * collapses component to only show truncated message - */ - var _collapse = function() { - var icon = _chevronButton.querySelector('.ms-Icon'); - _errorBanner.className = "ms-MessageBanner"; - icon.className = "ms-Icon ms-Icon--chevronsDown"; - }; - - var _toggleExpansion = function() { - if (_errorBanner.className.indexOf("is-expanded") > -1) { - _collapse(); - } else { - _expand(); - } - }; - - /** - * hides banner when close button is clicked - */ - var _hideBanner = function() { - if(_errorBanner.className.indexOf("hide") === -1) { - _errorBanner.className += " hide"; - setTimeout(function() { - _errorBanner.className = "ms-MessageBanner is-hidden"; - }, 500); - } - }; - - /** - * shows banner if the banner is hidden - */ - var _showBanner = function() { - _errorBanner.className = "ms-MessageBanner"; - }; - - /** - * sets handlers for resize and button click events - */ - var _setListeners = function() { - window.addEventListener('resize', _onResize, false); - _chevronButton.addEventListener("click", _toggleExpansion, false); - _closeButton.addEventListener("click", _hideBanner, false); - }; - - /** - * initializes component - */ - var init = function() { - _cacheDOM(this); - _setListeners(); - _clientWidth = _errorBanner.offsetWidth; - _initTextWidth = _clipper.offsetWidth; - _onResize(null); - }; - - return { - init: init, - showBanner: _showBanner - }; -}()); diff --git a/src/components/MessageBanner/MessageBanner.ts b/src/components/MessageBanner/MessageBanner.ts new file mode 100644 index 00000000..f7936b3e --- /dev/null +++ b/src/components/MessageBanner/MessageBanner.ts @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. + +/** + * @namespace fabric + */ +namespace fabric { + "use strict"; + + /** + * MessageBanner component + * + * A component to display error messages + * + */ + export class MessageBanner { + public container: HTMLElement; + + private _clipper: HTMLElement; + private _bufferSize: number; + private _textContainerMaxWidth: number = 700; + private _clientWidth: number; + private _textWidth: string; + private _initTextWidth: number; + private _chevronButton: HTMLElement; + private _errorBanner: HTMLElement; + private _actionButton: HTMLElement; + private _closeButton: HTMLElement; + private _bufferElementsWidth: number = 88; + private _bufferElementsWidthSmall: number = 35; + private SMALL_BREAK_POINT: number = 480; + + /** + * + * @param {HTMLElement} container - the target container for an instance of MessageBanner + * @constructor + */ + constructor(container: HTMLElement) { + this.container = container; + this.init(); + } + + /** + * initializes component + */ + public init(): void { + this._cacheDOM(); + this._setListeners(); + this._clientWidth = this._errorBanner.offsetWidth; + this._initTextWidth = this._clipper.offsetWidth; + this._onResize(); + } + + /** + * shows banner if the banner is hidden + */ + public showBanner(): void { + this._errorBanner.className = "ms-MessageBanner"; + } + + /** + * sets styles on resize + */ + private _onResize(): void { + this._clientWidth = this._errorBanner.offsetWidth; + if (window.innerWidth >= this.SMALL_BREAK_POINT ) { + this._resizeRegular(); + } else { + this._resizeSmall(); + } + } + + /** + * resize above 480 pixel breakpoint + */ + private _resizeRegular(): void { + if ((this._clientWidth - this._bufferSize) > this._initTextWidth && this._initTextWidth < this._textContainerMaxWidth) { + this._textWidth = "auto"; + this._chevronButton.className = "ms-MessageBanner-expand"; + this._collapse(); + } else { + this._textWidth = Math.min((this._clientWidth - this._bufferSize), this._textContainerMaxWidth) + "px"; + if (this._chevronButton.className.indexOf("is-visible") === -1) { + this._chevronButton.className += " is-visible"; + } + } + this._clipper.style.width = this._textWidth; + } + + /** + * resize below 480 pixel breakpoint + */ + private _resizeSmall(): void { + if (this._clientWidth - (this._bufferElementsWidthSmall + this._closeButton.offsetWidth) > this._initTextWidth) { + this._textWidth = "auto"; + this._collapse(); + } else { + this._textWidth = (this._clientWidth - (this._bufferElementsWidthSmall + this._closeButton.offsetWidth)) + "px"; + } + this._clipper.style.width = this._textWidth; + } + + /** + * caches elements and values of the component + */ + private _cacheDOM(): void { + this._errorBanner = this.container; + this._clipper = this.container.querySelector(".ms-MessageBanner-clipper"); + this._chevronButton = this.container.querySelector(".ms-MessageBanner-expand"); + this._actionButton = this.container.querySelector(".ms-MessageBanner-action"); + this._bufferSize = this._actionButton.offsetWidth + this._bufferElementsWidth; + this._closeButton = this.container.querySelector(".ms-MessageBanner-close"); + } + + /** + * expands component to show full error message + */ + private _expand(): void { + let icon = this._chevronButton.querySelector(".ms-Icon"); + this._errorBanner.className += " is-expanded"; + icon.className = "ms-Icon ms-Icon--chevronsUp"; + } + + /** + * collapses component to only show truncated message + */ + private _collapse(): void { + let icon = this._chevronButton.querySelector(".ms-Icon"); + this._errorBanner.className = "ms-MessageBanner"; + icon.className = "ms-Icon ms-Icon--chevronsDown"; + } + + private _toggleExpansion(): void { + if (this._errorBanner.className.indexOf("is-expanded") > -1) { + this._collapse(); + } else { + this._expand(); + } + } + + private _hideMessageBanner(): void { + this._errorBanner.className = "ms-MessageBanner is-hidden"; + } + + /** + * hides banner when close button is clicked + */ + private _hideBanner(): void { + if (this._errorBanner.className.indexOf("hide") === -1) { + this._errorBanner.className += " hide"; + setTimeout(this._hideMessageBanner.bind(this), 500); + } + } + + /** + * sets handlers for resize and button click events + */ + private _setListeners(): void { + window.addEventListener("resize", this._onResize.bind(this), false); + this._chevronButton.addEventListener("click", this._toggleExpansion.bind(this), false); + this._closeButton.addEventListener("click", this._hideBanner.bind(this), false); + } + } +} // end fabric namespace diff --git a/src/components/NavBar/Jquery.NavBar.js b/src/components/NavBar/Jquery.NavBar.js deleted file mode 100644 index 10202242..00000000 --- a/src/components/NavBar/Jquery.NavBar.js +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Nav Bar Plugin - */ -(function ($) { - $.fn.NavBar = function () { - - /** Go through each nav bar we've been given. */ - return this.each(function () { - - var $navBar = $(this); - - // Open the nav bar on mobile. - $navBar.on('click', '.js-openMenu', function(event) { - event.stopPropagation(); - $navBar.toggleClass('is-open'); - }); - - // Close the nav bar on mobile. - $navBar.click(function() { - if ($navBar.hasClass('is-open')) { - $navBar.removeClass('is-open'); - } - }); - - // Set selected states and open/close menus. - $navBar.on('click', '.ms-NavBar-item:not(.is-disabled)', function(event) { - var $searchBox = $navBar.find('.ms-NavBar-item.ms-NavBar-item--search .ms-TextField-field'); - event.stopPropagation(); - - // Prevent default actions from firing if links are not found. - if ($(this).children('.ms-NavBar-link').length === 0) { - event.preventDefault(); - } - - // Deselect all of the items. - $(this).siblings('.ms-NavBar-item').removeClass('is-selected'); - - // Close and blur the search box if it doesn't have text. - if ($searchBox.length > 0 && $searchBox.val().length === 0) { - $('.ms-NavBar-item.ms-NavBar-item--search').removeClass('is-open').find('.ms-TextField-field').blur(); - } - - // Does the selected item have a menu? - if ($(this).hasClass('ms-NavBar-item--hasMenu')) { - - // First, close any neighboring menus. - $(this).siblings('.ms-NavBar-item--hasMenu').children('.ms-ContextualMenu:first').removeClass('is-open'); - - // Toggle 'is-open' to open or close it. - $(this).children('.ms-ContextualMenu:first').toggleClass('is-open'); - - // Toggle 'is-selected' to indicate whether it is active. - $(this).toggleClass('is-selected'); - } else { - // Doesn't have a menu, so just select the item. - $(this).addClass('is-selected'); - - // Close the submenu and any open contextual menus. - $navBar.removeClass('is-open').find('.ms-ContextualMenu').removeClass('is-open'); - } - - // Is this the search box? Open it up and focus on the search field. - if ($(this).hasClass('ms-NavBar-item--search')) { - $(this).addClass('is-open'); - $(this).find('.ms-TextField-field').focus(); - - // Close any open menus. - $navBar.find('.ms-ContextualMenu:first').removeClass('is-open'); - } - }); - - // Prevent contextual menus from being hidden when clicking on them. - $navBar.on('click', '.ms-NavBar-item .ms-ContextualMenu', function(event) { - event.stopPropagation(); - - // Collapse the mobile "panel" for nav items. - $(this).removeClass('is-open'); - $navBar.removeClass('is-open').find('.ms-NavBar-item--hasMenu').removeClass('is-selected'); - }); - - // Hide any menus and close the search box when clicking anywhere in the document. - $(document).on('click', 'html', function() { - var $searchBox = $navBar.find('.ms-NavBar-item.ms-NavBar-item--search .ms-TextField-field'); - $navBar.find('.ms-NavBar-item').removeClass('is-selected').find('.ms-ContextualMenu').removeClass('is-open'); - - // Close and blur the search box if it doesn't have text. - if ($searchBox.length > 0 && $searchBox.val().length === 0) { - $navBar.find('.ms-NavBar-item.ms-NavBar-item--search').removeClass('is-open').find('.ms-TextField-field').blur(); - } - }); - }); - }; -})(jQuery); diff --git a/src/components/NavBar/Jquery.NavBar.ts b/src/components/NavBar/Jquery.NavBar.ts new file mode 100644 index 00000000..903a076a --- /dev/null +++ b/src/components/NavBar/Jquery.NavBar.ts @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + /** + * Nav Bar Plugin + */ + export class NavBar { + + /** + * + * @param {HTMLElement} container - the target container for an instance of NavBar + * @constructor + */ + constructor(container: HTMLElement) { + let $navBar = $(container); + + // Open the nav bar on mobile. + $navBar.on("click", ".js-openMenu", function(event) { + event.stopPropagation(); + $navBar.toggleClass("is-open"); + }); + + // Close the nav bar on mobile. + $navBar.click(function() { + if ($navBar.hasClass("is-open")) { + $navBar.removeClass("is-open"); + } + }); + + // Set selected states and open/close menus. + $navBar.on("click", ".ms-NavBar-item:not(.is-disabled)", function(event) { + let $searchBox = $navBar.find(".ms-NavBar-item.ms-NavBar-item--search .ms-TextField-field"); + event.stopPropagation(); + + // Prevent default actions from firing if links are not found. + if ($(this).children(".ms-NavBar-link").length === 0) { + event.preventDefault(); + } + + // Deselect all of the items. + $(this).siblings(".ms-NavBar-item").removeClass("is-selected"); + + // Close and blur the search box if it doesn't have text. + if ($searchBox.length > 0 && $searchBox.val().length === 0) { + $(".ms-NavBar-item.ms-NavBar-item--search").removeClass("is-open").find(".ms-TextField-field").blur(); + } + + // Does the selected item have a menu? + if ($(this).hasClass("ms-NavBar-item--hasMenu")) { + + // First, close any neighboring menus. + $(this).siblings(".ms-NavBar-item--hasMenu").children(".ms-ContextualMenu:first").removeClass("is-open"); + + // Toggle "is-open" to open or close it. + $(this).children(".ms-ContextualMenu:first").toggleClass("is-open"); + + // Toggle "is-selected" to indicate whether it is active. + $(this).toggleClass("is-selected"); + } else { + // Doesn't have a menu, so just select the item. + $(this).addClass("is-selected"); + + // Close the submenu and any open contextual menus. + $navBar.removeClass("is-open").find(".ms-ContextualMenu").removeClass("is-open"); + } + + // Is this the search box? Open it up and focus on the search field. + if ($(this).hasClass("ms-NavBar-item--search")) { + $(this).addClass("is-open"); + $(this).find(".ms-TextField-field").focus(); + + // Close any open menus. + $navBar.find(".ms-ContextualMenu:first").removeClass("is-open"); + } + }); + + // Prevent contextual menus from being hidden when clicking on them. + $navBar.on("click", ".ms-NavBar-item .ms-ContextualMenu", function(event) { + event.stopPropagation(); + + // Collapse the mobile "panel" for nav items. + $(this).removeClass("is-open"); + $navBar.removeClass("is-open").find(".ms-NavBar-item--hasMenu").removeClass("is-selected"); + }); + + // Hide any menus and close the search box when clicking anywhere in the document. + $(document).on("click", "html", function() { + let $searchBox = $navBar.find(".ms-NavBar-item.ms-NavBar-item--search .ms-TextField-field"); + $navBar.find(".ms-NavBar-item").removeClass("is-selected").find(".ms-ContextualMenu").removeClass("is-open"); + + // Close and blur the search box if it doesn't have text. + if ($searchBox.length > 0 && $searchBox.val().length === 0) { + $navBar.find(".ms-NavBar-item.ms-NavBar-item--search").removeClass("is-open").find(".ms-TextField-field").blur(); + } + }); + } + } +} diff --git a/src/components/Panel/Jquery.Panel.js b/src/components/Panel/Jquery.Panel.js deleted file mode 100644 index 1bb22422..00000000 --- a/src/components/Panel/Jquery.Panel.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Panel Plugin - * - * Adds basic demonstration functionality to .ms-Panel components. - * - * @param {jQuery Object} One or more .ms-Panel components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.Panel = function () { - - var pfx = ["webkit", "moz", "MS", "o", ""]; - - // Prefix function - function prefixedEvent(element, type, callback) { - for (var p = 0; p < pfx.length; p++) { - if (!pfx[p]) { type = type.toLowerCase(); } - element.addEventListener(pfx[p]+type, callback, false); - } - } - - /** Go through each panel we've been given. */ - return this.each(function () { - - var $panel = $(this); - var $panelMain = $panel.find(".ms-Panel-main"); - - /** Hook to open the panel. */ - $(".ms-PanelAction-close").on("click", function() { - - // Display Panel first, to allow animations - $panel.addClass("ms-Panel-animateOut"); - - }); - - $(".ms-PanelAction-open").on("click", function() { - - // Display Panel first, to allow animations - $panel.addClass("is-open"); - - // Add animation class - $panel.addClass("ms-Panel-animateIn"); - - }); - - prefixedEvent($panelMain[0], 'AnimationEnd', function(event) { - if (event.animationName.indexOf('Out') > -1) { - - // Hide and Prevent ms-Panel-main from being interactive - $panel.removeClass('is-open'); - - // Remove animating classes for the next time we open panel - $panel.removeClass('ms-Panel-animateIn ms-Panel-animateOut'); - - } - }); - - // Pivots for sample page to show variant panel sizes - $(".panelVariant-item").on("click", function() { - var className = $(this).find('span').attr('class'); - - $(".panelVariant-item").removeClass('is-selected'); - $(this).addClass('is-selected'); - - switch (className) { - case 'is-default': - $('.ms-Panel').removeClass().addClass('ms-Panel'); - break; - case 'is-left': - $('.ms-Panel').removeClass().addClass('ms-Panel ms-Panel--left'); - break; - case 'is-lightDismiss': - $('.ms-Panel').removeClass().addClass('ms-Panel ms-Panel--lightDismiss'); - break; - case 'is-md': - $('.ms-Panel').removeClass().addClass('ms-Panel ms-Panel--md'); - break; - case 'is-lg': - $('.ms-Panel').removeClass().addClass('ms-Panel ms-Panel--lg'); - break; - case 'is-xl': - $('.ms-Panel').removeClass().addClass('ms-Panel ms-Panel--xl'); - break; - } - }); - }); - - }; -})(jQuery); diff --git a/src/components/Panel/Jquery.Panel.ts b/src/components/Panel/Jquery.Panel.ts new file mode 100644 index 00000000..86475e11 --- /dev/null +++ b/src/components/Panel/Jquery.Panel.ts @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + + let pfx = ["webkit", "moz", "MS", "o", ""]; + + // Prefix function + function prefixedEvent(element, type, callback) { + for (let p = 0; p < pfx.length; p++) { + if (!pfx[p]) { type = type.toLowerCase(); } + element.addEventListener(pfx[p] + type, callback, false); + } + } + + + /** + * Panel Plugin + * + * Adds basic demonstration functionality to .ms-Panel components. + * + */ + export class Panel { + + /** + * + * @param {HTMLElement} container - the target container for an instance of Panel + * @constructor + */ + constructor(container: HTMLElement) { + let $panel = $(container); + let $panelMain = $panel.find(".ms-Panel-main"); + + /** Hook to open the panel. */ + $(".ms-PanelAction-close").on("click", function() { + + // Display Panel first, to allow animations + $panel.addClass("ms-Panel-animateOut"); + + }); + + $(".ms-PanelAction-open").on("click", function() { + + // Display Panel first, to allow animations + $panel.addClass("is-open"); + + // Add animation class + $panel.addClass("ms-Panel-animateIn"); + + }); + + prefixedEvent($panelMain[0], "AnimationEnd", function(event) { + if (event.animationName.indexOf("Out") > -1) { + + // Hide and Prevent ms-Panel-main from being interactive + $panel.removeClass("is-open"); + + // Remove animating classes for the next time we open panel + $panel.removeClass("ms-Panel-animateIn ms-Panel-animateOut"); + + } + }); + + // Pivots for sample page to show letiant panel sizes + $(".panelVariant-item").on("click", function() { + let className = $(this).find("span").attr("class"); + + $(".panelVariant-item").removeClass("is-selected"); + $(this).addClass("is-selected"); + + switch (className) { + case "is-default": + $(".ms-Panel").removeClass().addClass("ms-Panel"); + break; + case "is-left": + $(".ms-Panel").removeClass().addClass("ms-Panel ms-Panel--left"); + break; + case "is-lightDismiss": + $(".ms-Panel").removeClass().addClass("ms-Panel ms-Panel--lightDismiss"); + break; + case "is-md": + $(".ms-Panel").removeClass().addClass("ms-Panel ms-Panel--md"); + break; + case "is-lg": + $(".ms-Panel").removeClass().addClass("ms-Panel ms-Panel--lg"); + break; + case "is-xl": + $(".ms-Panel").removeClass().addClass("ms-Panel ms-Panel--xl"); + break; + } + }); + } + } +} diff --git a/src/components/PeoplePicker/Jquery.PeoplePicker.js b/src/components/PeoplePicker/Jquery.PeoplePicker.js deleted file mode 100644 index 5382cd63..00000000 --- a/src/components/PeoplePicker/Jquery.PeoplePicker.js +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -var fabric = fabric || {}; - -/** - * People Picker Plugin - * - * Adds basic demonstration functionality to .ms-PeoplePicker components. - * - * @param {jQuery Object} One or more .ms-PeoplePicker components - * @return {jQuery Object} The same components (allows for chaining) - */ - -(function ($) { - $.fn.PeoplePicker = function () { - - /** Iterate through each people picker provided. */ - return this.each(function () { - - var $peoplePicker = $(this); - var $searchField = $peoplePicker.find(".ms-PeoplePicker-searchField"); - var $results = $peoplePicker.find(".ms-PeoplePicker-results"); - var $selected = $peoplePicker.find('.ms-PeoplePicker-selected'); - var $selectedPeople = $peoplePicker.find(".ms-PeoplePicker-selectedPeople"); - var $selectedCount = $peoplePicker.find(".ms-PeoplePicker-selectedCount"); - var $peopleList = $peoplePicker.find(".ms-PeoplePicker-peopleList"); - var isActive = false; - var spinner; - var $personaCard = $('.ms-PeoplePicker').find('.ms-PersonaCard'); - - // Run when focused or clicked - function peoplePickerActive(event) { - /** Scroll the view so that the people picker is at the top. */ - $('html, body').animate({ - scrollTop: $peoplePicker.offset().top - }, 367); - - /** Start by closing any open people pickers. */ - if ( $peoplePicker.hasClass('is-active') ) { - $peoplePicker.removeClass("is-active"); - } - - /** Display a maxiumum of 5 people in Facepile variant */ - if ($peoplePicker.hasClass('ms-PeoplePicker--Facepile') && $searchField.val() === "") { - $peopleList.children(":gt(4)").hide(); - } - - /** Animate results and members in Facepile variant. */ - if ($peoplePicker.hasClass('ms-PeoplePicker--Facepile')) { - // $results.addClass('ms-u-slideDownIn20'); - $selectedPeople.addClass('ms-u-slideDownIn20'); - setTimeout(function() { $results.removeClass('ms-u-slideDownIn20'); $selectedPeople.removeClass('ms-u-slideDownIn20');}, 1000); - } - - isActive = true; - - /** Stop the click event from propagating, which would just close the dropdown immediately. */ - event.stopPropagation(); - - /** Before opening, size the results panel to match the people picker. */ - if (!$peoplePicker.hasClass('ms-PeoplePicker--Facepile')) { - $results.width($peoplePicker.width() - 2); - } - - /** Show the $results by setting the people picker to active. */ - $peoplePicker.addClass("is-active"); - - /** Temporarily bind an event to the document that will close the people picker when clicking anywhere. */ - $(document).bind("click.peoplepicker", function() { - $peoplePicker.removeClass('is-active'); - if ($peoplePicker.hasClass('ms-PeoplePicker--Facepile')) { - $peoplePicker.removeClass('is-searching'); - $('.ms-PeoplePicker-selected').show(); - $('.ms-PeoplePicker-searchMore').removeClass('is-active'); - $searchField.val(""); - } - $(document).unbind('click.peoplepicker'); - isActive = false; - }); - } - - /** Set to active when focusing on the input. */ - $peoplePicker.on('focus', '.ms-PeoplePicker-searchField', function(event) { - peoplePickerActive(event); - }); - - /** Set to active when clicking on the input. */ - $peoplePicker.on('click', '.ms-PeoplePicker-searchField', function(event) { - peoplePickerActive(event); - }); - - /** Keep the people picker active when clicking within it. */ - $(this).click(function (event) { - event.stopPropagation(); - }); - - /** Add the selected person to the text field or selected list and close the people picker. */ - $results.on('click', '.ms-PeoplePicker-result', function () { - var $this = $(this); - var selectedName = $this.find(".ms-Persona-primaryText").html(); - var selectedTitle = $this.find(".ms-Persona-secondaryText").html(); - var selectedInitials = (function() { - var name = selectedName.split(' '); - var nameInitials = ''; - - for (var i = 0; i < name.length; i++) { - nameInitials += name[i].charAt(0); - } - - return nameInitials.substring(0,2); - })(); - var selectedClasses = $this.find('.ms-Persona-initials').attr('class'); - var selectedImage = (function() { - if ($this.find('.ms-Persona-image').length) { - var selectedImageSrc = $this.find('.ms-Persona-image').attr('src'); - return 'Persona image'; - } else { - return ''; - } - })(); - - /** Token html */ - var personaHTML = '
    ' + - '
    ' + - '
    ' + - '
    ' + selectedInitials + '
    ' + - selectedImage + - '
    ' + - '
    ' + - '
    ' + - '
    ' + selectedName + '
    ' + - '
    ' + - '
    ' + - '' + - '
    '; - /** List item html */ - var personaListItem = '
  • ' + - '
    ' + - '
    ' + - '
    ' + selectedInitials + '
    ' + - selectedImage + - '
    ' + - '
    ' + - '
    ' + - '
    ' + selectedName + '
    ' + - '
    ' + selectedTitle + '
    ' + - '
    ' + - '
    ' + - '' + - '
  • '; - /** Tokenize selected persona if not Facepile or memberslist variants */ - if (!$peoplePicker.hasClass('ms-PeoplePicker--Facepile') && !$peoplePicker.hasClass('ms-PeoplePicker--membersList') ) { - $searchField.before(personaHTML); - $peoplePicker.removeClass("is-active"); - resizeSearchField($peoplePicker); - } - /** Add selected persona to a list if Facepile or memberslist variants */ - else { - if (!$selected.hasClass('is-active')) { - $selected.addClass('is-active'); - } - /** Prepend persona list item html to selected people list */ - $selectedPeople.prepend(personaListItem); - /** Close the picker */ - $peoplePicker.removeClass("is-active"); - /** Get the total amount of selected personas and display that number */ - var count = $peoplePicker.find('.ms-PeoplePicker-selectedPerson').length; - $selectedCount.html(count); - /** Return picker back to default state: - - Show only the first five results in the people list for when the picker is reopened - - Make searchMore inactive - - Clear any search field text - */ - $peopleList.children().show(); - $peopleList.children(":gt(4)").hide(); - - $('.ms-PeoplePicker-searchMore').removeClass('is-active'); - $searchField.val(""); - } - }); - - /** Remove the persona when clicking the personaRemove button. */ - $peoplePicker.on('click', '.ms-PeoplePicker-personaRemove', function() { - $(this).parents('.ms-PeoplePicker-persona').remove(); - - /** Make the search field 100% width if all personas have been removed */ - if ( $('.ms-PeoplePicker-persona').length === 0 ) { - $peoplePicker.find('.ms-PeoplePicker-searchField').outerWidth('100%'); - } else { - resizeSearchField($peoplePicker); - } - }); - - /** Trigger additional searching when clicking the search more area. */ - $results.on('click', '.js-searchMore', function() { - var $searchMore = $(this); - var primaryLabel = $searchMore.find(".ms-PeoplePicker-searchMorePrimary"); - var originalPrimaryLabelText = primaryLabel.html(); - var searchFieldText = $searchField.val(); - - /** Change to searching state. */ - $searchMore.addClass("is-searching"); - primaryLabel.html("Searching for " + searchFieldText); - - /** Attach Spinner */ - if (!spinner) { - spinner = new fabric.Spinner($searchMore.get(0)); - } else { - spinner.start(); - } - - /** Show all results in Facepile variant */ - if($peoplePicker.hasClass('ms-PeoplePicker--Facepile')) { - setTimeout(function() {$peopleList.children().show();}, 1500); - } - - /** Return the original state. */ - setTimeout(function() { - $searchMore.removeClass("is-searching"); - primaryLabel.html(originalPrimaryLabelText); - spinner.stop(); - }, 1500); - }); - - /** Remove a result using the action icon. */ - $results.on('click', '.js-resultRemove', function(event) { - event.stopPropagation(); - $(this).parent(".ms-PeoplePicker-result").remove(); - }); - - /** Expand a result if more details are available. */ - $results.on('click', '.js-resultExpand', function(event) { - event.stopPropagation(); - $(this).parent(".ms-PeoplePicker-result").toggleClass("is-expanded"); - }); - - /** Remove a selected person using the action icon. */ - $selectedPeople.on('click', '.js-selectedRemove', function(event) { - event.stopPropagation(); - $(this).parent(".ms-PeoplePicker-selectedPerson").remove(); - var count = $peoplePicker.find('.ms-PeoplePicker-selectedPerson').length; - $selectedCount.html(count); - if ($peoplePicker.find('.ms-PeoplePicker-selectedPerson').length === 0) { - $selected.removeClass('is-active'); - } - }); - - var filterResults = function(results, currentSuggestion, currentValueExists) { - return results.find('.ms-Persona-primaryText').filter(function() { - if (currentValueExists) { - return $(this).text().toLowerCase() === currentSuggestion; - } else { - return $(this).text().toLowerCase() !== currentSuggestion; - } - }).parents('.ms-PeoplePicker-peopleListItem'); - }; - - /** Search people picker items */ - $peoplePicker.on('keyup', '.ms-PeoplePicker-searchField', function(evt) { - var suggested = []; - var newSuggestions = []; - var $pickerResult = $results.find('.ms-Persona-primaryText'); - - $peoplePicker.addClass('is-searching'); - - /** Hide members */ - $selected.hide(); - - /** Show 5 results */ - $peopleList.children(":lt(5)").show(); - - /** Show searchMore button */ - $('.ms-PeoplePicker-searchMore').addClass('is-active'); - - /** Get array of suggested people */ - $pickerResult.each(function() { suggested.push($(this).text()); }); - - /** Iterate over array to find matches and show matching items */ - for (var i = 0; i < suggested.length; i++) { - var currentPersona = suggested[i].toLowerCase(); - var currentValue = evt.target.value.toLowerCase(); - var currentSuggestion; - - if (currentPersona.indexOf(currentValue) > -1) { - currentSuggestion = suggested[i].toLowerCase(); - - newSuggestions.push(suggested[i]); - - filterResults($results, currentSuggestion, true).show(); - } else { - filterResults($results, currentSuggestion, false).hide(); - } - } - - /** Show members and hide searchmore when field is empty */ - if ($(this).val() === "") { - $peoplePicker.removeClass('is-searching'); - $selected.show(); - $('.ms-PeoplePicker-searchMore').removeClass('is-active'); - $selectedPeople.addClass('ms-u-slideDownIn20'); - setTimeout(function() { $selectedPeople.removeClass('ms-u-slideDownIn20');}, 1000); - $peopleList.children(":gt(4)").hide(); - } - }); - - /** Show persona card when clicking a persona in the members list */ - $selectedPeople.on('click', '.ms-Persona', function() { - var selectedName = $(this).find(".ms-Persona-primaryText").html(); - var selectedTitle = $(this).find(".ms-Persona-secondaryText").html(); - var selectedInitials = (function() { - var name = selectedName.split(' '); - var nameInitials = ''; - - for (var i = 0; i < name.length; i++) { - nameInitials += name[i].charAt(0); - } - - return nameInitials.substring(0,2); - })(); - var selectedClasses = $(this).find('.ms-Persona-initials').attr('class'); - var selectedImage = $(this).find('.ms-Persona-image').attr('src'); - var $card = $('.ms-PersonaCard'); - var $cardName = $card.find('.ms-Persona-primaryText'); - var $cardTitle = $card.find('.ms-Persona-secondaryText'); - var $cardInitials = $card.find('.ms-Persona-initials'); - var $cardImage = $card.find('.ms-Persona-image'); - - /** Close any open persona cards */ - $personaCard.removeClass('is-active'); - - /** Add data to persona card */ - $cardName.text(selectedName); - $cardTitle.text(selectedTitle); - $cardInitials.text(selectedInitials); - $cardInitials.removeClass(); - $cardInitials.addClass(selectedClasses); - $cardImage.attr('src', selectedImage); - - /** Show persona card */ - setTimeout(function() { - $personaCard.addClass('is-active'); - setTimeout(function(){$personaCard.css({'animation-name': 'none'});}, 300); - }, 100); - - /** Align persona card on md and above screens */ - if ($(window).width() > 480) { - var itemPositionTop = $(this).offset().top; - var correctedPositionTop = itemPositionTop + 10; - - $personaCard.css({'top': correctedPositionTop, 'left': 0}); - } else { - $personaCard.css({'top': 'auto'}); - } - }); - - /** Dismiss persona card when clicking on the document */ - $(document).on('click', function(e) { - var $memberBtn = $('.ms-PeoplePicker-selectedPerson').find('.ms-Persona'); - - if (!$memberBtn.is(e.target) && !$personaCard.is(e.target) && $personaCard.has(e.target).length === 0) { - $personaCard.removeClass('is-active'); - setTimeout(function(){$personaCard.removeAttr('style');}, 300); - } else { - $personaCard.addClass('is-active'); - } - }); - }); - }; - - /** Resize the search field to match the search box */ - function resizeSearchField($peoplePicker) { - var $searchBox = $peoplePicker.find('.ms-PeoplePicker-searchBox'); - - // Where is the right edge of the search box? - var searchBoxLeftEdge = $searchBox.position().left; - var searchBoxWidth = $searchBox.outerWidth(); - var searchBoxRightEdge = searchBoxLeftEdge + searchBoxWidth; - - // Where is the right edge of the last persona component? - var $lastPersona = $searchBox.find('.ms-PeoplePicker-persona:last'); - var lastPersonaLeftEdge = $lastPersona.offset().left; - var lastPersonaWidth = $lastPersona.outerWidth(); - var lastPersonaRightEdge = lastPersonaLeftEdge + lastPersonaWidth; - - // Adjust the width of the field to fit the remaining space. - var newFieldWidth = searchBoxRightEdge - lastPersonaRightEdge - 7; - - // Don't let the field get too tiny. - if (newFieldWidth < 100) { - newFieldWidth = "100%"; - } - - // Set the width of the search field - $peoplePicker.find('.ms-PeoplePicker-searchField').outerWidth(newFieldWidth); - } -})(jQuery); diff --git a/src/components/PeoplePicker/Jquery.PeoplePicker.ts b/src/components/PeoplePicker/Jquery.PeoplePicker.ts new file mode 100644 index 00000000..0af0cbc8 --- /dev/null +++ b/src/components/PeoplePicker/Jquery.PeoplePicker.ts @@ -0,0 +1,417 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// +/// + +namespace fabric { + + + /** Resize the search field to match the search box */ + function resizeSearchField($peoplePicker) { + let $searchBox = $peoplePicker.find(".ms-PeoplePicker-searchBox"); + + // Where is the right edge of the search box? + let searchBoxLeftEdge = $searchBox.position().left; + let searchBoxWidth = $searchBox.outerWidth(); + let searchBoxRightEdge = searchBoxLeftEdge + searchBoxWidth; + + // Where is the right edge of the last persona component? + let $lastPersona = $searchBox.find(".ms-PeoplePicker-persona:last"); + let lastPersonaLeftEdge = $lastPersona.offset().left; + let lastPersonaWidth = $lastPersona.outerWidth(); + let lastPersonaRightEdge = lastPersonaLeftEdge + lastPersonaWidth; + + // Adjust the width of the field to fit the remaining space. + let newFieldWidth: any = searchBoxRightEdge - lastPersonaRightEdge - 7; + + // Don"t let the field get too tiny. + if (newFieldWidth < 100) { + newFieldWidth = "100%"; + } + + // Set the width of the search field + $peoplePicker.find(".ms-PeoplePicker-searchField").outerWidth(newFieldWidth); + } + + /** + * People Picker Plugin + * + * Adds basic demonstration functionality to .ms-PeoplePicker components. + * + */ + export class PeoplePicker { + + /** + * + * @param {HTMLElement} container - the target container for an instance of PeoplePicker + * @constructor + */ + constructor(container: HTMLElement) { + + let $peoplePicker = $(container); + let $searchField = $peoplePicker.find(".ms-PeoplePicker-searchField"); + let $results = $peoplePicker.find(".ms-PeoplePicker-results"); + let $selected = $peoplePicker.find(".ms-PeoplePicker-selected"); + let $selectedPeople = $peoplePicker.find(".ms-PeoplePicker-selectedPeople"); + let $selectedCount = $peoplePicker.find(".ms-PeoplePicker-selectedCount"); + let $peopleList = $peoplePicker.find(".ms-PeoplePicker-peopleList"); + let isActive = false; + let spinner; + let $personaCard = $(".ms-PeoplePicker").find(".ms-PersonaCard"); + + // Run when focused or clicked + function peoplePickerActive(event) { + /** Scroll the view so that the people picker is at the top. */ + $("html, body").animate({ + scrollTop: $peoplePicker.offset().top, + }, 367); + + /** Start by closing any open people pickers. */ + if ( $peoplePicker.hasClass("is-active") ) { + $peoplePicker.removeClass("is-active"); + } + + /** Display a maxiumum of 5 people in Facepile letiant */ + if ($peoplePicker.hasClass("ms-PeoplePicker--Facepile") && $searchField.val() === "") { + $peopleList.children(":gt(4)").hide(); + } + + /** Animate results and members in Facepile letiant. */ + if ($peoplePicker.hasClass("ms-PeoplePicker--Facepile")) { + // $results.addClass("ms-u-slideDownIn20"); + $selectedPeople.addClass("ms-u-slideDownIn20"); + setTimeout(function() { + $results.removeClass("ms-u-slideDownIn20"); + $selectedPeople.removeClass("ms-u-slideDownIn20"); + }, 1000); + } + + isActive = true; + + /** Stop the click event from propagating, which would just close the dropdown immediately. */ + event.stopPropagation(); + + /** Before opening, size the results panel to match the people picker. */ + if (!$peoplePicker.hasClass("ms-PeoplePicker--Facepile")) { + $results.width($peoplePicker.width() - 2); + } + + /** Show the $results by setting the people picker to active. */ + $peoplePicker.addClass("is-active"); + + /** Temporarily bind an event to the document that will close the people picker when clicking anywhere. */ + $(document).bind("click.peoplepicker", function() { + $peoplePicker.removeClass("is-active"); + if ($peoplePicker.hasClass("ms-PeoplePicker--Facepile")) { + $peoplePicker.removeClass("is-searching"); + $(".ms-PeoplePicker-selected").show(); + $(".ms-PeoplePicker-searchMore").removeClass("is-active"); + $searchField.val(""); + } + $(document).unbind("click.peoplepicker"); + isActive = false; + }); + } + + /** Set to active when focusing on the input. */ + $peoplePicker.on("focus", ".ms-PeoplePicker-searchField", function(event) { + peoplePickerActive(event); + }); + + /** Set to active when clicking on the input. */ + $peoplePicker.on("click", ".ms-PeoplePicker-searchField", function(event) { + peoplePickerActive(event); + }); + + /** Keep the people picker active when clicking within it. */ + $peoplePicker.click(function (event) { + event.stopPropagation(); + }); + + /** Add the selected person to the text field or selected list and close the people picker. */ + $results.on("click", ".ms-PeoplePicker-result", function () { + let $this = $(this); + let selectedName = $this.find(".ms-Persona-primaryText").html(); + let selectedTitle = $this.find(".ms-Persona-secondaryText").html(); + let selectedInitials = (function() { + let name = selectedName.split(" "); + let nameInitials = ""; + + for (let i = 0; i < name.length; i++) { + nameInitials += name[i].charAt(0); + } + + return nameInitials.substring(0, 2); + })(); + let selectedClasses = $this.find(".ms-Persona-initials").attr("class"); + let selectedImage = (function() { + if ($this.find(".ms-Persona-image").length) { + let selectedImageSrc = $this.find(".ms-Persona-image").attr("src"); + return "\"Persona"; + } else { + return ""; + } + })(); + + /** Token html */ + let personaHTML = "
    " + + "
    " + + "
    " + + "
    " + selectedInitials + "
    " + + selectedImage + + "
    " + + "
    " + + "
    " + + "
    " + selectedName + "
    " + + "
    " + + "
    " + + "" + + "
    "; + /** List item html */ + let personaListItem = "
  • " + + "
    " + + "
    " + + "
    " + selectedInitials + "
    " + + selectedImage + + "
    " + + "
    " + + "
    " + + "
    " + selectedName + "
    " + + "
    " + selectedTitle + "
    " + + "
    " + + "
    " + + "" + + "
  • "; + /** Tokenize selected persona if not Facepile or memberslist letiants */ + if (!$peoplePicker.hasClass("ms-PeoplePicker--Facepile") && !$peoplePicker.hasClass("ms-PeoplePicker--membersList") ) { + $searchField.before(personaHTML); + $peoplePicker.removeClass("is-active"); + resizeSearchField($peoplePicker); + } else { + /** Add selected persona to a list if Facepile or memberslist letiants */ + if (!$selected.hasClass("is-active")) { + $selected.addClass("is-active"); + } + /** Prepend persona list item html to selected people list */ + $selectedPeople.prepend(personaListItem); + /** Close the picker */ + $peoplePicker.removeClass("is-active"); + /** Get the total amount of selected personas and display that number */ + let count: any = $peoplePicker.find(".ms-PeoplePicker-selectedPerson").length; + $selectedCount.html(count); + /** Return picker back to default state: + - Show only the first five results in the people list for when the picker is reopened + - Make searchMore inactive + - Clear any search field text + */ + $peopleList.children().show(); + $peopleList.children(":gt(4)").hide(); + + $(".ms-PeoplePicker-searchMore").removeClass("is-active"); + $searchField.val(""); + } + }); + + /** Remove the persona when clicking the personaRemove button. */ + $peoplePicker.on("click", ".ms-PeoplePicker-personaRemove", function() { + $(this).parents(".ms-PeoplePicker-persona").remove(); + + /** Make the search field 100% width if all personas have been removed */ + if ( $(".ms-PeoplePicker-persona").length === 0 ) { + $peoplePicker.find(".ms-PeoplePicker-searchField").outerWidth("100%"); + } else { + resizeSearchField($peoplePicker); + } + }); + + /** Trigger additional searching when clicking the search more area. */ + $results.on("click", ".js-searchMore", function() { + let $searchMore = $(this); + let primaryLabel = $searchMore.find(".ms-PeoplePicker-searchMorePrimary"); + let originalPrimaryLabelText = primaryLabel.html(); + let searchFieldText = $searchField.val(); + + /** Change to searching state. */ + $searchMore.addClass("is-searching"); + primaryLabel.html("Searching for " + searchFieldText); + + /** Attach Spinner */ + if (!spinner) { + spinner = new fabric.Spinner($searchMore.get(0)); + } else { + spinner.start(); + } + + /** Show all results in Facepile letiant */ + if ($peoplePicker.hasClass("ms-PeoplePicker--Facepile")) { + setTimeout(function() { + $peopleList.children().show(); + }, 1500); + } + + /** Return the original state. */ + setTimeout(function() { + $searchMore.removeClass("is-searching"); + primaryLabel.html(originalPrimaryLabelText); + spinner.stop(); + }, 1500); + }); + + /** Remove a result using the action icon. */ + $results.on("click", ".js-resultRemove", function(event) { + event.stopPropagation(); + $(this).parent(".ms-PeoplePicker-result").remove(); + }); + + /** Expand a result if more details are available. */ + $results.on("click", ".js-resultExpand", function(event) { + event.stopPropagation(); + $(this).parent(".ms-PeoplePicker-result").toggleClass("is-expanded"); + }); + + /** Remove a selected person using the action icon. */ + $selectedPeople.on("click", ".js-selectedRemove", function(event) { + event.stopPropagation(); + $(this).parent(".ms-PeoplePicker-selectedPerson").remove(); + let count: any = $peoplePicker.find(".ms-PeoplePicker-selectedPerson").length; + $selectedCount.html(count); + if ($peoplePicker.find(".ms-PeoplePicker-selectedPerson").length === 0) { + $selected.removeClass("is-active"); + } + }); + + let filterResults = function(results, currentSuggestion, currentValueExists) { + return results.find(".ms-Persona-primaryText").filter(function() { + if (currentValueExists) { + return $(this).text().toLowerCase() === currentSuggestion; + } else { + return $(this).text().toLowerCase() !== currentSuggestion; + } + }).parents(".ms-PeoplePicker-peopleListItem"); + }; + + /** Search people picker items */ + $peoplePicker.on("keyup", ".ms-PeoplePicker-searchField", function(evt) { + let suggested = []; + let newSuggestions = []; + let $pickerResult = $results.find(".ms-Persona-primaryText"); + + $peoplePicker.addClass("is-searching"); + + /** Hide members */ + $selected.hide(); + + /** Show 5 results */ + $peopleList.children(":lt(5)").show(); + + /** Show searchMore button */ + $(".ms-PeoplePicker-searchMore").addClass("is-active"); + + /** Get array of suggested people */ + $pickerResult.each(function() { suggested.push($(this).text()); }); + + /** Iterate over array to find matches and show matching items */ + for (let i = 0; i < suggested.length; i++) { + let currentPersona = suggested[i].toLowerCase(); + let currentValue = (evt.target).value.toLowerCase(); + let currentSuggestion; + + if (currentPersona.indexOf(currentValue) > -1) { + currentSuggestion = suggested[i].toLowerCase(); + + newSuggestions.push(suggested[i]); + + filterResults($results, currentSuggestion, true).show(); + } else { + filterResults($results, currentSuggestion, false).hide(); + } + } + + /** Show members and hide searchmore when field is empty */ + if ($(this).val() === "") { + $peoplePicker.removeClass("is-searching"); + $selected.show(); + $(".ms-PeoplePicker-searchMore").removeClass("is-active"); + $selectedPeople.addClass("ms-u-slideDownIn20"); + setTimeout(function() { + $selectedPeople.removeClass("ms-u-slideDownIn20"); + }, 1000); + $peopleList.children(":gt(4)").hide(); + } + }); + + /** Show persona card when clicking a persona in the members list */ + $selectedPeople.on("click", ".ms-Persona", function() { + let selectedName = $(this).find(".ms-Persona-primaryText").html(); + let selectedTitle = $(this).find(".ms-Persona-secondaryText").html(); + let selectedInitials = (function() { + let name = selectedName.split(" "); + let nameInitials = ""; + + for (let i = 0; i < name.length; i++) { + nameInitials += name[i].charAt(0); + } + + return nameInitials.substring(0, 2); + })(); + let selectedClasses = $(this).find(".ms-Persona-initials").attr("class"); + let selectedImage = $(this).find(".ms-Persona-image").attr("src"); + let $card = $(".ms-PersonaCard"); + let $cardName = $card.find(".ms-Persona-primaryText"); + let $cardTitle = $card.find(".ms-Persona-secondaryText"); + let $cardInitials = $card.find(".ms-Persona-initials"); + let $cardImage = $card.find(".ms-Persona-image"); + + /** Close any open persona cards */ + $personaCard.removeClass("is-active"); + + /** Add data to persona card */ + $cardName.text(selectedName); + $cardTitle.text(selectedTitle); + $cardInitials.text(selectedInitials); + $cardInitials.removeClass(); + $cardInitials.addClass(selectedClasses); + $cardImage.attr("src", selectedImage); + + /** Show persona card */ + setTimeout(function() { + $personaCard.addClass("is-active"); + setTimeout(function(){ + $personaCard.css({"animation-name": "none"}); + }, 300); + }, 100); + + /** Align persona card on md and above screens */ + if ($(window).width() > 480) { + let itemPositionTop = $(this).offset().top; + let correctedPositionTop = itemPositionTop + 10; + + $personaCard.css({"top": correctedPositionTop, "left": 0}); + } else { + $personaCard.css({"top": "auto"}); + } + }); + + /** Dismiss persona card when clicking on the document */ + $(document).on("click", function(e) { + let $memberBtn = $(".ms-PeoplePicker-selectedPerson").find(".ms-Persona"); + + if (!$memberBtn.is(e.target) && !$personaCard.is(e.target) && $personaCard.has(e.target).length === 0) { + $personaCard.removeClass("is-active"); + setTimeout(function(){ + $personaCard.removeAttr("style"); + }, 300); + } else { + $personaCard.addClass("is-active"); + } + }); + + } + } +} diff --git a/src/components/PeoplePicker/PeoplePicker.json b/src/components/PeoplePicker/PeoplePicker.json index 61d741ac..daff3bab 100644 --- a/src/components/PeoplePicker/PeoplePicker.json +++ b/src/components/PeoplePicker/PeoplePicker.json @@ -9,7 +9,8 @@ "Label", "Overlay", "Persona", - "PersonaCard" + "PersonaCard", + "Spinner" ], "wrapBranches": true, "fileOrder": [ diff --git a/src/components/PersonaCard/Jquery.PersonaCard.js b/src/components/PersonaCard/Jquery.PersonaCard.js deleted file mode 100644 index 588a8dde..00000000 --- a/src/components/PersonaCard/Jquery.PersonaCard.js +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Persona Card Plugin - * - * Adds basic demonstration functionality to .ms-PersonaCard components. - * - * @param {jQuery Object} One or more .ms-PersonaCard components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.PersonaCard = function () { - - /** Go through each file picker we've been given. */ - return this.each(function () { - - var $personaCard = $(this); - - /** When selecting an action, show its details. */ - $personaCard.on('click', '.ms-PersonaCard-action', function() { - - /** Select the correct tab. */ - $personaCard.find('.ms-PersonaCard-action').removeClass('is-active'); - $(this).addClass('is-active'); - - /** Function for switching selected item into view by adding a class to ul. */ - var updateForItem = function(wrapper, item) { - var previousItem = wrapper.className + ""; - var detail = item.charAt(0).toUpperCase() + item.slice(1); - var nextItem = "ms-PersonaCard-detail" + detail; - if (previousItem !== nextItem){ - wrapper.classList.remove(previousItem); - wrapper.classList.add(nextItem); - } - }; - - /** Get id of selected item */ - var el = $(this).attr('id'); - /** Add detail class to ul to switch it into view. */ - updateForItem($(this).parent().next().find('#detailList')[0], el); - - /** Display the corresponding details. */ - var requestedAction = $(this).attr('id'); - $personaCard.find('.ms-PersonaCard-actionDetails').removeClass('is-active'); - $personaCard.find('#' + requestedAction + '.ms-PersonaCard-actionDetails').addClass('is-active'); - - }); - - /** Toggle more details. */ - $personaCard.on('click', '.ms-PersonaCard-detailExpander', function() { - $(this).parent('.ms-PersonaCard-actionDetails').toggleClass('is-collapsed'); - }); - - }); - - }; -})(jQuery); diff --git a/src/components/PersonaCard/Jquery.PersonaCard.ts b/src/components/PersonaCard/Jquery.PersonaCard.ts new file mode 100644 index 00000000..2e7d0487 --- /dev/null +++ b/src/components/PersonaCard/Jquery.PersonaCard.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + /** + * Persona Card Plugin + * + * Adds basic demonstration functionality to .ms-PersonaCard components. + * + */ + export class PersonaCard { + + /** + * + * @param {HTMLElement} container - the target container for an instance of PersonaCard + * @constructor + */ + constructor(container: HTMLElement) { + let $personaCard = $(container); + + /** When selecting an action, show its details. */ + $personaCard.on("click", ".ms-PersonaCard-action", function() { + + /** Select the correct tab. */ + $personaCard.find(".ms-PersonaCard-action").removeClass("is-active"); + $(this).addClass("is-active"); + + /** Function for switching selected item into view by adding a class to ul. */ + let updateForItem = function(wrapper, item) { + let previousItem = wrapper.className + ""; + let detail = item.charAt(0).toUpperCase() + item.slice(1); + let nextItem = "ms-PersonaCard-detail" + detail; + if (previousItem !== nextItem) { + wrapper.classList.remove(previousItem); + wrapper.classList.add(nextItem); + } + }; + + /** Get id of selected item */ + let el = $(this).attr("id"); + /** Add detail class to ul to switch it into view. */ + updateForItem($(this).parent().next().find("#detailList")[0], el); + + /** Display the corresponding details. */ + let requestedAction = $(this).attr("id"); + $personaCard.find(".ms-PersonaCard-actionDetails").removeClass("is-active"); + $personaCard.find("#" + requestedAction + ".ms-PersonaCard-actionDetails").addClass("is-active"); + + }); + + /** Toggle more details. */ + $personaCard.on("click", ".ms-PersonaCard-detailExpander", function() { + $(this).parent(".ms-PersonaCard-actionDetails").toggleClass("is-collapsed"); + }); + } + } +} diff --git a/src/components/Pivot/jquery.Pivot.js b/src/components/Pivot/jquery.Pivot.js deleted file mode 100644 index e5e44bb0..00000000 --- a/src/components/Pivot/jquery.Pivot.js +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Pivot Plugin - * - * Adds basic demonstration functionality to .ms-Pivot components. - * - * @param {jQuery Object} One or more .ms-Pivot components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.Pivot = function () { - - /** Go through each pivot we've been given. */ - return this.each(function () { - - var $pivotContainer = $(this); - - /** When clicking/tapping a link, select it. */ - $pivotContainer.on('click', '.ms-Pivot-link', function(event) { - event.preventDefault(); - /** Check if current selection has elipses child element **/ - var $elipsisCheck = $(this).find('.ms-Pivot-ellipsis'); - - /** Only execute when no elipses element can be found **/ - if($elipsisCheck.length === 0){ - - $(this).siblings('.ms-Pivot-link').removeClass('is-selected'); - $(this).addClass('is-selected'); - } - - }); - - }); - - }; -})(jQuery); diff --git a/src/components/Pivot/jquery.Pivot.ts b/src/components/Pivot/jquery.Pivot.ts new file mode 100644 index 00000000..dcf30a6f --- /dev/null +++ b/src/components/Pivot/jquery.Pivot.ts @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + + /** + * Pivot Plugin + * + * Adds basic demonstration functionality to .ms-Pivot components. + * + */ + export class Pivot { + + /** + * + * @param {HTMLElement} container - the target container for an instance of Pivot + * @constructor + */ + constructor(container: HTMLElement) { + let $pivotContainer = $(container); + + /** When clicking/tapping a link, select it. */ + $pivotContainer.on("click", ".ms-Pivot-link", function(event) { + event.preventDefault(); + /** Check if current selection has elipses child element **/ + let $elipsisCheck = $(this).find(".ms-Pivot-ellipsis"); + + /** Only execute when no elipses element can be found **/ + if ($elipsisCheck.length === 0) { + + $(this).siblings(".ms-Pivot-link").removeClass("is-selected"); + $(this).addClass("is-selected"); + } + }); + } + } +} diff --git a/src/components/ProgressIndicator/ProgressIndicator.js b/src/components/ProgressIndicator/ProgressIndicator.js deleted file mode 100644 index d70504b1..00000000 --- a/src/components/ProgressIndicator/ProgressIndicator.js +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * ProgressIndicator component - * - * A component for outputting determinate progress - * - */ - -/** - * @namespace fabric - */ -var fabric = fabric || {}; -/** - * - * @param {HTMLDivElement} container - the target container for an instance of ProgressIndicator - * @constructor - */ -fabric.ProgressIndicator = function(container) { - this.container = container; - this.cacheDOM(); -}; - -fabric.ProgressIndicator.prototype = (function() { - - var _progress; - var _width; - var _itemName; - var _total; - var _itemDescription; - var _progressBar; - - /** - * Sets the progress percentage for a determinate - * operation. Either use this or setProgress - * and setTotal as setProgressPercent assumes - * you've done a percentage calculation before - * injecting it into the function - * @param {number} percent - a floating point number from 0 to 1 - */ - var setProgressPercent = function(percent) { - _progressBar.style.width = Math.round(_width * percent) + "px"; - }; - - /** - * Sets the progress for a determinate operation. - * Use this in combination with setTotal. - * @param {number} progress - */ - var setProgress = function(progress) { - _progress = progress; - var percentage = _progress / _total; - this.setProgressPercent(percentage); - }; - - /** - * Sets the total file size, etc. for a - * determinate operation. Use this in - * combination with setProgress - * @param {number} total - */ - var setTotal = function(total) { - _total = total; - }; - - /** - * Sets the text for the title or label - * of an instance - * @param {string} name - */ - var setName = function(name) { - _itemName.innerHTML = name; - }; - - /** - * Sets the text for a description - * of an instance - * @param {string} name - */ - var setDescription = function(description) { - _itemDescription.innerHTML = description; - }; - - /** - * caches elements and values of the component - * - */ - var cacheDOM = function() { - _itemName = this.container.querySelector('.ms-ProgressIndicator-itemName') || null; //an itemName element is optional - _itemDescription = this.container.querySelector('.ms-ProgressIndicator-itemDescription') || null; //an itemDescription element is optional - _progressBar = this.container.querySelector('.ms-ProgressIndicator-progressBar'); - _width = this.container.querySelector('.ms-ProgressIndicator-itemProgress').offsetWidth; - }; - - return { - setProgressPercent: setProgressPercent, - setName: setName, - setDescription: setDescription, - setProgress: setProgress, - setTotal: setTotal, - cacheDOM: cacheDOM - }; -}()); diff --git a/src/components/ProgressIndicator/ProgressIndicator.ts b/src/components/ProgressIndicator/ProgressIndicator.ts new file mode 100644 index 00000000..77bd370b --- /dev/null +++ b/src/components/ProgressIndicator/ProgressIndicator.ts @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. + +/** + * @namespace fabric + */ +namespace fabric { + "use strict"; + + /** + * ProgressIndicator component + * + * A component for outputting determinate progress + * + */ + export class ProgressIndicator { + + public container: HTMLElement; + + private _progress: number; + private _width: number; + private _itemName: HTMLElement; + private _total: number; + private _itemDescription: HTMLElement; + private _progressBar: HTMLElement; + + /** + * + * @param {HTMLDivElement} container - the target container for an instance of ProgressIndicator + * @constructor + */ + constructor(container: HTMLElement) { + this.container = container; + this.cacheDOM(); + } + + /** + * Sets the progress percentage for a determinate + * operation. Either use this or setProgress + * and setTotal as setProgressPercent assumes + * you've done a percentage calculation before + * injecting it into the function + * @param {number} percent - a floating point number from 0 to 1 + */ + public setProgressPercent(percent: number): void { + this._progressBar.style.width = Math.round(this._width * percent) + "px"; + } + + /** + * Sets the progress for a determinate operation. + * Use this in combination with setTotal. + * @param {number} progress + */ + public setProgress(progress: number): void { + this._progress = progress; + let percentage = this._progress / this._total; + this.setProgressPercent(percentage); + } + + /** + * Sets the total file size, etc. for a + * determinate operation. Use this in + * combination with setProgress + * @param {number} total + */ + public setTotal(total: number): void { + this._total = total; + } + + /** + * Sets the text for the title or label + * of an instance + * @param {string} name + */ + public setName(name: string): void { + this._itemName.innerHTML = name; + } + + /** + * Sets the text for a description + * of an instance + * @param {string} name + */ + public setDescription(description: string): void { + this._itemDescription.innerHTML = description; + } + + /** + * caches elements and values of the component + * + */ + public cacheDOM(): void { + // an itemName element is optional + this._itemName = this.container.querySelector(".ms-ProgressIndicator-itemName") || null; + // an itemDescription element is optional + this._itemDescription = this.container.querySelector(".ms-ProgressIndicator-itemDescription") || null; + this._progressBar = this.container.querySelector(".ms-ProgressIndicator-progressBar"); + let itemProgress = this.container.querySelector(".ms-ProgressIndicator-itemProgress"); + this._width = itemProgress.offsetWidth; + } + } +} // end fabric namespace diff --git a/src/components/SearchBox/Jquery.SearchBox.js b/src/components/SearchBox/Jquery.SearchBox.js deleted file mode 100644 index c95c6029..00000000 --- a/src/components/SearchBox/Jquery.SearchBox.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * SearchBox Plugin - * - * Adds basic demonstration functionality to .ms-SearchBox components. - * - * @param {jQuery Object} One or more .ms-SearchBox components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.SearchBox = function () { - - /** Iterate through each text field provided. */ - return this.each(function () { - // Set cancel to false - var cancel = false; - var $searchField = $(this).find('.ms-SearchBox-field'); - - /** SearchBox focus - hide label and show cancel button */ - $searchField.on('focus', function() { - /** Hide the label on focus. */ - $(this).siblings('.ms-SearchBox-label').hide(); - // Show cancel button by adding is-active class - $(this).parent('.ms-SearchBox').addClass('is-active'); - }); - - /** 'hovering' class allows for more fine grained control of hover state */ - $searchField.on('mouseover', function() { - $searchField.addClass('hovering'); - }); - - $searchField.on('mouseout', function() { - $searchField.removeClass('hovering'); - }); - - // If cancel button is selected, change cancel value to true - $(this).find('.ms-SearchBox-closeButton').on('mousedown', function() { - cancel = true; - }); - - /** Show the label again when leaving the field. */ - $(this).find('.ms-SearchBox-field').on('blur', function() { - - // If cancel button is selected remove the text and show the label - if (cancel) { - $(this).val(''); - $searchField.addClass('hovering'); - } - - var $searchBox = $(this).parent('.ms-SearchBox'); - // Prevents inputfield from gaining focus too soon - setTimeout(function() { - // Remove is-active class - hides cancel button - $searchBox.removeClass('is-active'); - }, 10); - - /** Only do this if no text was entered. */ - if ($(this).val().length === 0 ) { - $(this).siblings('.ms-SearchBox-label').show(); - } - - // Reset cancel to false - cancel = false; - }); - }); - - }; -})(jQuery); diff --git a/src/components/SearchBox/Jquery.SearchBox.ts b/src/components/SearchBox/Jquery.SearchBox.ts new file mode 100644 index 00000000..77f67270 --- /dev/null +++ b/src/components/SearchBox/Jquery.SearchBox.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + + + /** + * SearchBox Plugin + * + * Adds basic demonstration functionality to .ms-SearchBox components. + */ + export class SearchBox { + + /** + * + * @param {HTMLDivElement} container - the target container for an instance of SearchBox + * @constructor + */ + constructor(container: HTMLElement) { + + let $searchBox = $(container); + + // Set cancel to false + let cancel = false; + let $searchField = $searchBox.find(".ms-SearchBox-field"); + + /** SearchBox focus - hide label and show cancel button */ + $searchField.on("focus", function() { + /** Hide the label on focus. */ + $(this).siblings(".ms-SearchBox-label").hide(); + // Show cancel button by adding is-active class + $(this).parent(".ms-SearchBox").addClass("is-active"); + }); + + /** "hovering" class allows for more fine grained control of hover state */ + $searchField.on("mouseover", function() { + $searchField.addClass("hovering"); + }); + + $searchField.on("mouseout", function() { + $searchField.removeClass("hovering"); + }); + + // If cancel button is selected, change cancel value to true + $searchBox.find(".ms-SearchBox-closeButton").on("mousedown", function() { + cancel = true; + }); + + /** Show the label again when leaving the field. */ + $searchBox.find(".ms-SearchBox-field").on("blur", function() { + + // If cancel button is selected remove the text and show the label + if (cancel) { + $(this).val(""); + $searchField.addClass("hovering"); + } + + // Prevents inputfield from gaining focus too soon + setTimeout(function() { + // Remove is-active class - hides cancel button + $searchBox.removeClass("is-active"); + }, 10); + + /** Only do this if no text was entered. */ + if ($(this).val().length === 0 ) { + $(this).siblings(".ms-SearchBox-label").show(); + } + + // Reset cancel to false + cancel = false; + }); + } + } +} diff --git a/src/components/Spinner/Spinner.js b/src/components/Spinner/Spinner.js deleted file mode 100644 index 1c4364d7..00000000 --- a/src/components/Spinner/Spinner.js +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Spinner Component - * - * An animating activity indicator. - * - */ - -/** - * @namespace fabric - */ -var fabric = fabric || {}; - -/** - * @param {HTMLDOMElement} target - The element the Spinner will attach itself to. - */ - -fabric.Spinner = function(target) { - - var _target = target; - var eightSize = 0.2; - var circleObjects = []; - var animationSpeed = 90; - var interval; - var spinner; - var numCircles; - var offsetSize; - var fadeIncrement = 0; - var parentSize = 20; - - /** - * @function start - starts or restarts the animation sequence - * @memberOf fabric.Spinner - */ - function start() { - stop(); - interval = setInterval(function() { - var i = circleObjects.length; - while(i--) { - _fade(circleObjects[i]); - } - }, animationSpeed); - } - - /** - * @function stop - stops the animation sequence - * @memberOf fabric.Spinner - */ - function stop() { - clearInterval(interval); - } - - //private methods - - function _init() { - _setTargetElement(); - _setPropertiesForSize(); - _createCirclesAndArrange(); - _initializeOpacities(); - start(); - } - - function _initializeOpacities() { - var i = 0; - var j = 1; - var opacity; - fadeIncrement = 1 / numCircles; - - for (i; i < numCircles; i++) { - var circleObject = circleObjects[i]; - opacity = (fadeIncrement * j++); - _setOpacity(circleObject.element, opacity); - } - } - - function _fade(circleObject) { - var opacity = _getOpacity(circleObject.element) - fadeIncrement; - - if (opacity <= 0) { - opacity = 1; - } - - _setOpacity(circleObject.element, opacity); - } - - function _getOpacity(element) { - return parseFloat(window.getComputedStyle(element).getPropertyValue("opacity")); - } - - function _setOpacity(element, opacity) { - element.style.opacity = opacity; - } - - function _createCircle() { - var circle = document.createElement('div'); - circle.className = "ms-Spinner-circle"; - circle.style.width = circle.style.height = parentSize * offsetSize + "px"; - return circle; - } - - function _createCirclesAndArrange() { - - var angle = 0; - var offset = parentSize * offsetSize; - var step = (2 * Math.PI) / numCircles; - var i = numCircles; - var circleObject; - var radius = (parentSize - offset) * 0.5; - - while (i--) { - var circle = _createCircle(); - var x = Math.round(parentSize * 0.5 + radius * Math.cos(angle) - circle.clientWidth * 0.5) - offset * 0.5; - var y = Math.round(parentSize * 0.5 + radius * Math.sin(angle) - circle.clientHeight * 0.5) - offset * 0.5; - spinner.appendChild(circle); - circle.style.left = x + 'px'; - circle.style.top = y + 'px'; - angle += step; - circleObject = { element:circle, j:i }; - circleObjects.push(circleObject); - } - } - - function _setPropertiesForSize() { - if (spinner.className.indexOf("large") > -1) { - parentSize = 28; - eightSize = 0.179; - } - - offsetSize = eightSize; - numCircles = 8; - } - - function _setTargetElement() { - //for backwards compatibility - if (_target.className.indexOf("ms-Spinner") === -1) { - spinner = document.createElement("div"); - spinner.className = "ms-Spinner"; - _target.appendChild(spinner); - } else { - spinner = _target; - } - } - - _init(); - - return { - start:start, - stop:stop - }; -}; diff --git a/src/components/Spinner/Spinner.ts b/src/components/Spinner/Spinner.ts new file mode 100644 index 00000000..08328b73 --- /dev/null +++ b/src/components/Spinner/Spinner.ts @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. + +/** + * @namespace fabric + */ +namespace fabric { + + class CircleObject { + public element: HTMLElement; + public j: number; + + constructor(element, j) { + this.element = element; + this.j = j; + } + } + + /** + * Spinner Component + * + * An animating activity indicator. + * + */ + export class Spinner { + + public eightSize: number = 0.2; + public animationSpeed: number = 90; + public interval: number; + public spinner: HTMLElement; + public numCircles: number; + public offsetSize: number; + public parentSize: number = 20; + public fadeIncrement: number = 0; + + private circleObjects: Array = []; + private _target: HTMLElement; + + /** + * @param {HTMLDOMElement} target - The element the Spinner will attach itself to. + */ + constructor(container: HTMLElement) { + this._target = container; + this._init(); + } + + /** + * @function start - starts or restarts the animation sequence + * @memberOf fabric.Spinner + */ + public start(): void { + this.stop(); + this.interval = setInterval(() => { + let i = this.circleObjects.length; + while (i--) { + this._fade(this.circleObjects[i]); + } + }, this.animationSpeed); + } + + /** + * @function stop - stops the animation sequence + * @memberOf fabric.Spinner + */ + public stop(): void { + clearInterval(this.interval); + } + + // private methods + + private _init(): void { + this._setTargetElement(); + this._setPropertiesForSize(); + this._createCirclesAndArrange(); + this._initializeOpacities(); + this.start(); + } + + private _setPropertiesForSize(): void { + if (this.spinner.className.indexOf("large") > -1) { + this.parentSize = 28; + this.eightSize = 0.179; + } + + this.offsetSize = this.eightSize; + this.numCircles = 8; + } + + private _setTargetElement(): void { + // for backwards compatibility + if (this._target.className.indexOf("ms-Spinner") === -1) { + this.spinner = document.createElement("div"); + this.spinner.className = "ms-Spinner"; + this._target.appendChild(this.spinner); + } else { + this.spinner = this._target; + } + } + + private _initializeOpacities(): void { + let i = 0; + let j = 1; + let opacity; + this.fadeIncrement = 1 / this.numCircles; + + for (i; i < this.numCircles; i++) { + let circleObject = this.circleObjects[i]; + opacity = (this.fadeIncrement * j++); + this._setOpacity(circleObject.element, opacity); + } + } + + private _fade(circleObject: CircleObject): void { + let opacity = this._getOpacity(circleObject.element) - this.fadeIncrement; + + if (opacity <= 0) { + opacity = 1; + } + + this._setOpacity(circleObject.element, opacity); + } + + private _getOpacity(element: HTMLElement): number { + return parseFloat(window.getComputedStyle(element).getPropertyValue("opacity")); + } + + private _setOpacity(element: HTMLElement, opacity: number): void { + element.style.opacity = opacity.toString(); + } + + private _createCircle(): HTMLElement { + let circle = document.createElement("div"); + circle.className = "ms-Spinner-circle"; + circle.style.width = circle.style.height = this.parentSize * this.offsetSize + "px"; + return circle; + } + + private _createCirclesAndArrange(): void { + + let angle = 0; + let offset = this.parentSize * this.offsetSize; + let step = (2 * Math.PI) / this.numCircles; + let i = this.numCircles; + let circleObject; + let radius = (this.parentSize - offset) * 0.5; + + while (i--) { + let circle = this._createCircle(); + let x = Math.round(this.parentSize * 0.5 + radius * Math.cos(angle) - circle.clientWidth * 0.5) - offset * 0.5; + let y = Math.round(this.parentSize * 0.5 + radius * Math.sin(angle) - circle.clientHeight * 0.5) - offset * 0.5; + this.spinner.appendChild(circle); + circle.style.left = x + "px"; + circle.style.top = y + "px"; + angle += step; + circleObject = new CircleObject(circle, i); + this.circleObjects.push(circleObject); + } + } + } +} diff --git a/src/components/TextField/Jquery.TextField.js b/src/components/TextField/Jquery.TextField.js deleted file mode 100644 index 4b7094c2..00000000 --- a/src/components/TextField/Jquery.TextField.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. - -/** - * Text Field Plugin - * - * Adds basic demonstration functionality to .ms-TextField components. - * - * @param {jQuery Object} One or more .ms-TextField components - * @return {jQuery Object} The same components (allows for chaining) - */ -(function ($) { - $.fn.TextField = function () { - - /** Iterate through each text field provided. */ - return this.each(function () { - - /** Does it have a placeholder? */ - if ($(this).hasClass("ms-TextField--placeholder")) { - - /** Hide the label on click. */ - $(this).on('click', function () { - $(this).find('.ms-Label').hide(); - }); - - /** Show the label again when leaving the field. */ - $(this).find('.ms-TextField-field').on('blur', function () { - - /** Only do this if no text was entered. */ - if ($(this).val().length === 0) { - $(this).siblings('.ms-Label').show(); - } - }); - } - - /** Underlined - adding/removing a focus class */ - if ($(this).hasClass('ms-TextField--underlined')) { - - /** Add is-active class - changes border color to theme primary */ - $(this).find('.ms-TextField-field').on('focus', function() { - $(this).parent('.ms-TextField--underlined').addClass('is-active'); - }); - - /** Remove is-active on blur of textfield */ - $(this).find('.ms-TextField-field').on('blur', function() { - $(this).parent('.ms-TextField--underlined').removeClass('is-active'); - }); - } - - }); - }; -})(jQuery); diff --git a/src/components/TextField/Jquery.TextField.ts b/src/components/TextField/Jquery.TextField.ts new file mode 100644 index 00000000..2e47d348 --- /dev/null +++ b/src/components/TextField/Jquery.TextField.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information. +// @TODO - we can add this once jquery is removed +// "use strict"; + +/// + +namespace fabric { + + /** + * Text Field Plugin + * + * Adds basic demonstration functionality to .ms-TextField components. + */ + export class TextField { + + /** + * + * @param {HTMLDivElement} container - the target container for an instance of TextField + * @constructor + */ + constructor(container: HTMLElement) { + let $textField = $(container); + + /** Does it have a placeholder? */ + if ($textField.hasClass("ms-TextField--placeholder")) { + + /** Hide the label on click. */ + $textField.on("click", function () { + $textField.find(".ms-Label").hide(); + }); + + /** Show the label again when leaving the field. */ + $textField.find(".ms-TextField-field").on("blur", function () { + + /** Only do this if no text was entered. */ + if ($textField.val().length === 0) { + $(this).siblings(".ms-Label").show(); + } + }); + } + + /** Underlined - adding/removing a focus class */ + if ($textField.hasClass("ms-TextField--underlined")) { + + /** Add is-active class - changes border color to theme primary */ + $textField.find(".ms-TextField-field").on("focus", function() { + $(this).parent(".ms-TextField--underlined").addClass("is-active"); + }); + + /** Remove is-active on blur of textfield */ + $textField.find(".ms-TextField-field").on("blur", function() { + $(this).parent(".ms-TextField--underlined").removeClass("is-active"); + }); + } + } + } +} diff --git a/src/templates/componentSampleTemplate.html b/src/templates/componentSampleTemplate.html index a8ce9911..df99d254 100644 --- a/src/templates/componentSampleTemplate.html +++ b/src/templates/componentSampleTemplate.html @@ -11,22 +11,11 @@ - + + + - <%= jsLinks %> -