From 13f6202aefd382cddee9dae9caa7185abcb0e579 Mon Sep 17 00:00:00 2001 From: Maw-Fox Date: Mon, 12 Oct 2015 17:39:20 -0600 Subject: [PATCH] Added initial definition and test file for the Sortable library. --- sortable/sortable-tests.ts | 311 +++++++++++++++++++++++++++++++++++++ sortable/sortable.d.ts | 208 +++++++++++++++++++++++++ 2 files changed, 519 insertions(+) create mode 100755 sortable/sortable-tests.ts create mode 100755 sortable/sortable.d.ts diff --git a/sortable/sortable-tests.ts b/sortable/sortable-tests.ts new file mode 100755 index 000000000..cc2b2cdcd --- /dev/null +++ b/sortable/sortable-tests.ts @@ -0,0 +1,311 @@ +// Examples from project repo used for tests. + +/// + +var simpleList = document.getElementById('list'); +var list = simpleList; +var el = document.getElementById('el'); +var sortable = new Sortable(simpleList, {}); +var order = sortable.toArray(); +var angular: any; +var Ply: any; + +sortable.sort(order.reverse()); + +Sortable.create(list, { + delay: 500, + chosenClass: "chosen" +}); + +Sortable.create(el, { + handle: ".my-handle" +}); + +Sortable.create(list, { + filter: ".js-remove, .js-edit", + onFilter: function(event) { + var item = event.item, + control = event.target; + + if (Sortable.utils.is(control, ".js-remove")) { + item.parentNode.removeChild(item); + } + else if (Sortable.utils.is(control, ".js-edit")) { + // .. + } + } +}); + +Sortable.create(el, { + group: "localStorage-example", + store: { + get: function(sortable) { + var order = localStorage.getItem(sortable.options.group); + + return order ? order.split('|') : []; + }, + set: function(sortable) { + var order = sortable.toArray(); + + localStorage.setItem(sortable.options.group, order.join('|')); + } + } +}); + +Sortable.create(simpleList, { + forceFallback: true +}); + +Sortable.create(simpleList, { + ghostClass: 'ghost' +}); + +simpleList.innerHTML = Array.apply(null, new Array(100)).map(function(value, iterator) { + return `
item ${iterator + 1}
`; +}).join(''); + +Sortable.create(simpleList, { + delay: 500, + chosenClass: 'chosen' +}); + +simpleList.innerHTML = Array.apply(null, new Array(10)).map(function(v, i) { + return '
item ' + + (i + 1) + + '
'; +}).join(''); + +Sortable.create(simpleList, {}); + +simpleList.innerHTML = Array.apply(null, new Array(100)).map(function(v, i) { + return '
item ' + + (i + 1) + + '
'; +}).join(''); + +(function() { + 'use strict'; + + var byId = function(id) { return document.getElementById(id); }, + + loadScripts = function(desc, callback) { + var deps = [], key, idx = 0; + + for (key in desc) { + deps.push(key); + } + + (function _next() { + var pid, + name = deps[idx], + script = document.createElement('script'); + + script.type = 'text/javascript'; + script.src = desc[deps[idx]]; + + pid = setInterval(function() { + if (window[name]) { + clearTimeout(pid); + + deps[idx++] = window[name]; + + if (deps[idx]) { + _next(); + } else { + callback.apply(null, deps); + } + } + }, 30); + + document.getElementsByTagName('head')[0].appendChild(script); + })() + }, + + console = window.console; + + + if (!console.log) { + console.log = function() { + alert([].join.apply(arguments, ' ')); + }; + } + + + Sortable.create(byId('foo'), { + group: "words", + animation: 150, + store: { + get: function(sortable) { + var order = localStorage.getItem(sortable.options.group); + return order ? order.split('|') : []; + }, + set: function(sortable) { + var order = sortable.toArray(); + localStorage.setItem(sortable.options.group, order.join('|')); + } + }, + onAdd: function(evt) { console.log('onAdd.foo:', [evt.item, evt.from]); }, + onUpdate: function(evt) { console.log('onUpdate.foo:', [evt.item, evt.from]); }, + onRemove: function(evt) { console.log('onRemove.foo:', [evt.item, evt.from]); }, + onStart: function(evt) { console.log('onStart.foo:', [evt.item, evt.from]); }, + onSort: function(evt) { console.log('onStart.foo:', [evt.item, evt.from]); }, + onEnd: function(evt) { console.log('onEnd.foo:', [evt.item, evt.from]); } + }); + + + Sortable.create(byId('bar'), { + group: "words", + animation: 150, + onAdd: function(evt) { console.log('onAdd.bar:', evt.item); }, + onUpdate: function(evt) { console.log('onUpdate.bar:', evt.item); }, + onRemove: function(evt) { console.log('onRemove.bar:', evt.item); }, + onStart: function(evt) { console.log('onStart.foo:', evt.item); }, + onEnd: function(evt) { console.log('onEnd.foo:', evt.item); } + }); + + + // Multi groups + Sortable.create(byId('multi'), { + animation: 150, + draggable: '.tile', + handle: '.tile__name' + }); + + [].forEach.call(byId('multi').getElementsByClassName('tile__list'), function(el) { + Sortable.create(el, { + group: 'photo', + animation: 150 + }); + }); + + + // Editable list + var editableList = Sortable.create(byId('editable'), { + animation: 150, + filter: '.js-remove', + onFilter: function(evt) { + evt.item.parentNode.removeChild(evt.item); + } + }); + + + byId('addUser').onclick = function() { + Ply.dialog('prompt', { + title: 'Add', + form: { name: 'name' } + }).done(function(ui) { + var el = document.createElement('li'); + el.innerHTML = ui.data.name + ''; + editableList.el.appendChild(el); + }); + }; + + + // Advanced groups + [{ + name: 'advanced', + pull: true, + put: true + }, + { + name: 'advanced', + pull: 'clone', + put: false + }, { + name: 'advanced', + pull: false, + put: true + }].forEach(function(groupOpts, i) { + Sortable.create(byId('advanced-' + (i + 1)), { + sort: (i != 1), + group: groupOpts, + animation: 150 + }); + }); + + + // 'handle' option + Sortable.create(byId('handle-1'), { + handle: '.drag-handle', + animation: 150 + }); + + + // Angular example + angular.module('todoApp', ['ng-sortable']) + .constant('ngSortableConfig', { + onEnd: function() { + console.log('default onEnd()'); + } + }) + .controller('TodoController', ['$scope', function($scope) { + $scope.todos = [ + { text: 'learn angular', done: true }, + { text: 'build an angular app', done: false } + ]; + + $scope.addTodo = function() { + $scope.todos.push({ text: $scope.todoText, done: false }); + $scope.todoText = ''; + }; + + $scope.remaining = function() { + var count = 0; + angular.forEach($scope.todos, function(todo) { + count += todo.done ? 0 : 1; + }); + return count; + }; + + $scope.archive = function() { + var oldTodos = $scope.todos; + $scope.todos = []; + angular.forEach(oldTodos, function(todo) { + if (!todo.done) $scope.todos.push(todo); + }); + }; + }]) + .controller('TodoControllerNext', ['$scope', function($scope) { + $scope.todos = [ + { text: 'learn Sortable', done: true }, + { text: 'use ng-sortable', done: false }, + { text: 'Enjoy', done: false } + ]; + + $scope.remaining = function() { + var count = 0; + angular.forEach($scope.todos, function(todo) { + count += todo.done ? 0 : 1; + }); + return count; + }; + + $scope.sortableConfig = { group: 'todo', animation: 150 }; + 'Start End Add Update Remove Sort'.split(' ').forEach(function(name) { + $scope.sortableConfig['on' + name] = console.log.bind(console, name); + }); + }]); +})(); + +// Background +document.addEventListener("DOMContentLoaded", function() { + function setNoiseBackground(el, width, height, opacity) { + var canvas = document.createElement("canvas"); + var context = canvas.getContext("2d"); + + canvas.width = width; + canvas.height = height; + + for (var i = 0; i < width; i++) { + for (var j = 0; j < height; j++) { + var val = Math.floor(Math.random() * 255); + context.fillStyle = "rgba(" + val + "," + val + "," + val + "," + opacity + ")"; + context.fillRect(i, j, 1, 1); + } + } + + el.style.background = "url(" + canvas.toDataURL("image/png") + ")"; + } + + setNoiseBackground(document.getElementsByTagName('body')[0], 50, 50, 0.02); +}, false); diff --git a/sortable/sortable.d.ts b/sortable/sortable.d.ts new file mode 100755 index 000000000..f52272659 --- /dev/null +++ b/sortable/sortable.d.ts @@ -0,0 +1,208 @@ +// Type definitions for Sortable.js v1.3.0-rc1 +// Project: https://github.com/RubaXa/Sortable +// Definitions by: Maw-Fox +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module Sortablejs { + interface SortableOptions { + group?: any; + sort?: boolean; + delay?: number; + disabled?: boolean; + store?: { + get: (sortable: Sortable) => any[]; + set: (sortable: Sortable) => any; + }; + animation?: number; + handle?: string; + filter?: any; + draggable?: string; + ghostClass?: string; + chosenClass?: string; + dataIdAttr?: string; + forceFallback?: boolean; + fallbackClass?: string; + fallbackOnBody?: boolean; + scroll?: boolean; + scrollSensitivity?: number; + scrollSpeed?: number; + setData?: (dataTransfer: any, draggedElement: any) => any; + onStart?: (event: any) => any; + onEnd?: (event: any) => any; + onAdd?: (event: any) => any; + onUpdate?: (event: any) => any; + onSort?: (event: any) => any; + onRemove?: (event: any) => any; + onFilter?: (event: any) => any; + onMove?: (event: any) => boolean; + } + + interface SortableableUtils { + /** + * Attach an event handler function + * @param {HTMLElement} element an HTMLElement. + * @param {string} event an Event context. + * @param {Function} fn + */ + on(element: any, event: string, fn: (event: any) => any): void; + + /** + * Remove an event handler function + * @param {HTMLElement} element an HTMLElement. + * @param {string} event an Event context. + * @param {Function} fn a callback. + */ + off(element: any, event: string, fn: (event: any) => any): void; + + /** + * Get the values of all the CSS properties. + * @param {HTMLElement} element an HTMLElement. + * @returns {Object} + */ + css(element: any): any; + + /** + * Get the value of style properties. + * @param {HTMLElement} element an HTMLElement. + * @param {string} prop a property key. + * @returns {*} + */ + css(element: any, prop: string): any; + + /** + * Set one CSS property. + * @param {HTMLElement} element an HTMLElement. + * @param {string} prop a property key. + * @param {string} value a property value. + */ + css(element: any, prop: string, value: string): void; + + /** + * Set CSS properties. + * @param {HTMLElement} element an HTMLElement. + * @param {Object} props a properties object. + */ + css(element: any, props: any): void; + + /** + * Get elements by tag name. + * @param {HTMLElement} context an HTMLElement. + * @param {string} tagName A tag name. + * @param {function} [iterator] An iterator. + * @returns {HTMLElement[]} + */ + find(context: any, tagName: string, iterator?: (value: any) => any): any[]; + + /** + * Takes a function and returns a new one that will always have a particular context. + * @param {*} context an HTMLElement. + * @param {function} fn a function. + * @returns {function} + */ + bind(context: any, fn: () => any): () => any; + + /** + * Check the current matched set of elements against a selector. + * @param {HTMLElement} element an HTMLElement. + * @param {string} selector an element selector. + * @returns {boolean} + */ + is(element: any, selector: string): boolean; + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} element an HTMLElement. + * @param {string} selector an element seletor. + * @param {HTMLElement} [context] a specific element's context. + * @returns {HTMLElement} + */ + closest(element: any, selector: string, context?: any): any; + + /** + * Add or remove one classes from each element + * @param {HTMLElement} element an HTMLElement. + * @param {string} name a class name. + * @param {boolean} state a class's state. + */ + toggleClass(element: any, name: string, state: boolean): void; + } + + class DOMRect { + public bottom: number; + public height: number; + public left: number; + public right: number; + public top: number; + public width: number; + public x: number; + public y: number; + } + + class Sortable { + public options: SortableOptions; + public el: any; + + /** + * Sortable's main constructor. + * @param {HTMLElement} element Any variety of HTMLElement. + * @param {SortableOptions} options Sortable options object. + */ + constructor(element: any, options: SortableOptions); + + static active: Sortable; + static utils: SortableableUtils; + + /** + * Creation of new instances. + * @param {HTMLElement} element Any variety of HTMLElement. + * @param {SortableOptions} options Sortable options object. + * @returns {Sortable} + */ + static create(element: any, options: SortableOptions): Sortable; + + /** + * Options getter/setter + * @param {string} name a SortableOptions property. + * @param {*} [value] a Value. + * @returns {*} + */ + option(name: string, value: any): any; + option(name: string): any; + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {string|HTMLElement} element an HTMLElement or selector string. + * @returns {HTMLElement} + */ + closest(element: any): any; + + /** + * Sorts the elements according to the array. + * @param {string[]} order an array of strings to sort. + */ + sort(order: string[]): void; + + /** + * Saving and restoring of the sort. + */ + save(): void; + + /** + * Removes the sortable functionality completely. + */ + destroy(): void; + + /** + * Serializes the sortable's item data-id's (dataIdAttr option) into an array of string. + * @returns {string[]} + */ + toArray(): string[]; + } +} + +import Sortable = Sortablejs.Sortable; + +declare module 'Sortable' { + import Sortable = Sortablejs.Sortable; + export = Sortable; +}