-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
327 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "alpinejs", | ||
"version": "3.13.0", | ||
"version": "3.14.1", | ||
"type": "module", | ||
"exports": { | ||
".": { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "@alpinets/sort", | ||
"version": "0.0.1", | ||
"description": "The rugged, minimal TypeScript framework", | ||
"author": "Eric Kwoka <[email protected]> (https://thekwoka.net/)", | ||
"license": "MIT", | ||
"type": "module", | ||
"exports": { | ||
".": { | ||
"import": "./dist/index.js", | ||
"types": "./dist/index.d.ts" | ||
}, | ||
"./src": { | ||
"import": "./src/index.ts" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist", | ||
"src" | ||
], | ||
"scripts": { | ||
"build": "vite build", | ||
"coverage": "vitest run --coverage", | ||
"lint:types": "tsc --noEmit", | ||
"prebuild": "rm -rf dist", | ||
"test": "vitest" | ||
}, | ||
"dependencies": { | ||
"sortablejs": "1.15.2" | ||
}, | ||
"peerDependencies": { | ||
"@alpinets/alpinets": "workspace:^" | ||
}, | ||
"devDependencies": { | ||
"@types/sortablejs": "1.15.8", | ||
"vite": "5.3.5", | ||
"vitest": "2.0.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import type { PluginCallback } from '@alpinets/alpinets'; | ||
import Alpine from '@alpinets/alpinets'; | ||
import { ElementWithXAttributes } from '@alpinets/alpinets'; | ||
import { Utilities } from '@alpinets/alpinets/src/types'; | ||
// @ts-expect-error | ||
import Sortable from 'sortablejs'; | ||
|
||
export const Sort: PluginCallback = (Alpine) => { | ||
Alpine.directive( | ||
'sort', | ||
( | ||
el, | ||
{ value, modifiers, expression }, | ||
{ evaluate, evaluateLater, cleanup }, | ||
) => { | ||
if (value === 'config') { | ||
return; // This will get handled by the main directive... | ||
} | ||
|
||
if (value === 'handle') { | ||
return; // This will get handled by the main directive... | ||
} | ||
|
||
if (value === 'group') { | ||
return; // This will get handled by the main directive... | ||
} | ||
|
||
// Supporting both `x-sort:item` AND `x-sort:key` (key for BC)... | ||
if (value === 'key' || value === 'item') { | ||
if ([undefined, null, ''].includes(expression)) return; | ||
|
||
el._x_sort_key = evaluate(expression); | ||
|
||
return; | ||
} | ||
|
||
const preferences = { | ||
hideGhost: !modifiers.includes('ghost'), | ||
useHandles: !!el.querySelector('[x-sort\\:handle]'), | ||
group: getGroupName(el, modifiers), | ||
}; | ||
|
||
const handleSort = generateSortHandler(expression, evaluateLater); | ||
|
||
const config = getConfigurationOverrides(el, modifiers, evaluate); | ||
|
||
const sortable = initSortable( | ||
el, | ||
config, | ||
preferences, | ||
(key, position) => { | ||
handleSort(key, position); | ||
}, | ||
); | ||
|
||
cleanup(() => sortable.destroy()); | ||
}, | ||
); | ||
}; | ||
|
||
function generateSortHandler( | ||
expression: string, | ||
evaluateLater: Utilities['evaluateLater'], | ||
) { | ||
// No handler was passed to x-sort... | ||
// biome-ignore lint/suspicious/noEmptyBlockStatements: Intentional No-op | ||
if ([undefined, null, ''].includes(expression)) return () => {}; | ||
|
||
const handle = evaluateLater(expression); | ||
|
||
return (key: string, position: number) => { | ||
// In the case of `x-sort="handleSort"`, let us call it manually... | ||
Alpine.dontAutoEvaluateFunctions(() => { | ||
handle( | ||
// If a function is returned, call it with the key/position params... | ||
(received) => { | ||
if (typeof received === 'function') received(key, position); | ||
}, | ||
// Provide $key and $position to the scope in case they want to call their own function... | ||
{ | ||
scope: { | ||
// Supporting both `$item` AND `$key` ($key for BC)... | ||
$key: key, | ||
$item: key, | ||
$position: position, | ||
}, | ||
}, | ||
); | ||
}); | ||
}; | ||
} | ||
|
||
function getConfigurationOverrides( | ||
el: ElementWithXAttributes, | ||
_modifiers: string[], | ||
evaluate: Utilities['evaluate'], | ||
) { | ||
return el.hasAttribute('x-sort:config') | ||
? evaluate(el.getAttribute('x-sort:config')) | ||
: {}; | ||
} | ||
|
||
function initSortable(el, config, preferences, handle) { | ||
let ghostRef; | ||
|
||
const options = { | ||
animation: 150, | ||
|
||
handle: preferences.useHandles ? '[x-sort\\:handle]' : null, | ||
|
||
group: preferences.group, | ||
|
||
filter(e) { | ||
// Normally, we would just filter out any elements without `[x-sort:item]` | ||
// on them, however for backwards compatibility (when we didn't require | ||
// `[x-sort:item]`) we will check for x-sort\\:item being used at all | ||
if (!el.querySelector('[x-sort\\:item]')) return false; | ||
|
||
const itemHasAttribute = e.target.closest('[x-sort\\:item]'); | ||
|
||
return itemHasAttribute ? false : true; | ||
}, | ||
|
||
onSort(e) { | ||
// If item has been dragged between groups... | ||
if (e.from !== e.to) { | ||
// And this is the group it was dragged FROM... | ||
if (e.to !== e.target) { | ||
return; // Don't do anything, because the other group will call the handler... | ||
} | ||
} | ||
|
||
const key = e.item._x_sort_key; | ||
const position = e.newIndex; | ||
|
||
if (key !== undefined || key !== null) { | ||
handle(key, position); | ||
} | ||
}, | ||
|
||
onStart() { | ||
document.body.classList.add('sorting'); | ||
|
||
ghostRef = document.querySelector('.sortable-ghost'); | ||
|
||
if (preferences.hideGhost && ghostRef) ghostRef.style.opacity = '0'; | ||
}, | ||
|
||
onEnd() { | ||
document.body.classList.remove('sorting'); | ||
|
||
if (preferences.hideGhost && ghostRef) ghostRef.style.opacity = '1'; | ||
|
||
ghostRef = undefined; | ||
|
||
keepElementsWithinMorphMarkers(el); | ||
}, | ||
}; | ||
|
||
return new Sortable(el, { ...options, ...config }); | ||
} | ||
|
||
function keepElementsWithinMorphMarkers(el) { | ||
let cursor = el.firstChild; | ||
|
||
while (cursor.nextSibling) { | ||
if (cursor.textContent.trim() === '[if ENDBLOCK]><![endif]') { | ||
el.append(cursor); | ||
break; | ||
} | ||
|
||
cursor = cursor.nextSibling; | ||
} | ||
} | ||
|
||
function getGroupName(el, modifiers) { | ||
if (el.hasAttribute('x-sort:group')) { | ||
return el.getAttribute('x-sort:group'); | ||
} | ||
|
||
return modifiers.indexOf('group') !== -1 | ||
? modifiers[modifiers.indexOf('group') + 1] | ||
: null; | ||
} | ||
|
||
export default Sort; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"extends": "../../tsconfig.json", | ||
"include": ["src/**/*", "tests/**/*"], | ||
"exclude": ["**/node_modules", "**/dist"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/// <reference types="vitest" /> | ||
import { resolve } from 'node:path'; | ||
import { defineConfig } from 'vite'; | ||
import dts from 'vite-plugin-dts'; | ||
import ExternalDeps from 'vite-plugin-external-deps'; | ||
import WorkspaceSource from 'vite-plugin-workspace-source'; | ||
import tsconfigPaths from 'vite-tsconfig-paths'; | ||
|
||
export default defineConfig({ | ||
root: resolve(__dirname), | ||
plugins: [ | ||
dts({ | ||
entryRoot: resolve(__dirname, 'src'), | ||
tsconfigPath: resolve(__dirname, 'tsconfig.json'), | ||
}), | ||
tsconfigPaths(), | ||
ExternalDeps(), | ||
WorkspaceSource(), | ||
], | ||
define: { | ||
'import.meta.vitest': 'undefined', | ||
'import.meta.DEBUG': 'false', | ||
}, | ||
build: { | ||
target: 'esnext', | ||
outDir: resolve(__dirname, 'dist'), | ||
lib: { | ||
entry: resolve(__dirname, 'src', 'index.ts'), | ||
formats: ['es'], | ||
}, | ||
minify: false, | ||
rollupOptions: { | ||
output: { | ||
preserveModules: true, | ||
preserveModulesRoot: 'src', | ||
entryFileNames: ({ name: fileName }) => { | ||
return `${fileName}.js`; | ||
}, | ||
sourcemap: true, | ||
}, | ||
external: [/node_modules/], | ||
}, | ||
}, | ||
test: { | ||
globals: true, | ||
include: ['./**/*{.spec,.test}.{ts,tsx}'], | ||
includeSource: ['./**/*.{ts,tsx}'], | ||
reporters: ['dot'], | ||
deps: {}, | ||
passWithNoTests: true, | ||
}, | ||
}); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters