Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.github export-ignore
/.gitattributes export-ignore
/repeatable.gif export-ignore
/Resources/Private/Editor export-ignore
Expand Down
34 changes: 34 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# How to contribute

I love to welcome your contributions. There are several ways to help out:

- Create an [issue](../../../issues) on GitHub, if you have found a bug
- Provide examples for open bug issues
- Write patches for open bug/feature issues

There are a few guidelines that I need contributors to follow so that I have a chance of keeping on top of things.

## Getting Started

- Make sure you have a [GitHub account](https://github.com/signup/free).
- Submit an [issue](../../../issues), assuming one does not already exist.
- Clearly describe the issue including steps to reproduce when it is a bug.
- Make sure you fill in the earliest version that you know has the issue.
- Fork the repository on GitHub.

## Making Changes

- Create a topic branch from where you want to base your work.
- This is usually the master branch.
- Only target release branches if you are certain your fix must be on that branch.
- To quickly create a topic branch based on master; `git branch master/my_contribution master` then checkout the new branch
with `git checkout master/my_contribution`. Better avoid working directly on the `master` branch, to avoid conflicts if
you pull in updates from origin.
- Make commits of logical units.
- Check for unnecessary whitespace with `git diff --check` before committing.
- Use descriptive commit messages and reference the #issue number.

## Submitting Changes

- Push your changes to a topic branch in your fork of the repository.
- Submit a pull request to the repository
73 changes: 73 additions & 0 deletions .github/ISSUE_TEMPLATE/Bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: 🐛 Bug report
description: Create a report to help us improve
labels: bug
body:
- type: markdown
attributes:
value: Thank you for taking the time to complete this bug report!
- type: markdown
attributes:
value: Please make sure to provide the information we ask for. This will allow us to help you better.
- type: textarea
id: what-happened
attributes:
label: Describe the bug
description: A clear and concise description of the current behaviour.
placeholder: A bug happened!
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behaviour
description: A description of what you expect to happen.
placeholder: I expect to see X or Y
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Please add a link to a repository with a minimal reproduction. Or describe accurately how we can reproduce/verify the bug.
placeholder: |
Example steps (replace with your own):
1. Clone my repo at https://github.com/<myuser>/example
2. Do x and y
3. You should see the error come up
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: The version of RepeatableFields you are using.
placeholder: ex. v2.2.7
validations:
required: true
- type: textarea
id: phpinfo
attributes:
label: PHP version
description: |
Please paste the output of running `php -v`.
This will be automatically formatted as a code block, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
id: composerinfo
attributes:
label: Environment
description: |
Please paste the output of running `composer info`.
This will be automatically formatted as a code block, so no need for backticks.
render: shell
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Anything else that might be relevant
validations:
required: false
45 changes: 45 additions & 0 deletions .github/ISSUE_TEMPLATE/Feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: ✨ Feature request
description: Suggest an idea for this project
labels: enhancement
body:
- type: markdown
attributes:
value: Thank you for taking the time to suggest a new feature!
- type: markdown
attributes:
value: Please describe the feature in detail. Why would it would be a good addition to RepeatableFields?
- type: textarea
id: description
attributes:
label: What should be improved?
description: Is your feature request related to a problem? Please describe it.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe the solution you would like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Possible alternatives
description: Describe any alternatives you have considered.
validations:
required: false
- type: textarea
id: extra
attributes:
label: Additional context
description: Add any other relevant information.
validations:
required: false
- type: checkboxes
id: can-work
attributes:
label: Are you willing to work on this?
description: Are you willing to help us add this feature? If you are not sure how, feel free to ask for guidance.
options:
- label: Yes, I would like to help
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: 🗣️ Ask question
url: https://github.com/mireo91/RepeatableFields/discussions
about: Please ask and answer questions in discussions
6 changes: 6 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Describe the big picture of your changes here to communicate to the maintainers why I should accept this pull request.
If it fixes a bug or resolves a feature request, be sure to link to that issue.

The best way to propose a feature is to open an issue first and discuss your ideas there before implementing them.

Always follow the [contribution guidelines](.github/CONTRIBUTING.md) when submitting a pull request.
21 changes: 21 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
changelog:
exclude:
labels:
- ignore-for-releasenotes
- dependencies
categories:
- title: 💥 Breaking changes
labels:
- breaking
- title: ✨ Exciting new features
labels:
- enhancement
- title: 🐛 Found and fixed bugs
labels:
- bug
- title: 📝 Added documentation
labels:
- documentation
- title: 🩹 Other changes
labels:
- '*'
1 change: 1 addition & 0 deletions Resources/Private/Editor/Repeatable/Sortable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function Sortable({ onChange, value, element, items, enable, automaticSor
manualSort={manualSort}
onSortEnd={onSortEnd}
automaticSorting={automaticSorting}
helperClass={style.sortableHelper}
useDragHandle
axis="y"
lockAxis="y"
Expand Down
99 changes: 98 additions & 1 deletion Resources/Private/Editor/Repeatable/helper.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import positionalArraySorter from "@neos-project/positional-array-sorter";
import { nanoid } from "nanoid";

export function set(path, value, object) {
path = getPath(path);
return recursivelySetValueInObject(object, value, path);
}

export function removeKeyPropertyFromObject(array, KEY_PROPERTY) {
return clone(array).map((item) => {
delete item[KEY_PROPERTY];
return item;
});
}

export const clone = (input) => JSON.parse(JSON.stringify(input));

export const isSame = (a, b) => JSON.stringify(a) == JSON.stringify(b);
Expand Down Expand Up @@ -165,7 +175,7 @@ function recursivelySetValueInObject(object, value, path) {

if (Array.isArray(object)) {
//
// Make sure, that array elements are always inserted at the last position, if the path exceeds the length
// Make sure that array elements are always inserted at the last position, if the path exceeds the length
// of the array
//
if (typeof path[0] === "number" && object.length < path[0]) {
Expand All @@ -180,3 +190,90 @@ function recursivelySetValueInObject(object, value, path) {

return Object.assign({}, object, { [path[0]]: recursivelySetValueInObject(object[path[0]], value, path.slice(1)) });
}

export function getInitialValue({ emptyGroup, value, KEY_PROPERTY, options }) {
// add a fixed index to the value
let result = addKeyToValue(value, KEY_PROPERTY);
const { min, max } = options;

if (min) {
if (result.length < min) {
for (var i = 0; i < min; ++i) {
if (result[i]) {
result[i] = value[i];
} else {
result[i] = emptyGroup;
}
}
}
}
if (max && result.length > max) {
result = result.slice(0, max);
}

if (result.length) {
for (let key = 0; key < result.length; key++) {
const predefined = options.predefinedProperties?.[key]?.properties;
const currentEntry = clone(result[key]);
const availableKeys = Object.keys(currentEntry).filter((key) => key == KEY_PROPERTY || key in emptyGroup);
const cleanedUpEntry = availableKeys.reduce((cur, keyname) => {
const isPredefined = predefined?.[keyname]?.defaultValue != undefined;
let value = isPredefined ? predefined[keyname].defaultValue : currentEntry[keyname];
if (isNumeric(value)) {
value = parseFloat(value);
}

return {
...cur,
[keyname]: value,
};
}, {});
result[key] = cleanedUpEntry;
}
}
return result;
}

export function getEmptyGroup(properties) {
if (!properties) {
return {};
}
const group = {};
// Create array to enable sorting
const array = [];
for (const key in properties) {
const item = properties[key];
array.push({ key, position: item?.position ?? null, item });
}
positionalArraySorter(array).forEach(({ key, item }) => {
const defaultValue = item && item.defaultValue;
group[key] = returnValueIfSet(defaultValue, "");
});
return group;
}

export function addKeyToValue(value, KEY_PROPERTY) {
if (!Array.isArray(value)) {
return [];
}
let newValue = clone(value);
// add an fixed index to the value
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "add an fixed index" but should be "add a fixed index". The article "an" is used before vowel sounds, and "fixed" starts with a consonant sound.

Copilot uses AI. Check for mistakes.
newValue = newValue.map((item) => {
if (item[KEY_PROPERTY]) {
return item;
}
return {
...item,
[KEY_PROPERTY]: nanoid(),
};
});
return newValue;
}

export function checkIfValueIsSet(value) {
return !!(value !== null && value !== undefined);
}

export function returnValueIfSet(value, fallback = "") {
return checkIfValueIsSet(value) ? value : fallback;
}
Loading