Skip to content
Merged
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Adds

* Translation strings added for the layout- and layout-column-widgets.
* New `box` schema field type
* When switching locale from the doc editor, ask if the user wants to localize the current document in the target locale or want to start a blank document.
* Introduced a new `longPolling: false` option for the `@apostrophecms/notification` module. This eliminates long-pending requests when logged in, but also slows down the delivery of notifications. The behavior can be tuned further via the `pollingInterval` option, which defaults to `5000` milliseconds.

Expand Down
1 change: 1 addition & 0 deletions defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ module.exports = {
'@apostrophecms/rich-text-widget': {},
'@apostrophecms/html-widget': {},
'@apostrophecms/color-field': {},
'@apostrophecms/box-field': {},
'@apostrophecms/oembed-field': {},
'@apostrophecms/video-widget': {},
'@apostrophecms/file-widget': {},
Expand Down
157 changes: 157 additions & 0 deletions modules/@apostrophecms/box-field/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
module.exports = {
options: {
name: 'box',
alias: 'boxField'
},
init(self) {
self.name = self.options.name;
self.addFieldType();
self.enableBrowserData();
},
methods(self) {
return {
addFieldType() {
self.apos.schema.addFieldType({
name: 'box',
convert(req, field, data, destination) {
const defProps = [ 'top', 'right', 'bottom', 'left' ];
const temp = {};
const min = self.apos.launder.integer(field.min);
let max = null;

if ('max' in field) {
max = self.apos.launder.integer(field.max);
}

// All values to numbers or null
defProps.forEach(side => {
const int = parseInt(data[field.name][side]);
if (int || int === 0) {
temp[side] = int;
} else {
temp[side] = null;
}
});

// One non-null value if required
if (field.required) {
const unique = [ ...new Set(Object.values(temp)) ];
if (unique.length === 1 && unique[0] === null) {
throw self.apos.error('required');
}
}

// Minimum values in range
for (const key of defProps) {
if (temp[key] && temp < min) {
throw self.apos.error(`${key} is below the min ${field.min}, is ${temp[key]}`);
}
}

// Maximum values in range
if (max) {
for (const key of defProps) {
if (temp[key] && temp[key] > field.max) {
throw self.apos.error(`${key} is greater than the max ${field.max}, is ${temp[key]}`);
}
}
}

// Copy values to destination
destination[field.name] = temp;
},
validate(field, options, warn, fail) {
const defProps = [ 'top', 'right', 'bottom', 'left' ];
let defMin = 0;

if (field.max && typeof field.max !== 'number') {
fail('Property "max" must be a number');
}

if (field.step && typeof field.step !== 'number') {
fail('Property "step" must be a number');
}

if (field.def) {
const fieldDefProps = Object.keys(field.def);
if (
!(fieldDefProps.length === defProps.length &&
fieldDefProps.every(k => defProps.includes(k)))
) {
fail('Def must be an object with only "top", "right", "bottom", and "left" keys');
}

for (const key in field.def) {
if (!Number.isFinite(field.def[key])) {
fail(`Default property "${key}" must be a number`);
}
}
}

if (field.min) {
if (typeof field.min !== 'number') {
fail('Property "min" must be a number');
}
} else {
if (field.def) {
// if def has a negative value, it needs to be the min
const fieldDefMin = Math.min(...Object.values(field.def));
if (defMin > fieldDefMin) {
defMin = fieldDefMin;
}
}
field.min = defMin;
}

},
def: {
top: null,
right: null,
bottom: null,
left: null
}
});
},
getBrowserData(req) {
return {
name: self.name,
action: self.action
};
}
};
},
helpers() {
return {
toCss(value, property, unit = 'px') {
const {
top, right, bottom, left
} = value;
const vals = [ top, right, bottom, left ];

if (vals.every(v => v == null)) {
return '';
};

if (vals.every(v => v === top && v != null)) {
return `${property}: ${top}${unit};`;
}

const sides = {
top,
right,
bottom,
left
};
const parts = [];

for (const [ side, val ] of Object.entries(sides)) {
if (val != null) {
parts.push(`${property}-${side}: ${val}${unit};`);
}
}

return parts.join(' ');
}
};
}
};
Loading