Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ hv_button_behavior: "back"
/>
</view>
<text
id="basic-forms-submit"
action="replace"
delay="500"
href="/hyperview/public/advanced/case-studies/basic-forms/submit.xml"
Expand Down
54 changes: 54 additions & 0 deletions demo/backend/advanced/case-studies/scroll-to-input/_styles.xml.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<style
id="form-group"
flex="1"
marginLeft="24"
marginRight="24"
marginTop="24"
/>
<style
id="input"
borderBottomColor="#E1E1E1"
borderBottomWidth="1"
borderColor="#4E4D4D"
flex="1"
fontSize="16"
fontWeight="normal"
paddingBottom="8"
paddingTop="8"
>
<modifier focused="true">
<style borderBottomColor="#4778FF" />
</modifier>
</style>
<style id="input-error" borderBottomColor="#FF4847" color="#FF4847">
<modifier focused="true">
<style borderBottomColor="#FF4847" />
</modifier>
</style>
<style
id="label"
borderColor="#4E4D4D"
fontSize="16"
fontWeight="bold"
lineHeight="24"
marginBottom="8"
/>
<style
id="help"
borderColor="#FF4847"
fontSize="16"
fontWeight="normal"
lineHeight="24"
marginTop="16"
/>
<style id="help-error" color="#FF4847" />
<style
id="submit"
color="#4778FF"
fontSize="16"
fontWeight="bold"
lineHeight="24"
marginLeft="24"
marginTop="16"
/>
<style id="spacer" height="200" />
44 changes: 44 additions & 0 deletions demo/backend/advanced/case-studies/scroll-to-input/index.xml.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
permalink: "/advanced/case-studies/scroll-to-input/index.xml"
tags: "Advanced/Case Studies"
hv_title: "Scroll to Input Offset"
hv_button_behavior: "back"
---
{% extends 'templates/base.xml.njk' %}
{% from 'macros/description/index.xml.njk' import description %}

{% block styles %}
{% include './_styles.xml.njk' %}
{% endblock %}

{% block container %}
<form id="myScrollForm" scroll="true" scroll-to-input-offset="450">
{{ description('Scroll to input offset is a property that can be used to scroll to an input field when it is focused.') }}
<view style="spacer" />
<view style="spacer" />
<view style="form-group">
<text style="label">Label line</text>
<text-field
name="data"
placeholder="Placeholder"
placeholderTextColor="#8D9494"
style="input"
/>
</view>
<text
action="replace"
href="/hyperview/public/advanced/case-studies/scroll-to-input/submit.xml"
show-during-load="mySpinner"
style="submit"
target="myScrollForm"
>
Submit
</text>
<view style="spacer" />
<view style="spacer" />
<view style="spacer" />
<view style="spacer" />
{{ description('Bottom of the form') }}
</form>
<spinner id="mySpinner" hide="true" />
{% endblock %}
18 changes: 18 additions & 0 deletions demo/backend/advanced/case-studies/scroll-to-input/submit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<form
id="myScrollForm"
scroll="true"
xmlns="https://hyperview.org/hyperview"
scroll-to-input-offset="512"
>
<view style="form-group">
<text style="label">Label line</text>
<text-field
name="data"
placeholder="Placeholder"
placeholderTextColor="#8D9494"
style="input"
value=""
/>
</view>
<text style="submit">Saved!</text>
</form>
2 changes: 1 addition & 1 deletion demo/backend/ui/ui-elements/forms/text-field/index.xml.njk
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ hv_button_behavior: "back"
{% endblock %}

{% block content %}
{{ about('Text field are used in forms to collect textual data') }}
{{ about('Text fields are used in forms to collect textual data') }}
{% include './_form.xml.njk' %}
{% endblock %}
74 changes: 68 additions & 6 deletions demo/src/Behaviors/AddStyles/AddStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,44 @@ import {
shallowCloneToRoot,
} from 'hyperview/src/services';

/**
* Checks if a style element has changed by comparing its attributes and children
*/
const hasStyleChanged = (oldStyle: Element, newStyle: Element): boolean => {
// Compare attributes
const oldAttrs = oldStyle.attributes ? Array.from(oldStyle.attributes) : [];
const newAttrs = newStyle.attributes ? Array.from(newStyle.attributes) : [];

if (oldAttrs.length !== newAttrs.length) {
return true;
}

// Check if any attribute values differ
const hasAttrChanged = newAttrs.some(
newAttr => oldStyle.getAttribute(newAttr.name) !== newAttr.value,
);
if (hasAttrChanged) {
return true;
}

// Compare children recursively
const oldChildren = oldStyle.childNodes
? Array.from(oldStyle.childNodes)
: [];
const newChildren = newStyle.childNodes
? Array.from(newStyle.childNodes)
: [];

if (oldChildren.length !== newChildren.length) {
return true;
}

// Check if any child elements differ
return newChildren.some((newChild, index) =>
hasStyleChanged(oldChildren[index] as Element, newChild as Element),
);
};

/**
* This behavior allows injecting styles into the screen's styles element.
* It is useful when loading partial Hyperview documents that need their own styles.
Expand All @@ -33,17 +71,41 @@ export const AddStyles: HvBehavior = {
if (newStyles) {
const screen = getAncestorByTagName(element, 'screen');
if (screen) {
const styles = Dom.getFirstTag(screen, 'styles');
const styleElements = newStyles.getElementsByTagName('style');
const styles: Element | null | undefined = Dom.getFirstTag(
screen,
'styles',
);
const styleElements = Array.from(
newStyles.getElementsByTagName('style'),
).filter(e => !!e.getAttribute('id'));
if (styles && styleElements) {
Array.from(styleElements).forEach(style => {
styles.appendChild(style);
const existingStyles = Array.from(
styles.getElementsByTagName('style') || [],
);
let hasChanges = false;
styleElements.forEach(style => {
const styleId = style.getAttribute('id');
const existingStyle = existingStyles.find(
e => e.getAttribute('id') === styleId,
);
if (!existingStyle) {
styles.appendChild(style);
hasChanges = true;
} else if (style && hasStyleChanged(existingStyle, style)) {
styles.replaceChild(style, existingStyle);
hasChanges = true;
}
});
const newRoot = shallowCloneToRoot(styles);
updateRoot(newRoot, true);

if (hasChanges) {
// Only perform cloning if styles have changed
const newRoot: Document = shallowCloneToRoot(styles);
updateRoot(newRoot, true);
}
}
}
}

const ranOnce = element.getAttribute('ran-once');
const once = element.getAttribute('once');
if (once === 'true') {
Expand Down
6 changes: 3 additions & 3 deletions docs/reference_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ An attribute indicating the direction in which the view will scroll.

#### `scroll-to-input-offset`

| Type | Required |
| ------ | ----------------------- |
| number | No (defauls to **120**) |
| Type | Required |
| ------ | ------------------------ |
| number | No (defaults to **120**) |

An attribute defining an additional scroll offset to be applied to the view, when a `<text-field>` is focused. Only valid in combination with attribute `scroll` set to `"true"`.

Expand Down
1 change: 1 addition & 0 deletions schema/core.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@
<xs:attribute name="safe-area" type="xs:boolean" />
<xs:attribute name="value" type="xs:string" />
<xs:attribute name="sticky" type="xs:boolean" />
<xs:attribute name="scroll-to-input-offset" type="xs:nonNegativeInteger" />
<xs:attributeGroup ref="hv:behaviorAttributes" />
<xs:attributeGroup ref="hv:scrollAttributes" />
</xs:attributeGroup>
Expand Down
Loading