Chandra
view release on metacpan or search on metacpan
xs/contextmenu.xs view on Meta::CPAN
SV *
js_code(self)
SV *self
CODE:
{
HV *hv = (HV *)SvRV(self);
SV **att_svp = hv_fetchs(hv, "_attachments", 0);
SV **items_svp = hv_fetchs(hv, "_items", 0);
SV **global_svp = hv_fetchs(hv, "_global", 0);
HV *att_hv = (HV *)SvRV(*att_svp);
AV *items_av = (AV *)SvRV(*items_svp);
int is_global = (global_svp && SvTRUE(*global_svp));
SV *js;
js = newSVpvs(
"(function(){\n"
"if(window.__chandraCtxMenu)return;\n"
"window.__chandraCtxMenu=1;\n"
"var sels=["
);
/* Emit selectors */
{
HE *entry;
int first = 1;
hv_iterinit(att_hv);
while ((entry = hv_iternext(att_hv)) != NULL) {
I32 klen;
const char *key = hv_iterkey(entry, &klen);
if (!first) sv_catpvs(js, ",");
sv_catpvs(js, "'");
sv_catpvn(js, key, klen);
sv_catpvs(js, "'");
first = 0;
}
}
sv_catpvs(js, "];\n");
/* Items JSON */
sv_catpvs(js, "var items=");
_cm_items_to_js(aTHX_ js, items_av);
sv_catpvs(js, ";\n");
/* Global flag */
sv_catpvf(js, "var isGlobal=%d;\n", is_global);
/* CSS for menu */
sv_catpvs(js,
"var style=document.createElement('style');\n"
"style.textContent='"
".chandra-ctx-menu{"
"position:fixed;z-index:999999;min-width:160px;"
"background:#fff;border:1px solid #ccc;border-radius:6px;"
"box-shadow:0 4px 16px rgba(0,0,0,.18);padding:4px 0;"
"font:13px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;"
"color:#222;user-select:none;opacity:0;transform:scale(.96);"
"transition:opacity .12s,transform .12s;"
"}"
".chandra-ctx-menu.show{opacity:1;transform:scale(1);}"
".chandra-ctx-item{"
"padding:6px 32px 6px 28px;cursor:pointer;white-space:nowrap;"
"display:flex;align-items:center;position:relative;"
"}"
".chandra-ctx-item:hover{background:#e8f0fe;}"
".chandra-ctx-item.disabled{color:#999;pointer-events:none;}"
".chandra-ctx-sep{height:1px;background:#e0e0e0;margin:4px 8px;}"
".chandra-ctx-icon{width:20px;margin-right:6px;text-align:center;}"
".chandra-ctx-sc{margin-left:auto;padding-left:24px;color:#888;font-size:12px;}"
".chandra-ctx-check{position:absolute;left:8px;}"
".chandra-ctx-sub-arrow{margin-left:auto;padding-left:16px;color:#888;}"
".chandra-ctx-sub{position:fixed;}"
"@media(prefers-color-scheme:dark){"
".chandra-ctx-menu{background:#2d2d2d;border-color:#555;color:#e0e0e0;}"
".chandra-ctx-item:hover{background:#404040;}"
".chandra-ctx-sep{background:#555;}"
".chandra-ctx-sc,.chandra-ctx-sub-arrow{color:#888;}"
".chandra-ctx-item.disabled{color:#666;}"
"}"
"';\n"
"document.head.appendChild(style);\n"
);
/* Menu rendering and event handling JS */
sv_catpvs(js,
"var openMenus=[];\n"
"function closeAll(){"
"openMenus.forEach(function(m){m.remove()});"
"openMenus=[];"
"}\n"
"function buildMenu(items,x,y,depth){"
"var m=document.createElement('div');"
"m.className='chandra-ctx-menu';"
"items.forEach(function(it){"
"if(it.sep){"
"var s=document.createElement('div');"
"s.className='chandra-ctx-sep';"
"m.appendChild(s);return;"
"}"
"var row=document.createElement('div');"
"row.className='chandra-ctx-item'+(it.dis?' disabled':'');"
/* Check mark */
"if(it.chk){"
"var ck=document.createElement('span');"
"ck.className='chandra-ctx-check';"
"ck.textContent=it.ckd?'\\u2713':'';"
"row.appendChild(ck);"
"}"
/* Icon */
"if(it.ico){"
"var ic=document.createElement('span');"
"ic.className='chandra-ctx-icon';"
"ic.textContent=it.ico;"
"row.appendChild(ic);"
"}"
/* Label */
( run in 0.586 second using v1.01-cache-2.11-cpan-140bd7fdf52 )