Right I managed to sort this out, looks like it took 40 minutes. Hopefully it can help others.
What you can do is take this script and ask GPT to analyze it so it understands the logic, and you can then use this to add other types of UX functions within knack.
For example I have a character counter function with a circle that goes red as people reach the character limit on a field, where I didn’t want to set hard length validations on fields and simply provide some visual feedback to the user.
The script is structured with parings so you enter the view (knack builder, page view, copy just the view value, and then the field alongside which the icon needs to appear - you get this from the knack table that contains the field - you may have to punch the icon so you can see the field names in the table view).
Test this first on your setup. If there are any real programmers what want to comment and improve please go ahead.
What this script will do is read the field first - so in my case where I have a log, it will simply add a date to the top of the field without cancelling what was there before. I even got it to put the cursor focus underneath the field ready for typing.
Have a good day.
// 1. Add your view-field pairings here
const calendarFieldPairs = [
{ view: 'view_XXX', field: 'field_XXX' },
{ view: 'view_XXX', field: 'field_XXX' }
// Add more as needed: { view: 'view_xxx', field: 'field_yyy' }
];
function getFormattedDate() {
const now = new Date();
const day = String(now.getDate()).padStart(2, '0');
const month = String(now.getMonth() + 1).padStart(2, '0');
const year = now.getFullYear();
return `${day}/${month}/${year}`;
}
// Modified process to accept view and field as arguments
function insertCalendarIcon(viewId, fieldId) {
const fieldSelector = `#${viewId} #${fieldId}`;
const $field = $(fieldSelector);
if (!$field.length) return;
// Prevent adding duplicate icon if the function runs more than once
if ($field.parent().hasClass('calendar-icon-wrapper')) return;
const calendarIconSvg = `
<span class="calendar-icon-btn" style="cursor:pointer; display: flex; align-items: center; margin-left: 8px;">
<svg height="18" width="18" style="display:inline;" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
<line x1="16" y1="2" x2="16" y2="6"></line>
<line x1="8" y1="2" x2="8" y2="6"></line>
<line x1="3" y1="10" x2="21" y2="10"></line>
</svg>
</span>
`;
const $wrapper = $('<div class="calendar-icon-wrapper" style="display: flex; align-items: center;"></div>');
$field.before($wrapper);
$wrapper.append($field).append(calendarIconSvg);
$wrapper.find('.calendar-icon-btn').on('click', function() {
const currentValue = $field.val();
const todayStr = getFormattedDate();
let newValue, cursorPos;
if (!currentValue || currentValue.trim() === "") {
newValue = todayStr + "\n";
cursorPos = newValue.length;
} else {
newValue = todayStr + "\n";
cursorPos = newValue.length;
newValue += "\n" + currentValue;
}
$field.val(newValue).focus();
const fieldEl = $field.get(0);
if (fieldEl.setSelectionRange) {
fieldEl.setSelectionRange(cursorPos, cursorPos);
} else if (fieldEl.createTextRange) {
var range = fieldEl.createTextRange();
range.collapse(true);
range.moveEnd('character', cursorPos);
range.moveStart('character', cursorPos);
range.select();
}
});
}
// Wire up our logic to each view render event for each pairing
calendarFieldPairs.forEach(pair => {
$(document).on(`knack-view-render.${pair.view}`, function(event, view) {
insertCalendarIcon(pair.view, pair.field);
});
});