attrs-in-props 3.14.0 → 3.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -19,20 +19,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var index_exports = {};
20
20
  __export(index_exports, {
21
21
  ARIA_ROLES: () => ARIA_ROLES,
22
- ATTR_TRANSFORMS: () => ATTR_TRANSFORMS,
23
22
  DOM_EVENTS: () => DOM_EVENTS,
24
23
  HTML_ATTRIBUTES: () => HTML_ATTRIBUTES,
25
- applyAttrTransforms: () => applyAttrTransforms,
26
24
  checkAttributeByTagName: () => checkAttributeByTagName,
27
25
  checkEventFunctions: () => checkEventFunctions,
28
26
  executeAttr: () => executeAttr,
29
- extractConditionalAttrs: () => extractConditionalAttrs,
30
- filterAttributesByTagName: () => filterAttributesByTagName,
31
- resolveFileSource: () => resolveFileSource,
32
- resolvePropValue: () => resolvePropValue
27
+ filterAttributesByTagName: () => filterAttributesByTagName
33
28
  });
34
29
  module.exports = __toCommonJS(index_exports);
35
- var import_utils = require("@symbo.ls/utils");
30
+ var import_utils = require("@domql/utils");
36
31
  const ARIA_ROLES = [
37
32
  "alert",
38
33
  "alertdialog",
@@ -222,15 +217,7 @@ const HTML_ATTRIBUTES = {
222
217
  "type",
223
218
  "referrerpolicy"
224
219
  ],
225
- audio: [
226
- "autoplay",
227
- "controls",
228
- "crossorigin",
229
- "loop",
230
- "muted",
231
- "preload",
232
- "src"
233
- ],
220
+ audio: [],
234
221
  area: [
235
222
  "alt",
236
223
  "coords",
@@ -321,7 +308,6 @@ const HTML_ATTRIBUTES = {
321
308
  "loading",
322
309
  "marginheight",
323
310
  "marginwidth",
324
- "mozallowfullscreen",
325
311
  "name",
326
312
  "referrerpolicy",
327
313
  "sandbox",
@@ -329,7 +315,6 @@ const HTML_ATTRIBUTES = {
329
315
  "seamless",
330
316
  "src",
331
317
  "srcdoc",
332
- "webkitallowfullscreen",
333
318
  "width"
334
319
  ],
335
320
  img: [
@@ -590,19 +575,12 @@ const HTML_ATTRIBUTES = {
590
575
  "srclang"
591
576
  ],
592
577
  video: [
593
- "autoplay",
594
- "controls",
595
- "crossorigin",
596
- "disablepictureinpicture",
597
- "disableremoteplayback",
598
578
  "height",
599
- "loop",
600
- "muted",
601
579
  "playsinline",
602
580
  "poster",
603
- "preload",
604
- "src",
605
- "width"
581
+ "width",
582
+ "disablepictureinpicture",
583
+ "disableremoteplayback"
606
584
  ],
607
585
  svg: [
608
586
  "className",
@@ -994,18 +972,7 @@ const DOM_EVENTS = [
994
972
  "onfullscreenchange",
995
973
  "onfullscreenerror"
996
974
  ];
997
- const camelToAttr = (key) => {
998
- if (key.startsWith("aria") && key.length > 4 && key.charCodeAt(4) >= 65 && key.charCodeAt(4) <= 90) {
999
- return "aria-" + key.charAt(4).toLowerCase() + key.slice(5).replace(/([A-Z])/g, (m) => "-" + m.toLowerCase());
1000
- }
1001
- if (key.startsWith("data") && key.length > 4 && key.charCodeAt(4) >= 65 && key.charCodeAt(4) <= 90) {
1002
- return "data-" + key.charAt(4).toLowerCase() + key.slice(5).replace(/([A-Z])/g, (m) => "-" + m.toLowerCase());
1003
- }
1004
- return null;
1005
- };
1006
975
  const checkAttributeByTagName = (tag, attribute) => {
1007
- if (attribute.startsWith("aria-") || attribute.startsWith("data-")) return true;
1008
- if (camelToAttr(attribute)) return true;
1009
976
  if (Object.prototype.hasOwnProperty.call(HTML_ATTRIBUTES, tag)) {
1010
977
  const attributes = HTML_ATTRIBUTES[tag];
1011
978
  return attributes.includes(attribute) || attributes.includes("default");
@@ -1019,33 +986,14 @@ const checkEventFunctions = (key) => {
1019
986
  const normalizedKey = key.toLowerCase();
1020
987
  return DOM_EVENTS.includes(normalizedKey);
1021
988
  };
1022
- const filterAttributesByTagName = (tag, props, cssProps) => {
989
+ const filterAttributesByTagName = (tag, props) => {
1023
990
  const filteredObject = {};
1024
991
  for (const key in props) {
1025
992
  if (Object.prototype.hasOwnProperty.call(props, key)) {
1026
- if (cssProps && key in cssProps) continue;
1027
- if (key === "aria" && props[key] && typeof props[key] === "object") {
1028
- for (const ariaKey in props[key]) {
1029
- if ((0, import_utils.isDefined)(props[key][ariaKey])) {
1030
- filteredObject["aria-" + ariaKey] = props[key][ariaKey];
1031
- }
1032
- }
1033
- continue;
1034
- }
1035
- if (key === "data" && props[key] && typeof props[key] === "object") {
1036
- for (const dataKey in props[key]) {
1037
- if ((0, import_utils.isDefined)(props[key][dataKey])) {
1038
- const kebab = dataKey.replace(/([A-Z])/g, (m) => "-" + m.toLowerCase());
1039
- filteredObject["data-" + kebab] = props[key][dataKey];
1040
- }
1041
- }
1042
- continue;
1043
- }
1044
993
  const isAttribute = checkAttributeByTagName(tag, key);
1045
994
  const isEvent = checkEventFunctions(key);
1046
995
  if ((0, import_utils.isDefined)(props[key]) && (isAttribute || isEvent)) {
1047
- const attrName = camelToAttr(key) || key;
1048
- filteredObject[attrName] = props[key];
996
+ filteredObject[key] = props[key];
1049
997
  }
1050
998
  }
1051
999
  }
@@ -1060,120 +1008,3 @@ const executeAttr = (elem, element) => {
1060
1008
  }
1061
1009
  return attrObj;
1062
1010
  };
1063
- const resolvePropValue = (el, value) => {
1064
- let resolved = el.call("exec", value, el);
1065
- if (!resolved) return;
1066
- if ((0, import_utils.isString)(resolved) && resolved.includes("{{")) {
1067
- resolved = el.call("replaceLiteralsWithObjectFields", resolved);
1068
- }
1069
- return resolved;
1070
- };
1071
- const resolveFileSource = (el, value) => {
1072
- let src = (el.preSrc || "") + (resolvePropValue(el, value) || "");
1073
- if (!src) return;
1074
- try {
1075
- new URL(src);
1076
- return src;
1077
- } catch (e) {
1078
- }
1079
- const { context } = el;
1080
- if (!context.files && !context.assets) return src;
1081
- if (src.startsWith("/assets/")) {
1082
- const key = src.slice(8);
1083
- const asset = context.assets && (context.assets[src] || context.assets[key]);
1084
- if (asset && asset.content) return asset.content.src;
1085
- return src;
1086
- }
1087
- if (src.startsWith("/files/")) {
1088
- const key = src.slice(7);
1089
- const file = context.files && (context.files[src] || context.files[key]);
1090
- if (file && file.content) return file.content.src;
1091
- return src;
1092
- }
1093
- if (context.assets) {
1094
- const asset = context.assets[src];
1095
- if (asset && asset.content) return asset.content.src;
1096
- }
1097
- if (context.files) {
1098
- const file = context.files[src];
1099
- if (file && file.content) return file.content.src;
1100
- }
1101
- return src;
1102
- };
1103
- const ATTR_TRANSFORMS = {
1104
- src: (el) => resolveFileSource(el, el.src),
1105
- href: (el) => resolvePropValue(el, el.href),
1106
- action: (el) => resolvePropValue(el, el.action),
1107
- poster: (el) => resolveFileSource(el, el.poster),
1108
- data: (el) => resolvePropValue(el, el.data)
1109
- };
1110
- const applyAttrTransforms = (element) => {
1111
- const tag = element.tag || "div";
1112
- const result = {};
1113
- for (const attr in ATTR_TRANSFORMS) {
1114
- if (element[attr] !== void 0 && checkAttributeByTagName(tag, attr)) {
1115
- const val = ATTR_TRANSFORMS[attr](element);
1116
- if (val !== void 0) result[attr] = val;
1117
- }
1118
- }
1119
- return result;
1120
- };
1121
- const resolveCase = (caseKey, element) => {
1122
- const caseFn = element.context?.cases?.[caseKey];
1123
- if (caseFn === void 0) return void 0;
1124
- if ((0, import_utils.isFunction)(caseFn)) return caseFn.call(element, element);
1125
- return !!caseFn;
1126
- };
1127
- const evaluateCondition = (prefix, caseKey, element) => {
1128
- if (prefix === "$") {
1129
- let result = resolveCase(caseKey, element);
1130
- if (result === void 0) result = !!element[caseKey];
1131
- return result;
1132
- }
1133
- let isTruthy = element[caseKey] === true || element.state[caseKey];
1134
- if (!isTruthy) {
1135
- const caseResult = resolveCase(caseKey, element);
1136
- if (caseResult !== void 0) isTruthy = caseResult;
1137
- }
1138
- return prefix === "." ? !!isTruthy : !isTruthy;
1139
- };
1140
- const CONDITIONAL_PREFIXES = /* @__PURE__ */ new Set(["$", ".", "!"]);
1141
- const extractConditionalAttrs = (props, tag, cssProps) => {
1142
- const result = {};
1143
- const addConditionalAttr = (attrName, attrVal, prefix, caseKey) => {
1144
- const capturedVal = attrVal;
1145
- result[attrName] = (el) => {
1146
- if (!evaluateCondition(prefix, caseKey, el)) return void 0;
1147
- return (0, import_utils.isFunction)(capturedVal) ? capturedVal(el) : capturedVal;
1148
- };
1149
- };
1150
- for (const key in props) {
1151
- const prefix = key.charAt(0);
1152
- if (!CONDITIONAL_PREFIXES.has(prefix)) continue;
1153
- const block = props[key];
1154
- if (!block || typeof block !== "object") continue;
1155
- const caseKey = key.slice(1);
1156
- for (const attrKey in block) {
1157
- if (cssProps && attrKey in cssProps) continue;
1158
- if (attrKey === "aria" && block[attrKey] && typeof block[attrKey] === "object") {
1159
- for (const ariaKey in block[attrKey]) {
1160
- addConditionalAttr("aria-" + ariaKey, block[attrKey][ariaKey], prefix, caseKey);
1161
- }
1162
- continue;
1163
- }
1164
- if (attrKey === "data" && block[attrKey] && typeof block[attrKey] === "object") {
1165
- for (const dataKey in block[attrKey]) {
1166
- const kebab = dataKey.replace(/([A-Z])/g, (m) => "-" + m.toLowerCase());
1167
- addConditionalAttr("data-" + kebab, block[attrKey][dataKey], prefix, caseKey);
1168
- }
1169
- continue;
1170
- }
1171
- const isAttribute = checkAttributeByTagName(tag, attrKey);
1172
- const isEvent = checkEventFunctions(attrKey);
1173
- if (!isAttribute && !isEvent) continue;
1174
- const attrName = camelToAttr(attrKey) || attrKey;
1175
- addConditionalAttr(attrName, block[attrKey], prefix, caseKey);
1176
- }
1177
- }
1178
- return result;
1179
- };
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "commonjs",
3
+ "main": "index.js"
4
+ }
package/index.js CHANGED
@@ -1057,13 +1057,19 @@ export const checkAttributeByTagName = (tag, attribute) => {
1057
1057
  // camelCase aria/data attrs
1058
1058
  if (camelToAttr(attribute)) return true
1059
1059
 
1060
+ // Global attributes (`title`, `id`, `class`, `role`, `tabindex`, `hidden`,
1061
+ // `lang`, `dir`, `draggable`, `contenteditable`, `spellcheck`, `slot`, …)
1062
+ // apply to every tag. Prior behavior only consulted HTML_ATTRIBUTES.default
1063
+ // for tags WITHOUT a per-tag list, which silently dropped global attrs on
1064
+ // every typed tag (button/a/img/input/...). Always allow defaults first,
1065
+ // then tag-specific extras.
1066
+ if (HTML_ATTRIBUTES.default.includes(attribute)) return true
1067
+
1060
1068
  if (Object.prototype.hasOwnProperty.call(HTML_ATTRIBUTES, tag)) {
1061
1069
  const attributes = HTML_ATTRIBUTES[tag]
1062
- return attributes.includes(attribute) || attributes.includes('default')
1063
- } else {
1064
- const defaultAttributes = HTML_ATTRIBUTES.default
1065
- return defaultAttributes.includes(attribute)
1070
+ return attributes.includes(attribute)
1066
1071
  }
1072
+ return false
1067
1073
  }
1068
1074
 
1069
1075
  export const checkEventFunctions = (key) => {
@@ -1080,6 +1086,11 @@ export const filterAttributesByTagName = (tag, props, cssProps) => {
1080
1086
  if (cssProps && key in cssProps) continue
1081
1087
 
1082
1088
  // aria: { label: 'foo', expanded: true } → aria-label, aria-expanded
1089
+ // Function values are stored as-is here; `executeAttr` is the single
1090
+ // place that resolves reactive (el, state) → string for HTML attrs.
1091
+ // We funnel aria/data through `attr` instead of duplicating execution
1092
+ // logic so v3.14 reactive aria/data attributes work with the same
1093
+ // re-render cycle as `attr.foo` — see FA-L1.
1083
1094
  if (key === 'aria' && props[key] && typeof props[key] === 'object') {
1084
1095
  for (const ariaKey in props[key]) {
1085
1096
  if (isDefined(props[key][ariaKey])) {
@@ -1119,6 +1130,29 @@ export const executeAttr = (elem, element) => {
1119
1130
  attrObj[attrProp] = elem.attr[attrProp](element, element.state, element.context)
1120
1131
  }
1121
1132
  }
1133
+ // FA-L1: aria.X and data.X with FUNCTION values must execute the same way
1134
+ // as attr.X. Without this, `aria: { label: (el, s) => s.text }` writes a
1135
+ // function reference to the DOM attribute and renders as `null`.
1136
+ // Static (non-function) aria/data values are already promoted to flat
1137
+ // attributes by `filterAttributesByTagName`; we only handle functions
1138
+ // here to avoid double-write.
1139
+ if (elem.aria && typeof elem.aria === 'object') {
1140
+ for (const ariaKey in elem.aria) {
1141
+ const v = elem.aria[ariaKey]
1142
+ if (typeof v === 'function') {
1143
+ attrObj['aria-' + ariaKey] = v(element, element.state, element.context)
1144
+ }
1145
+ }
1146
+ }
1147
+ if (elem.data && typeof elem.data === 'object') {
1148
+ for (const dataKey in elem.data) {
1149
+ const v = elem.data[dataKey]
1150
+ if (typeof v === 'function') {
1151
+ const kebab = dataKey.replace(/([A-Z])/g, (m) => '-' + m.toLowerCase())
1152
+ attrObj['data-' + kebab] = v(element, element.state, element.context)
1153
+ }
1154
+ }
1155
+ }
1122
1156
  return attrObj
1123
1157
  }
1124
1158
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "attrs-in-props",
3
3
  "description": "Utilize props as attributes",
4
4
  "author": "symbo.ls",
5
- "version": "3.14.0",
5
+ "version": "3.14.1",
6
6
  "repository": "https://github.com/symbo-ls/smbls",
7
7
  "type": "module",
8
8
  "module": "./index.js",