Skip to content

Commit

Permalink
feat: component transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
valeriocomo authored Oct 31, 2024
1 parent 9cdc929 commit bb26b09
Show file tree
Hide file tree
Showing 30 changed files with 1,078 additions and 61 deletions.
70 changes: 34 additions & 36 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node
{
"name": "design-angular-kit",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
// "image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bookworm",
"build": {
"dockerfile": "Dockerfile"
},
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers-contrib/features/angular-cli:2": {
"version": "18.0.7"
},
"ghcr.io/devcontainers-contrib/features/cz-cli:1": {}
},
"mounts": [
"source=design-angular-kit-bundle-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" // deps volume
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "sh .devcontainer/scripts/postCreateCommand.sh",
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"angular.ng-template",
"cyrilletuzi.angular-schematics",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"vivaxy.vscode-conventional-commits"
]
}
}
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
"name": "design-angular-kit",
"build": {
"dockerfile": "Dockerfile"
},
"mounts": [
"source=design-angular-kit-bundle-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" // deps volume
],
"remoteUser": "root",
"postCreateCommand": "sh .devcontainer/scripts/postCreateCommand.sh",
// "postStartCommand": "/bin/bash -c '.devcontainer/scripts/postStartCommand.sh \"${containerWorkspaceFolder}\"'",
"postStartCommand": "sh .devcontainer/scripts/postStartCommand.sh \"${containerWorkspaceFolder}\"",
"customizations": {
"vscode": {
"extensions": [
"angular.ng-template",
"cyrilletuzi.angular-schematics",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"vivaxy.vscode-conventional-commits"
],
"settings": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "always"
}
}
}
},
"features": {
"ghcr.io/devcontainers-contrib/features/angular-cli:2": {
"version": "18.0.7"
},
"ghcr.io/devcontainers-contrib/features/cz-cli:1": {}
}
}
11 changes: 7 additions & 4 deletions .devcontainer/scripts/postCreateCommand.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#/bin/bash
echo "lifecycle hook: postCreateCommand => start"

echo "Set node_modules permission"
sudo chown -R node:node node_modules
echo "Set node_modules permission: done"
# echo "Set node_modules permission"
# sudo chown -R node:node node_modules
# echo "Set node_modules permission: done"

echo "Installing deps"
npm i
echo "Installing deps: done"
echo "Installing deps: done"

echo "lifecycle hook: postCreateCommand => done"
9 changes: 9 additions & 0 deletions .devcontainer/scripts/postStartCommand.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

WORKSPACE_FOLDER=$1

echo "lifecycle hook: postStartCommand => start"
echo "Set $WORKSPACE_FOLDER as git safe directory"
git config --global --add safe.directory $WORKSPACE_FOLDER
# git config --global --add safe.directory /workspaces/design-angular-kit
echo "lifecycle hook: postStartCommand => done"
3 changes: 2 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"buildTarget": "design-angular-kit-bundle:build"
"buildTarget": "design-angular-kit-bundle:build",
"host": "127.0.0.1"
},
"configurations": {
"production": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ItTextareaComponent } from './textarea/textarea.component';
import { ItUploadDragDropComponent } from './upload-drag-drop/upload-drag-drop.component';
import { ItUploadFileListComponent } from './upload-file-list/upload-file-list.component';
import { ItAutocompleteComponent } from './autocomplete/autocomplete.component';
import { ItTransferComponent } from './transfer/transfer.component';

const formComponents = [
ItAutocompleteComponent,
Expand All @@ -21,6 +22,7 @@ const formComponents = [
ItRatingComponent,
ItSelectComponent,
ItTextareaComponent,
ItTransferComponent,
ItUploadDragDropComponent,
ItUploadFileListComponent,
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { TransferItem, TransferItemSelection } from '../transfer.model';
import { SelectionState, State } from './transfer.state';

//#region private utility functions
const generateSelectAll = <T>(checked: boolean, items: TransferItem<T>[]) => {
const selected = new Set<TransferItem<T>>();
if (checked) {
items.forEach(item => selected.add(item));
}

return selected;
};

const updateSelected = <T>(set: Set<TransferItem<T>>, item: TransferItem<T>) => {
if (set.has(item)) {
set.delete(item);
} else {
set.add(item);
}

return set;
};
//#endregion

//#region reducers
const init = <T>(state: State<T>, { source, target }: SelectionState<T>) => ({
...state,
initialItems: {
source: [...source],
target: [...target],
},
current: {
source: [...source],
target: [...target],
},
});

const transfer = <T>(state: State<T>) => {
return {
...state,
current: {
...state.current,
source: state.current.source.filter(i => !state.selections.source.has(i)),
target: Array.from(new Set([...state.current.target, ...Array.from(state.selections.source)] as TransferItemSelection<T>)),
},
selections: {
...state.selections,
source: new Set<TransferItem<T>>(),
},
operationsEnabled: {
...state.operationsEnabled,
transfer: false,
reset: true,
},
} satisfies State<T>;
};

const backtransfer = <T>(state: State<T>) => {
return {
...state,
current: {
...state.current,
target: state.current.target.filter(i => !state.selections.target.has(i)),
source: Array.from(new Set([...state.current.source, ...Array.from(state.selections.target)] as TransferItemSelection<T>)),
},
selections: {
...state.selections,
target: new Set<TransferItem<T>>(),
},
operationsEnabled: {
...state.operationsEnabled,
backtransfer: false,
reset: true,
},
} satisfies State<T>;
};

const reset = <T>(state: State<T>) => {
return {
...state,
current: {
source: [...state.initialItems.source],
target: [...state.initialItems.target],
},
operationsEnabled: {
...state.operationsEnabled,
reset: false,
},
} satisfies State<T>;
};

const selectAllSource = <T>(state: State<T>, { checked }: { checked: boolean }) => {
const items = state.current.source;
const selected = generateSelectAll(checked, items);
const transfer = Boolean(selected.size);

return {
...state,
selections: {
...state.selections,
source: selected,
},
operationsEnabled: {
...state.operationsEnabled,
transfer,
},
} satisfies State<T>;
};

const selectAllTarget = <T>(state: State<T>, { checked }: { checked: boolean }) => {
const items = state.current.target;
const selected = generateSelectAll(checked, items);
const backtransfer = Boolean(selected.size);

return {
...state,
selections: {
...state.selections,
target: selected,
},
operationsEnabled: {
...state.operationsEnabled,
backtransfer,
},
} satisfies State<T>;
};

const selectionItemSource = <T>(previousState: State<T>, { item }: { item: TransferItem<T> }) => {
const selected = updateSelected(previousState.selections.source, item);
const selectedItems = Array.from(selected);
const transfer = Boolean(selectedItems.length);
const source = new Set([...selectedItems]);

const state = {
...previousState,
selections: {
...previousState.selections,
source,
},
operationsEnabled: {
...previousState.operationsEnabled,
transfer,
},
} satisfies State<T>;

return state;
};

const selectionItemTarget = <T>(previousState: State<T>, { item }: { item: TransferItem<T> }) => {
const selected = updateSelected(previousState.selections.target, item);
const selectedItems = Array.from(selected);
const backtransfer = Boolean(selectedItems.length);
const target = new Set([...selectedItems]);

const state = {
...previousState,
selections: {
...previousState.selections,
target,
},
operationsEnabled: {
...previousState.operationsEnabled,
backtransfer,
},
} satisfies State<T>;

return state;
};
//#endregion reducers

//#region public reducers
const initialStateFn = <T>() => ({
initialItems: {
source: [],
target: [],
},
current: {
source: [],
target: [],
},
selections: {
source: new Set<TransferItem<T>>(),
target: new Set<TransferItem<T>>(),
},
operationsEnabled: {
transfer: false,
backtransfer: false,
reset: false,
},
});
const initFn =
<T>(payload: SelectionState<T>) =>
(state: State<T>) =>
init(state, payload);

const transferFn =
<T>() =>
(state: State<T>) =>
transfer(state);

const backtransferFn =
<T>() =>
(state: State<T>) =>
backtransfer(state);

const resetFn =
<T>() =>
(state: State<T>) =>
reset(state);

const selectAllSourceFn =
<T>({ checked }: { checked: boolean }) =>
(state: State<T>) =>
selectAllSource(state, { checked }) as State<T>;

const selectAllTargetFn =
<T>({ checked }: { checked: boolean }) =>
(state: State<T>) =>
selectAllTarget(state, { checked }) as State<T>;

const selectionItemSourceFn =
<T>({ item }: { item: TransferItem<T> }) =>
(state: State<T>) =>
selectionItemSource(state, { item }) as State<T>;

const selectionItemTargetFn =
<T>({ item }: { item: TransferItem<T> }) =>
(state: State<T>) =>
selectionItemTarget(state, { item }) as State<T>;
//#endregion

export default {
initialStateFn,
initFn,
transferFn,
backtransferFn,
resetFn,
selectAllSourceFn,
selectAllTargetFn,
selectionItemSourceFn,
selectionItemTargetFn,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TransferItem } from '../transfer.model';

export interface SelectionState<T> {
source: Array<TransferItem<T>>;
target: Array<TransferItem<T>>;
}

export interface State<T> {
initialItems: SelectionState<T>;
current: SelectionState<T>;
selections: {
source: Set<TransferItem<T>>;
target: Set<TransferItem<T>>;
};
operationsEnabled: {
transfer: boolean;
backtransfer: boolean;
reset: boolean;
};
}
Loading

0 comments on commit bb26b09

Please sign in to comment.